Compare commits

...

6 Commits

Author SHA1 Message Date
DevTekVE e7a3ea1b32 (TICI) sync master and master-tici (#1245)
* SL: bugfix parameter handling in sunnylink restore and remote setting (#1234)

* refactor: improve parameter handling in sunnylink for robustness

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

* refactor: centralize parameter restoration with new helper function

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

* don't bother

* clean

(cherry picked from commit b7f8dd11a5)

* UI: Developer UI (#1233)

(cherry picked from commit 1bb4ca2547)

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

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

This reverts commit 1bb4ca2547.

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

This reverts commit b0a77049da.

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

(cherry picked from commit 810a2d9448)

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

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

This reverts commit 810a2d9448.

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

This reverts commit 1bb4ca2547.

(cherry picked from commit 1be13fdc55)

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

This reverts commit 1be13fdc55.

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

---------

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

* disable reset and squash for master-tici

* disable release drafter

* docker img

* temp builds

* actual temp

* new ref

* Revert "actual temp"

This reverts commit 2e73befd17.

* wrong name

* Revert "wrong name"

This reverts commit 5efa687aaf.

* Reapply "actual temp"

This reverts commit 75a9d986fd.

* Revert "Reapply "actual temp""

This reverts commit 1f77d902d4.

* Reapply "wrong name"

This reverts commit ed24def13c.

* revert
2025-09-06 17:28:43 -04:00
43 changed files with 697 additions and 111 deletions
+2 -2
View File
@@ -29,11 +29,11 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
target: /^(?!master$).*/
target: /^(?!master-tici$).*/
exclude: /sunnypilot:.*/
change-to: ${{ github.base_ref }}
already-exists-action: close_this
already-exists-comment: "Your PR should be made against the `master` branch"
already-exists-comment: "Your PR should be made against the `master-tici` branch"
update-pr-labels:
name: Update fork's PR Labels
+1 -1
View File
@@ -5,7 +5,7 @@ on:
workflow_dispatch:
env:
BASE_IMAGE: sunnypilot-base
BASE_IMAGE: sunnypilot-tici-base
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
+2 -2
View File
@@ -3,7 +3,7 @@ name: cereal validation
on:
push:
branches:
- master
- master-tici
pull_request:
paths:
- 'cereal/**'
@@ -16,7 +16,7 @@ on:
type: string
concurrency:
group: cereal-validation-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
group: cereal-validation-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true
env:
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master-tici
with:
run_number: ${{ matrix.run_number }}
+1 -1
View File
@@ -12,6 +12,6 @@ concurrency:
jobs:
selfdrive_tests:
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master-tici
with:
run_number: ${{ inputs.run_number }}
@@ -15,7 +15,7 @@ runs:
scons -j$(nproc) --cache-populate"
- name: Save scons cache
uses: actions/cache/save@v4
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master-tici'
with:
path: .ci_cache/scons_cache
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
+4 -4
View File
@@ -3,7 +3,7 @@ name: docs
on:
push:
branches:
- master
- master-tici
pull_request:
workflow_call:
inputs:
@@ -12,7 +12,7 @@ on:
required: true
type: string
concurrency:
group: docs-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
group: docs-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
@@ -35,13 +35,13 @@ jobs:
# Push to docs.comma.ai
- uses: actions/checkout@v4
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
if: github.ref == 'refs/heads/master-tici' && github.repository == 'sunnypilot/sunnypilot'
with:
path: openpilot-docs
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
repository: sunnypilot/sunnypilot-docs
- name: Push
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
if: github.ref == 'refs/heads/master-tici' && github.repository == 'sunnypilot/sunnypilot'
run: |
set -x
+2 -2
View File
@@ -24,11 +24,11 @@ jobs:
if: ${{ github.event_name != 'workflow_dispatch' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with:
ref: master
ref: master-tici
wait-interval: 30
running-workflow-name: 'build prebuilt'
repo-token: ${{ secrets.GITHUB_TOKEN }}
check-regexp: ^((?!.*(build master-ci).*).)*$
check-regexp: ^((?!.*(build __nightly).*).)*$
- uses: actions/checkout@v4
with:
submodules: true
+1
View File
@@ -19,6 +19,7 @@ jobs:
contents: write
pull-requests: write
runs-on: ubuntu-latest
if: False
steps:
- uses: release-drafter/release-drafter@v6
with:
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
env:
ImageOS: ubuntu24
container:
image: ghcr.io/sunnypilot/sunnypilot-base:latest
image: ghcr.io/sunnypilot/sunnypilot-tici-base:latest
runs-on: ubuntu-latest
if: github.repository == 'sunnypilot/sunnypilot'
permissions:
@@ -25,7 +25,7 @@ jobs:
if: ${{ github.event_name == 'schedule' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with:
ref: master
ref: master-tici
wait-interval: 30
running-workflow-name: 'build __nightly'
repo-token: ${{ secrets.GITHUB_TOKEN }}
+4 -4
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
env:
BASE_IMAGE: sunnypilot-base
BASE_IMAGE: sunnypilot-tici-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
@@ -28,7 +28,7 @@ jobs:
title: "[bot] Update translations"
body: "Automatic PR from repo-maintenance -> update_translations"
branch: "update-translations"
base: "master"
base: "master-tici"
delete-branch: true
labels: bot
@@ -36,7 +36,7 @@ jobs:
name: package_updates
runs-on: ubuntu-latest
container:
image: ghcr.io/sunnypilot/sunnypilot-base:latest
image: ghcr.io/sunnypilot/sunnypilot-tici-base:latest
if: github.repository == 'sunnypilot/sunnypilot'
steps:
- uses: actions/checkout@v4
@@ -68,7 +68,7 @@ jobs:
commit-message: Update Python packages
title: '[bot] Update Python packages'
branch: auto-package-updates
base: master
base: master-tici
delete-branch: true
body: 'Automatic PR from repo-maintenance -> package_updates'
labels: bot
+10 -10
View File
@@ -3,7 +3,7 @@ name: selfdrive
on:
push:
branches:
- master
- master-tici
pull_request:
workflow_dispatch:
workflow_call:
@@ -14,12 +14,12 @@ on:
type: string
concurrency:
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true
env:
PYTHONWARNINGS: error
BASE_IMAGE: sunnypilot-base
BASE_IMAGE: sunnypilot-tici-base
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
@@ -68,10 +68,10 @@ jobs:
if: github.repository == 'sunnypilot/sunnypilot'
timeout-minutes: 3
run: |
if [ "${{ github.ref }}" != "refs/heads/master" ]; then
git fetch origin master:refs/remotes/origin/master
if [ "${{ github.ref }}" != "refs/heads/master-tici" ]; then
git fetch origin master-tici:refs/remotes/origin/master-tici
SUBMODULE_PATHS=$(git diff origin/master HEAD --name-only | grep -E '^[^/]+$' | while read path; do
SUBMODULE_PATHS=$(git diff origin/master-tici HEAD --name-only | grep -E '^[^/]+$' | while read path; do
if git ls-files --stage "$path" | grep -q "^160000"; then
echo "$path"
fi
@@ -97,7 +97,7 @@ jobs:
with:
submodules: true
- name: Setup docker push
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot'
if: github.ref == 'refs/heads/master-tici' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot'
run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
$DOCKER_LOGIN
@@ -129,7 +129,7 @@ jobs:
PYTHONWARNINGS: default
- name: Save Homebrew cache
uses: actions/cache/save@v4
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master-tici'
with:
path: ~/Library/Caches/Homebrew
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
@@ -146,7 +146,7 @@ jobs:
run: . .venv/bin/activate && scons -j$(nproc)
- name: Save scons cache
uses: actions/cache/save@v4
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master-tici'
with:
path: /tmp/scons_cache
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
@@ -305,5 +305,5 @@ jobs:
- name: Upload Test Report
uses: actions/upload-artifact@v4
with:
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && 'master-tici' || github.event.number }}
path: selfdrive/ui/tests/test_ui/report_1/screenshots
@@ -14,7 +14,7 @@ on:
upstream_branch:
description: 'Upstream branch to build from'
required: true
default: 'master'
default: 'master-tici'
type: string
custom_name:
description: 'Custom name for the model (no date, only name)'
@@ -35,7 +35,7 @@ on:
upstream_branch:
description: 'Upstream branch to build from'
required: true
default: 'master'
default: 'master-tici'
type: string
custom_name:
description: 'Custom name for the model (no date, only name)'
@@ -8,14 +8,14 @@ env:
PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot"
# Branch configurations
STAGING_C3_SOURCE_BRANCH: ${{ vars.STAGING_C3_SOURCE_BRANCH || 'master' }} # vars are set on repo settings.
STAGING_C3_SOURCE_BRANCH: 'master-tici' # vars.STAGING_C3_SOURCE_BRANCH could be used, set on repo settings.
# Runtime configuration
SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}"
on:
push:
branches: [ master, master-dev-c3-new ]
branches: [ master-tici ]
tags: [ 'release/*' ]
pull_request_target:
types: [ labeled ]
@@ -1,7 +1,7 @@
name: Build dev-c3-new
env:
DEFAULT_SOURCE_BRANCH: "master"
DEFAULT_SOURCE_BRANCH: "master-tici"
DEFAULT_TARGET_BRANCH: "master-dev-c3-new"
PR_LABEL: "dev-c3"
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
@@ -10,17 +10,17 @@ env:
on:
push:
branches:
- master
- master-tici
pull_request_target:
types: [ labeled ]
branches:
- 'master'
- 'master-tici'
workflow_dispatch:
inputs:
source_branch:
description: 'Source branch to reset from'
required: true
default: 'master'
default: 'master-tici'
type: string
target_branch:
description: 'Target branch to reset and squash into'
@@ -40,11 +40,7 @@ concurrency:
jobs:
reset-and-squash:
runs-on: ubuntu-latest
if: (
(github.event_name == 'workflow_dispatch')
|| (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3'))))
)
if: False
steps:
- uses: actions/checkout@v4
with:
+10 -10
View File
@@ -2,19 +2,19 @@ name: "ui preview"
on:
push:
branches:
- master
- master-tici
pull_request_target:
types: [assigned, opened, synchronize, reopened, edited]
branches:
- 'master'
- 'master-tici'
paths:
- 'selfdrive/ui/**'
workflow_dispatch:
env:
UI_JOB_NAME: "Create UI Report"
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && 'master-tici' || github.event.number }}
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master-tici' && github.sha || github.event.pull_request.head.sha }}
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
jobs:
@@ -55,7 +55,7 @@ jobs:
name: report-1-${{ env.REPORT_NAME }}
path: ${{ github.workspace }}/pr_ui
- name: Getting master ui
- name: Getting master-tici ui
uses: actions/checkout@v4
with:
repository: sunnypilot/ci-artifacts
@@ -63,8 +63,8 @@ jobs:
path: ${{ github.workspace }}/master_ui
ref: openpilot_master_ui
- name: Saving new master ui
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
- name: Saving new master-tici ui
if: github.ref == 'refs/heads/master-tici' && github.event_name == 'push'
working-directory: ${{ github.workspace }}/master_ui
run: |
git checkout --orphan=new_master_ui
@@ -93,9 +93,9 @@ jobs:
for ((i=0; i<${#A[*]}; i=i+1));
do
# Check if the master file exists
# Check if the master-tici file exists
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
# This is a new file in PR UI that doesn't exist in master
# This is a new file in PR UI that doesn't exist in master-tici
DIFF="${DIFF}<details open>"
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
DIFF="${DIFF}<table>"
@@ -118,7 +118,7 @@ jobs:
DIFF="${DIFF}<table>"
DIFF="${DIFF}<tr>"
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
DIFF="${DIFF} <td> master-tici <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
DIFF="${DIFF}</tr>"
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ghcr.io/sunnypilot/sunnypilot-base:latest
FROM ghcr.io/sunnypilot/sunnypilot-tici-base:latest
ENV PYTHONUNBUFFERED=1
+9 -10
View File
@@ -22,7 +22,7 @@ https://docs.sunnypilot.ai/ is your one stop shop for everything from features t
Detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
## Installation
Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `staging-c3-new` branch.
Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `staging-tici` branch.
### If you want to use our newest branches (our rewrite)
> [!TIP]
@@ -31,25 +31,24 @@ Please refer to [Recommended Branches](#-recommended-branches) to find your pref
* sunnypilot not installed or you installed a version before 0.8.17?
1. [Factory reset/uninstall](https://github.com/commaai/openpilot/wiki/FAQ#how-can-i-reset-the-device) the previous software if you have another software/fork installed.
2. After factory reset/uninstall and upon reboot, select `Custom Software` when given the option.
3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```https://staging-c3-new.sunnypilot.ai```.
3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```https://staging-tici.sunnypilot.ai```.
4. Complete the rest of the installation following the onscreen instructions.
* sunnypilot already installed and you installed a version after 0.8.17?
1. On the comma three, go to `Settings` ▶️ `Software`.
2. At the `Download` option, press `CHECK`. This will fetch the list of latest branches from sunnypilot.
3. At the `Target Branch` option, press `SELECT` to open the Target Branch selector.
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `staging-c3-new`
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `staging-tici`
| Branch | Installation URL |
|:----------------:|:---------------------------------------------:|
| `staging-c3-new` | `https://staging-c3-new.sunnypilot.ai` |
| `dev-c3-new` | `https://dev-c3-new.sunnypilot.ai` |
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
| `release-c3-new` | **Not yet available**. |
| Branch | Installation URL |
|:---------------:|:---------------------------------------------:|
| `staging-tici` | `https://staging-tici.sunnypilot.ai` |
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
| `release-tici` | **Not yet available**. |
> [!TIP]
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-c3-new'.
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-tici'.
> [!NOTE]
> 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
View File
@@ -146,6 +146,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CustomAccLongPressIncrement", {PERSISTENT | BACKUP, INT, "5"}},
{"CustomAccShortPressIncrement", {PERSISTENT | BACKUP, INT, "1"}},
{"DeviceBootMode", {PERSISTENT | BACKUP, INT, "0"}},
{"DevUIInfo", {PERSISTENT | BACKUP, INT, "0"}},
{"EnableCopyparty", {PERSISTENT | BACKUP, BOOL}},
{"EnableGithubRunner", {PERSISTENT | BACKUP, BOOL}},
{"GithubRunnerSufficientVoltage", {CLEAR_ON_MANAGER_START , BOOL}},
+2 -2
View File
@@ -1,8 +1,8 @@
if [ "$1" = "base" ]; then
export DOCKER_IMAGE=sunnypilot-base
export DOCKER_IMAGE=sunnypilot-tici-base
export DOCKER_FILE=Dockerfile.sunnypilot_base
elif [ "$1" = "prebuilt" ]; then
export DOCKER_IMAGE=sunnypilot-prebuilt
export DOCKER_IMAGE=sunnypilot-tici-prebuilt
export DOCKER_FILE=Dockerfile.sunnypilot
else
echo "Invalid docker build image: '$1'"
+9
View File
@@ -4,6 +4,9 @@
#include <map>
#include "selfdrive/ui/qt/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#endif
void OnroadAlerts::updateState(const UIState &s) {
Alert a = getAlert(*(s.sm), s.scene.started_frame);
@@ -73,6 +76,12 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
}
QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2);
#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);
// draw background + gradient
@@ -12,6 +12,7 @@
#include "selfdrive/ui/sunnypilot/qt/onroad/model.h"
#define ExperimentalButton ExperimentalButtonSP
#define ModelRenderer ModelRendererSP
#define HudRenderer HudRendererSP
#else
#include "selfdrive/ui/qt/onroad/buttons.h"
#include "selfdrive/ui/qt/onroad/hud.h"
@@ -73,6 +73,11 @@ void DriverMonitorRenderer::draw(QPainter &painter, const QRect &surface_rect) {
float y = surface_rect.height() - offset;
float 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);
QPointF keypoints[std::size(DEFAULT_FACE_KPTS_3D)];
+1
View File
@@ -39,6 +39,7 @@ qt_src = [
"sunnypilot/qt/offroad/settings/visuals_panel.cc",
"sunnypilot/qt/onroad/annotated_camera.cc",
"sunnypilot/qt/onroad/buttons.cc",
"sunnypilot/qt/onroad/developer_ui/developer_ui.cc",
"sunnypilot/qt/onroad/hud.cc",
"sunnypilot/qt/onroad/model.cc",
"sunnypilot/qt/onroad/onroad_home.cc",
@@ -72,6 +72,15 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
list->addItem(chevron_info_settings);
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);
vlayout->addWidget(sunnypilotScroller);
@@ -90,4 +99,7 @@ void VisualsPanel::paramsRefresh() {
if (chevron_info_settings) {
chevron_info_settings->refresh();
}
if (dev_ui_settings) {
dev_ui_settings->refresh();
}
}
@@ -28,4 +28,5 @@ protected:
std::map<std::string, ParamControlSP*> toggles;
ParamWatcher * param_watcher;
ButtonParamControlSP *chevron_info_settings;
ButtonParamControlSP *dev_ui_settings;
};
@@ -14,3 +14,8 @@ AnnotatedCameraWidgetSP::AnnotatedCameraWidgetSP(VisionStreamType type, QWidget
void AnnotatedCameraWidgetSP::updateState(const UIState &s) {
AnnotatedCameraWidget::updateState(s);
}
void AnnotatedCameraWidgetSP::showEvent(QShowEvent *event) {
AnnotatedCameraWidget::showEvent(event);
ui_update_params_sp(uiState());
}
@@ -15,4 +15,7 @@ class AnnotatedCameraWidgetSP : public AnnotatedCameraWidget {
public:
explicit AnnotatedCameraWidgetSP(VisionStreamType type, QWidget *parent = nullptr);
void updateState(const UIState &s) override;
protected:
void showEvent(QShowEvent *event) override;
};
@@ -0,0 +1,227 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include <cmath>
#include "common/util.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h"
// Add Relative Distance to Primary Lead Car
// Unit: Meters
UiElement DeveloperUi::getDRel(bool lead_status, float lead_d_rel) {
QString value = lead_status ? QString::number(lead_d_rel, 'f', 0) : "-";
QColor color = QColor(255, 255, 255, 255);
if (lead_status) {
// Orange if close, Red if very close
if (lead_d_rel < 5) {
color = QColor(255, 0, 0, 255);
} else if (lead_d_rel < 15) {
color = QColor(255, 188, 0, 255);
}
}
return UiElement(value, "REL DIST", "m", color);
}
// Add Relative Velocity vs Primary Lead Car
// Unit: kph if metric, else mph
UiElement DeveloperUi::getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit) {
QString value = lead_status ? QString::number(lead_v_rel * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-";
QColor color = QColor(255, 255, 255, 255);
if (lead_status) {
// Red if approaching faster than 10mph
// Orange if approaching (negative)
if (lead_v_rel < -4.4704) {
color = QColor(255, 0, 0, 255);
} else if (lead_v_rel < 0) {
color = QColor(255, 188, 0, 255);
}
}
return UiElement(value, "REL SPEED", speed_unit, color);
}
// Add Real Steering Angle
// Unit: Degrees
UiElement DeveloperUi::getSteeringAngleDeg(float angle_steers, bool lat_active, bool steer_override) {
QString value = QString("%1%2%3").arg(QString::number(angle_steers, 'f', 1)).arg("°").arg("");
QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255);
// Red if large steering angle
// Orange if moderate steering angle
if (std::fabs(angle_steers) > 180) {
color = QColor(255, 0, 0, 255);
} else if (std::fabs(angle_steers) > 90) {
color = QColor(255, 188, 0, 255);
}
return UiElement(value, "REAL STEER", "", color);
}
// Add Actual Lateral Acceleration (roll compensated) when using Torque
// Unit: m/s²
UiElement DeveloperUi::getActualLateralAccel(float curvature, float v_ego, float roll, bool lat_active, bool steer_override) {
double actualLateralAccel = (curvature * pow(v_ego, 2)) - (roll * 9.81);
QString value = QString::number(actualLateralAccel, 'f', 2);
QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255);
return UiElement(value, "ACTUAL L.A.", "m/s²", color);
}
// Add Desired Steering Angle when using PID
// Unit: Degrees
UiElement DeveloperUi::getSteeringAngleDesiredDeg(bool lat_active, float steer_angle_desired, float angle_steers) {
QString value = lat_active ? QString("%1%2%3").arg(QString::number(steer_angle_desired, 'f', 1)).arg("°").arg("") : "-";
QColor color = QColor(255, 255, 255, 255);
if (lat_active) {
// Red if large steering angle
// Orange if moderate steering angle
if (std::fabs(angle_steers) > 180) {
color = QColor(255, 0, 0, 255);
} else if (std::fabs(angle_steers) > 90) {
color = QColor(255, 188, 0, 255);
} else {
color = QColor(0, 255, 0, 255);
}
}
return UiElement(value, "DESIRED STEER", "", color);
}
// Add Device Memory (RAM) Usage
// Unit: Percent
UiElement DeveloperUi::getMemoryUsagePercent(int memory_usage_percent) {
QString value = QString("%1%2").arg(QString::number(memory_usage_percent, 'd', 0)).arg("%");
QColor color = (memory_usage_percent > 85) ? QColor(255, 188, 0, 255) : QColor(255, 255, 255, 255);
return UiElement(value, "RAM", "", color);
}
// Add Vehicle Current Acceleration
// Unit: m/s²
UiElement DeveloperUi::getAEgo(float a_ego) {
QString value = QString::number(a_ego, 'f', 1);
QColor color = QColor(255, 255, 255, 255);
return UiElement(value, "ACC.", "m/s²", color);
}
// Add Relative Velocity to Primary Lead Car
// Unit: kph if metric, else mph
UiElement DeveloperUi::getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit) {
QString value = lead_status ? QString::number((lead_v_rel + v_ego) * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-";
QColor color = QColor(255, 255, 255, 255);
if (lead_status) {
// Red if approaching faster than 10mph
// Orange if approaching (negative)
if (lead_v_rel < -4.4704) {
color = QColor(255, 0, 0, 255);
} else if (lead_v_rel < 0) {
color = QColor(255, 188, 0, 255);
}
}
return UiElement(value, "L.S.", speed_unit, color);
}
// Add Friction Coefficient Raw from torqued
// Unit: None
UiElement DeveloperUi::getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid) {
QString value = QString::number(friction_coefficient_filtered, 'f', 3);
QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255);
return UiElement(value, "FRIC.", "", color);
}
// Add Lateral Acceleration Factor Raw from torqued
// Unit: m/s²
UiElement DeveloperUi::getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid) {
QString value = QString::number(lat_accel_factor_filtered, 'f', 3);
QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255);
return UiElement(value, "L.A.", "m/s²", color);
}
// Add Steering Torque from Car EPS
// Unit: Newton Meters
UiElement DeveloperUi::getSteeringTorqueEps(float steering_torque_eps) {
QString value = QString::number(std::fabs(steering_torque_eps), 'f', 1);
QColor color = QColor(255, 255, 255, 255);
return UiElement(value, "E.T.", "N·dm", color);
}
// Add Bearing Degree and Direction from Car (Compass)
// Unit: Meters
UiElement DeveloperUi::getBearingDeg(float bearing_accuracy_deg, float bearing_deg) {
QString value = (bearing_accuracy_deg != 180.00) ? QString("%1%2%3").arg(QString::number(bearing_deg, 'd', 0)).arg("°").arg("") : "-";
QColor color = QColor(255, 255, 255, 255);
QString dir_value;
if (bearing_accuracy_deg != 180.00) {
if (((bearing_deg >= 337.5) && (bearing_deg <= 360)) || ((bearing_deg >= 0) && (bearing_deg <= 22.5))) {
dir_value = "N";
} else if ((bearing_deg > 22.5) && (bearing_deg < 67.5)) {
dir_value = "NE";
} else if ((bearing_deg >= 67.5) && (bearing_deg <= 112.5)) {
dir_value = "E";
} else if ((bearing_deg > 112.5) && (bearing_deg < 157.5)) {
dir_value = "SE";
} else if ((bearing_deg >= 157.5) && (bearing_deg <= 202.5)) {
dir_value = "S";
} else if ((bearing_deg > 202.5) && (bearing_deg < 247.5)) {
dir_value = "SW";
} else if ((bearing_deg >= 247.5) && (bearing_deg <= 292.5)) {
dir_value = "W";
} else if ((bearing_deg > 292.5) && (bearing_deg < 337.5)) {
dir_value = "NW";
}
} else {
dir_value = "OFF";
}
return UiElement(QString("%1 | %2").arg(dir_value).arg(value), "B.D.", "", color);
}
// Add Altitude of Current Location
// Unit: Meters
UiElement DeveloperUi::getAltitude(float gps_accuracy, float altitude) {
QString value = (gps_accuracy != 0.00) ? QString::number(altitude, 'f', 1) : "-";
QColor color = QColor(255, 255, 255, 255);
return UiElement(value, "ALT.", "m", color);
}
// Add Actuators Output
// Unit: Degree (angle) or m/s² (torque)
UiElement DeveloperUi::getActuatorsOutputLateral(cereal::CarParams::SteerControlType steerControlType,
cereal::CarControl::Actuators::Reader &actuators,
float desiredCurvature, float v_ego, float roll, bool lat_active, bool steer_override) {
QString label;
QString value;
QString unit;
if (steerControlType == cereal::CarParams::SteerControlType::ANGLE) {
label = "DESIRED STEER";
value = QString("%1%2%3").arg(QString::number(actuators.getSteeringAngleDeg(), 'f', 1)).arg("°").arg("");
} else {
label = "DESIRED L.A.";
double desiredLateralAccel = (desiredCurvature * pow(v_ego, 2)) - (roll * 9.81);
value = QString::number(desiredLateralAccel, 'f', 2);
unit = "m/s²";
}
value = lat_active ? value : "-";
QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255);
return UiElement(value, label, unit, color);
}
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h"
class DeveloperUi {
public:
static UiElement getDRel(bool lead_status, float lead_d_rel);
static UiElement getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit);
static UiElement getSteeringAngleDeg(float angle_steers, bool lat_active, bool steer_override);
static UiElement getActualLateralAccel(float curvature, float v_ego, float roll, bool lat_active, bool steer_override);
static UiElement getSteeringAngleDesiredDeg(bool lat_active, float steer_angle_desired, float angle_steers);
static UiElement getMemoryUsagePercent(int memory_usage_percent);
static UiElement getAEgo(float a_ego);
static UiElement getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit);
static UiElement getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid);
static UiElement getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid);
static UiElement getSteeringTorqueEps(float steering_torque_eps);
static UiElement getBearingDeg(float bearing_accuracy_deg, float bearing_deg);
static UiElement getAltitude(float gps_accuracy, float altitude);
static UiElement getActuatorsOutputLateral(cereal::CarParams::SteerControlType steerControlType,
cereal::CarControl::Actuators::Reader &actuators,
float desiredCurvature, float v_ego, float roll, bool lat_active, bool steer_override);
};
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include <QColor>
struct UiElement {
QString value{};
QString label{};
QString units{};
QColor color{};
explicit UiElement(const QString &value = "", const QString &label = "", const QString &units = "", const QColor &color = QColor(255, 255, 255, 255))
: value(value), label(label), units(units), color(color) {}
};
+190
View File
@@ -7,12 +7,202 @@
#include "selfdrive/ui/sunnypilot/qt/onroad/hud.h"
#include "selfdrive/ui/qt/util.h"
HudRendererSP::HudRendererSP() {}
void HudRendererSP::updateState(const UIState &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) {
HudRenderer::draw(p, surface_rect);
if (!reversing) {
// Bottom Dev UI
if (devUiInfo == 2) {
QRect rect_bottom(surface_rect.left(), surface_rect.bottom() - 60, surface_rect.width(), 61);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 100));
p.drawRect(rect_bottom);
drawBottomDevUI(p, rect_bottom.left(), rect_bottom.center().y());
}
// Right Dev UI
if (devUiInfo != 0) {
QRect rect_right(surface_rect.right() - (UI_BORDER_SIZE * 2), UI_BORDER_SIZE * 1.5, 184, 170);
drawRightDevUI(p, surface_rect.right() - 184 - UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2 + rect_right.height());
}
}
}
void HudRendererSP::drawText(QPainter &p, int x, int y, const QString &text, QColor color) {
QRect real_rect = p.fontMetrics().boundingRect(text);
real_rect.moveCenter({x, y - real_rect.height() / 2});
p.setPen(color);
p.drawText(real_rect.x(), real_rect.bottom(), text);
}
int HudRendererSP::drawRightDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) {
p.setFont(InterFont(28, QFont::Bold));
x += 92;
y += 80;
drawText(p, x, y, label);
p.setFont(InterFont(30 * 2, QFont::Bold));
y += 65;
drawText(p, x, y, value, color);
p.setFont(InterFont(28, QFont::Bold));
if (units.length() > 0) {
p.save();
x += 120;
y -= 25;
p.translate(x, y);
p.rotate(-90);
drawText(p, 0, 0, units);
p.restore();
}
return 130;
}
void HudRendererSP::drawRightDevUI(QPainter &p, int x, int y) {
int rh = 5;
int ry = y;
UiElement dRelElement = DeveloperUi::getDRel(lead_status, lead_d_rel);
rh += drawRightDevUIElement(p, x, ry, dRelElement.value, dRelElement.label, dRelElement.units, dRelElement.color);
ry = y + rh;
UiElement vRelElement = DeveloperUi::getVRel(lead_status, lead_v_rel, is_metric, speedUnit);
rh += drawRightDevUIElement(p, x, ry, vRelElement.value, vRelElement.label, vRelElement.units, vRelElement.color);
ry = y + rh;
UiElement steeringAngleDegElement = DeveloperUi::getSteeringAngleDeg(angleSteers, latActive, steerOverride);
rh += drawRightDevUIElement(p, x, ry, steeringAngleDegElement.value, steeringAngleDegElement.label, steeringAngleDegElement.units, steeringAngleDegElement.color);
ry = y + rh;
UiElement actuatorsOutputLateralElement = DeveloperUi::getActuatorsOutputLateral(steerControlType, actuators, desiredCurvature, vEgo, roll, latActive, steerOverride);
rh += drawRightDevUIElement(p, x, ry, actuatorsOutputLateralElement.value, actuatorsOutputLateralElement.label, actuatorsOutputLateralElement.units, actuatorsOutputLateralElement.color);
ry = y + rh;
UiElement actualLateralAccelElement = DeveloperUi::getActualLateralAccel(curvature, vEgo, roll, latActive, steerOverride);
rh += drawRightDevUIElement(p, x, ry, actualLateralAccelElement.value, actualLateralAccelElement.label, actualLateralAccelElement.units, actualLateralAccelElement.color);
}
int HudRendererSP::drawBottomDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) {
p.setFont(InterFont(38, QFont::Bold));
QFontMetrics fm(p.font());
QRect init_rect = fm.boundingRect(label + " ");
QRect real_rect = fm.boundingRect(init_rect, 0, label + " ");
real_rect.moveCenter({x, y});
QRect init_rect2 = fm.boundingRect(value);
QRect real_rect2 = fm.boundingRect(init_rect2, 0, value);
real_rect2.moveTop(real_rect.top());
real_rect2.moveLeft(real_rect.right() + 10);
QRect init_rect3 = fm.boundingRect(units);
QRect real_rect3 = fm.boundingRect(init_rect3, 0, units);
real_rect3.moveTop(real_rect.top());
real_rect3.moveLeft(real_rect2.right() + 10);
p.setPen(Qt::white);
p.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, label);
p.setPen(color);
p.drawText(real_rect2, Qt::AlignRight | Qt::AlignVCenter, value);
p.drawText(real_rect3, Qt::AlignLeft | Qt::AlignVCenter, units);
return 430;
}
void HudRendererSP::drawBottomDevUI(QPainter &p, int x, int y) {
int rw = 90;
UiElement aEgoElement = DeveloperUi::getAEgo(aEgo);
rw += drawBottomDevUIElement(p, rw, y, aEgoElement.value, aEgoElement.label, aEgoElement.units, aEgoElement.color);
UiElement vEgoLeadElement = DeveloperUi::getVEgoLead(lead_status, lead_v_rel, vEgo, is_metric, speedUnit);
rw += drawBottomDevUIElement(p, rw, y, vEgoLeadElement.value, vEgoLeadElement.label, vEgoLeadElement.units, vEgoLeadElement.color);
if (torqueLateral && torquedUseParams) {
UiElement frictionCoefficientFilteredElement = DeveloperUi::getFrictionCoefficientFiltered(frictionCoefficientFiltered, liveValid);
rw += drawBottomDevUIElement(p, rw, y, frictionCoefficientFilteredElement.value, frictionCoefficientFilteredElement.label, frictionCoefficientFilteredElement.units, frictionCoefficientFilteredElement.color);
UiElement latAccelFactorFilteredElement = DeveloperUi::getLatAccelFactorFiltered(latAccelFactorFiltered, liveValid);
rw += drawBottomDevUIElement(p, rw, y, latAccelFactorFilteredElement.value, latAccelFactorFilteredElement.label, latAccelFactorFilteredElement.units, latAccelFactorFilteredElement.color);
} else {
UiElement steeringTorqueEpsElement = DeveloperUi::getSteeringTorqueEps(steeringTorqueEps);
rw += drawBottomDevUIElement(p, rw, y, steeringTorqueEpsElement.value, steeringTorqueEpsElement.label, steeringTorqueEpsElement.units, steeringTorqueEpsElement.color);
UiElement bearingDegElement = DeveloperUi::getBearingDeg(bearingAccuracyDeg, bearingDeg);
rw += drawBottomDevUIElement(p, rw, y, bearingDegElement.value, bearingDegElement.label, bearingDegElement.units, bearingDegElement.color);
}
UiElement altitudeElement = DeveloperUi::getAltitude(gpsAccuracy, altitude);
rw += drawBottomDevUIElement(p, rw, y, altitudeElement.value, altitudeElement.label, altitudeElement.units, altitudeElement.color);
}
+37 -2
View File
@@ -7,9 +7,8 @@
#pragma once
#include <QPainter>
#include "selfdrive/ui/qt/onroad/hud.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h"
class HudRendererSP : public HudRenderer {
Q_OBJECT
@@ -18,4 +17,40 @@ public:
HudRendererSP();
void updateState(const UIState &s) override;
void draw(QPainter &p, const QRect &surface_rect) override;
private:
Params params;
void drawText(QPainter &p, int x, int y, const QString &text, QColor color = Qt::white);
void drawRightDevUI(QPainter &p, int x, int y);
int drawRightDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color);
int drawBottomDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color);
void drawBottomDevUI(QPainter &p, int x, int y);
bool lead_status;
float lead_d_rel;
float lead_v_rel;
bool torqueLateral;
float angleSteers;
float desiredCurvature;
float curvature;
float roll;
int memoryUsagePercent;
int devUiInfo;
float gpsAccuracy;
float altitude;
float vEgo;
float aEgo;
float steeringTorqueEps;
float bearingAccuracyDeg;
float bearingDeg;
bool torquedUseParams;
float latAccelFactorFiltered;
float frictionCoefficientFiltered;
bool liveValid;
QString speedUnit;
bool latActive;
bool steerOverride;
bool reversing;
cereal::CarParams::SteerControlType steerControlType;
cereal::CarControl::Actuators::Reader actuators;
};
+15 -1
View File
@@ -18,13 +18,22 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
"pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2",
"wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan",
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP"
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
"carControl", "gpsLocationExternal", "gpsLocation", "liveTorqueParameters",
"carStateSP", "liveParameters"
});
// update timer
timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update);
timer->start(1000 / UI_FREQ);
// Param watcher for UIScene param updates
param_watcher = new ParamWatcher(this);
connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
ui_update_params_sp(this);
});
param_watcher->addParam("DevUIInfo");
}
// This method overrides completely the update method from the parent class intentionally.
@@ -39,6 +48,11 @@ void UIStateSP::update() {
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) {
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update);
QObject::connect(this, &Device::displayPowerChanged, this, &DeviceSP::handleDisplayPowerChanged);
+4
View File
@@ -13,6 +13,7 @@
#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h"
#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
class UIStateSP : public UIState {
Q_OBJECT
@@ -73,6 +74,7 @@ private slots:
private:
std::vector<RoleModel> sunnylinkRoles = {};
std::vector<UserModel> sunnylinkUsers = {};
ParamWatcher *param_watcher;
};
UIStateSP *uiStateSP();
@@ -92,3 +94,5 @@ private:
DeviceSP *deviceSP();
inline DeviceSP *device() { return deviceSP(); }
void ui_update_params_sp(UIStateSP *s);
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
typedef struct UISceneSP : UIScene {
int dev_ui_info = 0;
} UISceneSP;
+5
View File
@@ -66,6 +66,11 @@ typedef struct UIScene {
uint64_t started_frame;
} UIScene;
#ifdef SUNNYPILOT
#include "sunnypilot/ui_scene.h"
#define UIScene UISceneSP
#endif
class UIState : public QObject {
Q_OBJECT
+12 -14
View File
@@ -23,7 +23,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
import cereal.messaging as messaging
from sunnypilot.sunnylink.api import SunnylinkApi
from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte
from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string
SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sunnypilot.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
@@ -184,14 +184,18 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s
try:
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,
"value": base64.b64encode(gzip.compress(get_param_as_byte(key)) if compression else get_param_as_byte(key)).decode('utf-8'),
"value": base64.b64encode(gzip.compress(value) if compression else value).decode('utf-8'),
"type": int(params.get_type(key).value),
"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 |= {"params": json.dumps(params_dict.get("params", []))} # Upcoming for settings v1
@@ -204,15 +208,9 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s
@dispatcher.add_method
def saveParams(params_to_update: dict[str, str], compression: bool = False) -> None:
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():
for key, value in params_to_update.items():
try:
params.put(key, value)
save_param_from_base64_encoded_string(key, value, compression)
except Exception as e:
cloudlog.error(f"sunnylinkd.saveParams.exception {e}")
+5 -24
View File
@@ -12,7 +12,7 @@ from enum import Enum
from typing import Any
from openpilot.common.git import get_branch
from openpilot.common.params import Params, ParamKeyType, ParamKeyFlag
from openpilot.common.params import Params, ParamKeyFlag
from openpilot.common.realtime import Ratekeeper
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_version
@@ -20,7 +20,7 @@ from openpilot.system.version import get_version
from cereal import messaging, custom
from sunnypilot.sunnylink.api import SunnylinkApi
from sunnypilot.sunnylink.backups.utils import decrypt_compressed_data, encrypt_compress_data, SnakeCaseEncoder
from sunnypilot.sunnylink.utils import get_param_as_byte
from sunnypilot.sunnylink.utils import get_param_as_byte, save_param_from_base64_encoded_string
class OperationType(Enum):
@@ -173,8 +173,7 @@ class BackupManagerSP:
self._update_progress(75.0, OperationType.RESTORE)
# Apply configuration
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._apply_config(config_data)
self.restore_status = custom.BackupManagerSP.Status.completed
self._update_progress(100.0, OperationType.RESTORE)
@@ -187,7 +186,7 @@ class BackupManagerSP:
self._report_status()
return False
def _apply_config(self, config_data: dict[str, str], all_values_encoded: bool = False) -> None:
def _apply_config(self, config_data: dict[str, str]) -> None:
"""Applies configuration data from a backup, but only for parameters marked as backupable."""
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}
@@ -199,26 +198,8 @@ class BackupManagerSP:
if param.lower() in backupable_set_lower:
# Find real param name (with correct casing)
real_param = next(p for p in backupable_params if p.lower() == param.lower())
param_type = self.params.get_type(real_param)
try:
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)
save_param_from_base64_encoded_string(real_param, encoded_value)
restored_count += 1
except Exception as e:
cloudlog.error(f"Failed to restore param {param}: {str(e)}")
+36 -2
View File
@@ -1,3 +1,5 @@
import base64
import gzip
import json
from sunnypilot.sunnylink.api import SunnylinkApi, UNREGISTERED_SUNNYLINK_DONGLE_ID
from openpilot.common.params import Params, ParamKeyType
@@ -58,13 +60,45 @@ def get_api_token():
print(f"API Token: {token}")
def get_param_as_byte(param_name: str) -> bytes:
def get_param_as_byte(param_name: str) -> bytes | None:
"""Get a parameter as bytes. Returns None if the parameter does not exist."""
params = Params()
param = params.get(param_name)
param_type = params.get_type(param_name)
if param is None:
return None
param_type = params.get_type(param_name)
if param_type == ParamKeyType.BYTES:
return bytes(param)
elif param_type == ParamKeyType.JSON:
return json.dumps(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.
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)
params.put(param_name, value)
@@ -9,3 +9,5 @@ export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.8"
fi
export STAGING_ROOT="/data/safe_staging"
+1 -1
View File
@@ -331,7 +331,7 @@ def hardware_thread(end_event, hw_queue) -> None:
# - TIZI, or
# - TICI and channel_type is "tici"
build_metadata = get_build_metadata()
is_unsupported_combo = TICI and build_metadata.channel_type != "tici"
is_unsupported_combo = TICI and HARDWARE.get_device_type() == "tici" and build_metadata.channel_type != "tici"
startup_conditions["not_tici"] = not is_unsupported_combo
onroad_conditions["not_tici"] = not is_unsupported_combo
set_offroad_alert("Offroad_TiciSupport", is_unsupported_combo, extra_text=build_metadata.channel)
+1 -1
View File
@@ -11,7 +11,7 @@ from openpilot.common.swaglog import cloudlog
from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date
RELEASE_SP_BRANCHES = ['release-c3']
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new']
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new', 'staging-tici']
MASTER_SP_BRANCHES = ['master']
RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] + RELEASE_SP_BRANCHES
TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] + TESTED_SP_BRANCHES