Compare commits

..

150 Commits

Author SHA1 Message Date
Jason Wen f90175833d sync: fix upstream conflicts 2026-07-16 12:52:22 -04:00
Jason Wen 14318d2f09 Merge commit '91b067cca0913d0cc9f8b5dab68e3302e278efdc' into sync-20260716
# Conflicts:
#	.github/labeler.yaml
#	.github/workflows/auto_pr_review.yaml
#	.github/workflows/docs.yaml
#	.github/workflows/release.yaml
#	.github/workflows/repo-maintenance.yaml
#	.github/workflows/tests.yaml
#	.github/workflows/ui_preview.yaml
#	README.md
#	SConstruct
#	conftest.py
#	docs/CARS.md
#	msgq_repo
#	opendbc_repo
#	openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py
#	openpilot/common/api.py
#	openpilot/common/hardware/hw.py
#	openpilot/common/version.py
#	openpilot/selfdrive/assets/fonts/Audiowide-Regular.ttf
#	openpilot/selfdrive/assets/sounds/prompt_single_high.wav
#	openpilot/selfdrive/assets/sounds/prompt_single_low.wav
#	openpilot/selfdrive/car/card.py
#	openpilot/selfdrive/car/helpers.py
#	openpilot/selfdrive/car/tests/test_car_interfaces.py
#	openpilot/selfdrive/car/tests/test_cruise_speed.py
#	openpilot/selfdrive/controls/lib/desire_helper.py
#	openpilot/selfdrive/controls/lib/longcontrol.py
#	openpilot/selfdrive/controls/plannerd.py
#	openpilot/selfdrive/controls/radard.py
#	openpilot/selfdrive/controls/tests/test_longcontrol.py
#	openpilot/selfdrive/modeld/compile_warp.py
#	openpilot/selfdrive/modeld/modeld.py
#	openpilot/selfdrive/monitoring/policy.py
#	openpilot/selfdrive/monitoring/test_monitoring.py
#	openpilot/selfdrive/selfdrived/events.py
#	openpilot/selfdrive/selfdrived/selfdrived.py
#	openpilot/selfdrive/ui/layouts/onboarding.py
#	openpilot/selfdrive/ui/mici/layouts/onboarding.py
#	openpilot/selfdrive/ui/soundd.py
#	openpilot/selfdrive/ui/tests/diff/replay.py
#	openpilot/selfdrive/ui/translations/app.pot
#	openpilot/system/athena/athenad.py
#	openpilot/system/athena/manage_athenad.py
#	openpilot/system/hardware/hardwared.py
#	openpilot/system/loggerd/config.py
#	openpilot/system/manager/github_runner.sh
#	openpilot/system/manager/manager.py
#	openpilot/system/updated/updated.py
#	panda
#	pyproject.toml
#	scripts/lint/lint.sh
#	system/manager/process_config.py
#	system/statsd.py
#	tinygrad_repo
#	tools/release/build_release.sh
#	tools/release/build_stripped.sh
#	uv.lock
2026-07-16 00:58:32 -04:00
Jason Wen 70f2c3ac79 Sync: commaai/openpilot:mastersunnypilot/sunnypilot:master (#1875) 2026-07-15 23:45:06 -04:00
Jason Wen a43fc920b5 Merge commit 'b871364e1f48af04113f05c0f6604a9c42f57ac7' into sync-20260715-1
# Conflicts:
#	.github/workflows/prebuilt.yaml
#	common/api.py
#	common/params_keys.h
#	opendbc_repo
#	selfdrive/controls/controlsd.py
#	selfdrive/controls/lib/desire_helper.py
#	selfdrive/modeld/modeld.py
#	selfdrive/ui/mici/layouts/settings/device.py
#	selfdrive/ui/soundd.py
#	system/athena/athenad.py
#	system/athena/registration.py
#	system/hardware/hardwared.py
#	system/loggerd/deleter.py
#	system/manager/manager.py
#	system/manager/process_config.py
#	system/sentry.py
#	system/updated/updated.py
#	tinygrad_repo
#	tools/scripts/uiview.py
2026-07-15 23:33:33 -04:00
Adeeb Shihadeh 91b067cca0 jenkins speedups (#38328)
* power draw first

* speedup encoder

* speed up hardware test suites

* refresh IRQ actions after hardware setup

* skip unused big model in device checkout

* move LFS pruning off device checkout path

* wtf is that for?

* reverts

* locality

* rm that
2026-07-15 18:43:48 -07:00
Adeeb Shihadeh 41886a90da rm redundant params tests 2026-07-15 18:40:08 -07:00
Daniel Koepping 680dd7cf3b bump opendbc (#38352) 2026-07-15 16:52:25 -07:00
Adeeb Shihadeh db99c8e5a6 op: add -u to scons command line (#38348) 2026-07-15 15:39:56 -07:00
Jason Wen 48a5bdc8e4 Revert "msgq: point back to comma's" (#1874)
* Revert "msgq: point back to comma's (#1498)"

This reverts commit 04eac60983.

* point
2026-07-15 17:18:14 -04:00
Jason Wen 79b79edd2f MADS: add tests and prevent re-enable at standstill in Pause mode (#1871)
* tests

* lateral mismatch

* no unittest oops
2026-07-15 17:02:34 -04:00
Adeeb Shihadeh 2b1fd89064 annotate radar fields (#38347) 2026-07-15 12:01:46 -07:00
Adeeb Shihadeh ec72ee096d cleanup unused radar fields (#38346)
* cleanup unused radar fields

* bump opendbc

* this is nice for debugging

* bring that back too

* bring these back

* more revert
2026-07-15 11:52:31 -07:00
Harald Schäfer eee13472ce longitudinal: add e2e flag to tuning report (#38345) 2026-07-15 10:31:03 -07:00
Shane Smiskol d275c65f75 fix ui.py 2026-07-14 23:53:52 -07:00
Adeeb Shihadeh a9ffebe96e plannerd: check all services for validity (#38341)
* longitudinal planner: carControl must be valid

* plannerd: check all services for validity
2026-07-14 20:40:15 -07:00
Adeeb Shihadeh 45661ee8c0 tmp disable ui_report 2026-07-14 19:24:16 -07:00
Kacper Rączy c16039e0bc lagd: lower max lag (#38307)
* Max lag 0.6

* Fix tests

* 650ms
2026-07-15 02:17:30 +00:00
Adeeb Shihadeh d1e143ac98 longcontrol: simplify state machine (#38337) 2026-07-14 19:01:22 -07:00
Adeeb Shihadeh 45b53cf66a Rename LeadData.status to LeadData.present (#38339)
* rename LeadData.status to LeadData.present

* rename LeadData.status to LeadData.present

* resolve longitudinal MPC merge conflict
2026-07-14 18:54:07 -07:00
Daniel Koepping cf8371c2c5 add usb link err counter (#38338)
* add usb link err counter

* ci
2026-07-14 17:44:46 -07:00
Adeeb Shihadeh f42dbbb01c longitudinal planner: remove unused model trajectory parsing (#38336) 2026-07-14 16:01:45 -07:00
Adeeb Shihadeh 4fd4ddb438 long_mpc: remove unused solver state and timings (#38335) 2026-07-14 15:55:04 -07:00
Adeeb Shihadeh 503531ab38 replace psutil with simple /proc helper (#38326)
* replace psutil with simple /proc helper

* mv to common
2026-07-11 13:51:22 -07:00
Adeeb Shihadeh a52da0d3df bump msgq, no runtime deps 2026-07-11 13:08:39 -07:00
Adeeb Shihadeh a26254304f Move eigen dependency into rednose (#38321)
* Move eigen dependency into rednose

* Declare complete rednose package dependencies

* Update rednose submodule

* Update rednose submodule

* Update rednose submodule

* Fix rednose dependency integration

* bump rednose
2026-07-11 12:59:49 -07:00
stef c91731b13a athena: remove take snapshot (#38319)
remove snapshot
2026-07-09 19:15:23 -07:00
Adeeb Shihadeh 60924556f6 athena: minimal json-rpc implementation (#38318)
* athena: minimal json-rpc implementation

* lil more
2026-07-09 16:55:49 -07:00
Adeeb Shihadeh ee11c24cf4 ci: test op setup with empty submodules (#38316)
* ci: test op setup with empty submodules

* ci: init submodules via op setup instead of checkout

* ci: keep submodules for build release

* ci: drop explicit submodules: false

* ci: remove checkout comment
2026-07-09 12:26:56 -07:00
Adeeb Shihadeh 6392096de5 op setup: init submodules before uv sync (#38314) 2026-07-09 09:31:34 -07:00
Adeeb Shihadeh 39117a5879 remove submodule symlinks (#38312) 2026-07-08 21:30:17 -07:00
Adeeb Shihadeh 1478d0c476 Fix diff report artifact download (#38313) 2026-07-08 21:25:43 -07:00
Adeeb Shihadeh f283f6703c Revert "try removing submodule symlinks (#38310)"
This reverts commit b827c0f55e.
2026-07-08 21:12:12 -07:00
Adeeb Shihadeh b827c0f55e try removing submodule symlinks (#38310) 2026-07-08 21:11:39 -07:00
Adeeb Shihadeh bf5540c364 rm pyserial (#38311)
* rm pyserial

* lil more

* lil more
2026-07-08 21:06:53 -07:00
Adeeb Shihadeh 3b3f5967ec rm crcmod-plus (#38309)
* rm crcmod-plus

* cleanup
2026-07-08 20:43:12 -07:00
Adeeb Shihadeh 1e49eac4d1 rm libyuv (#38306)
* rm libyuv

* cleanup
2026-07-08 20:27:27 -07:00
Adeeb Shihadeh 9877f6ac0e ffmpeg: use shared libraries (#38308) 2026-07-08 20:20:24 -07:00
Adeeb Shihadeh 1ab1ed7453 Use PyPI comma-deps packages (#38295) 2026-07-08 19:35:39 -07:00
Adeeb Shihadeh cb82722dd5 remove libjpeg (#38305)
* remove libjpeg

* lil smaller

* happy linter
2026-07-08 12:21:22 -07:00
Adeeb Shihadeh 960c988355 Replace xvfb with headless raylib backend (#38298) 2026-07-08 11:53:47 -07:00
Shane Smiskol 05cf8023a9 fix cabana 2026-07-07 16:14:26 -07:00
Shane Smiskol 1b6e127e68 fix juggle.py 2026-07-07 15:40:56 -07:00
Adeeb Shihadeh c02cf706a6 fix cabana launch script 2026-07-07 14:09:32 -07:00
Adeeb Shihadeh 0d93f0e820 declare submodules as dependencies in pyproject (#38300)
* declare submodules as dependencies in pyproject

* spidev

* lock
2026-07-07 10:36:49 -07:00
stef 6de5d2c23a revert checkout@v7 to fix nightly release (#38294)
* revert back to checkout@v4

* add comment
2026-07-05 20:04:06 -07:00
Adeeb Shihadeh 92f3d24c8a Remove lateral MPC library (#38281) 2026-07-03 17:17:51 -07:00
Adeeb Shihadeh d606014ce2 ci: delete closed PR branches automatically (#38280) 2026-07-03 09:45:08 -07:00
commaci-public 2aa934e7c2 [bot] Update Python packages (#38263)
Update Python packages

Co-authored-by: Vehicle Researcher <user@comma.ai>
2026-07-02 19:04:06 -07:00
Adeeb Shihadeh 70a6efb8fc ci: update GitHub Actions to latest versions (#38278)
* ci: update GitHub Actions to latest versions

Bump all workflow actions to their latest releases, including major
updates for checkout (v7), upload-artifact (v7), github-script (v9),
action-download-artifact (v21), and thollander comment action (v3).

* ci: pin third-party actions to commit SHAs

Keep official GitHub actions on version tags, but pin community
actions to immutable commit hashes for supply-chain safety.
2026-07-02 19:00:35 -07:00
Adeeb Shihadeh 07ec389f4c bump ty (#38277)
* bump ty

* cleanup
2026-07-02 18:48:53 -07:00
Adeeb Shihadeh 9d03e737dd test_onroad: fix service timing threshold 2026-07-02 14:48:32 -07:00
YassineYousfi 4532320fb3 desire: enter preLaneChange if blinker before engaged (#38276) 2026-07-01 18:52:11 -07:00
YassineYousfi 4c5bef5840 rm modeld/models README 2026-07-01 18:50:52 -07:00
Jimmy 8e0ba7a91e modeld: use correct intrinsics for extra transform when wide camera disabled (#38239)
use correct intrinsics for extra transform when wide camera disabled
2026-07-01 17:17:25 -07:00
Armand du Parc Locmaria fa0c6876d3 lebowski model (#38268)
* Reapply "usbgpu: never materialize model weights in RAM" (#38271)

This reverts commit 8196b743af.

* release oob buffers as we load them

* don't leak fds

* lint

* bigger big

* don't roundtrip the features to qcom

* Revert "don't roundtrip the features to qcom"

This reverts commit 0d2ca73d7a71bd32c46beda425957339f1dbbcf8.

* tmp bump allowed lag

* faster

* Revert "tmp bump allowed lag"

This reverts commit b407702a0242074eb12abdff14e61789fc05109a.
2026-07-01 01:26:11 -07:00
Armand du Parc Locmaria a2c554805b Reapply "usbgpu: never materialize model weights in RAM" (#38273)
* Reapply "usbgpu: never materialize model weights in RAM" (#38271)

This reverts commit 8196b743af.

* release oob buffers as we load them

* don't leak fds

* lint

* 10s faster loading time for big model
2026-07-01 01:22:48 -07:00
Daniel Koepping e1e9efb965 add USB logging (#38186)
* usb logging

* fix test

* discover by PID

* hardwared, add counters

* edit msg

* add manufacturer

* reorder

* rm portli

* rm this
2026-06-30 16:17:26 -07:00
Armand du Parc Locmaria 8196b743af Revert "usbgpu: never materialize model weights in RAM" (#38271)
Revert "usbgpu: never materialize model weights in RAM (#38265)"

This reverts commit 724971afc3.
2026-06-30 13:13:23 -07:00
Armand du Parc Locmaria 724971afc3 usbgpu: never materialize model weights in RAM (#38265)
* bigger big

* don't load big model in ram

* patch tg to allow out of band buffers

* dump/load oob

* roundtrip pkl to disk

* streaming chunking

* to_disk

* fixup

* linter

* Revert "bigger big"

This reverts commit 7a8147dd7fab88db963d512ab4008de940714025.
2026-06-30 00:35:12 -07:00
Armand du Parc Locmaria 4fc5e308f3 Reapply "modeld: enqueue in policy" (#38267)
* Reapply "modeld: enqueue in policy" (#38266)

This reverts commit 8a3bbcdd4e.

* correct device for policy inputs
2026-06-30 00:31:54 -07:00
Armand du Parc Locmaria 8a3bbcdd4e Revert "modeld: enqueue in policy" (#38266)
Revert "modeld: enqueue in policy (#38264)"

This reverts commit 8c927d0fc2.
2026-06-30 00:15:50 -07:00
Armand du Parc Locmaria 8c927d0fc2 modeld: enqueue in policy (#38264)
* move enqueue to policy

* typo

* correct shape

* correct shape
2026-06-29 13:08:37 -07:00
Armand du Parc Locmaria 97215b17d7 modeld: remove prepare_only (#38252)
remove prepare_only
2026-06-29 11:53:27 -07:00
Toby Penner 61b6d52b01 Fix broken tools README link (#38260) 2026-06-28 10:40:05 -07:00
ZwX1616 e9e5548edc bump DM NoEntryAlert priority (#38253) 2026-06-26 18:23:01 -07:00
ZwX1616 f9c555a3fe DM: update escalation/lockout specs; softer sounds (#38244) 2026-06-26 14:57:19 -07:00
ZwX1616 069506aa36 sound: use log enum (#38246) 2026-06-26 13:55:30 -07:00
probablyanasian 5629b5a83d bump sensors test limits (#38245)
* bump sensors test limits

* loosen back to 60 c max due to device density

* 10 dps in 3d is 0.3 rad max
2026-06-25 18:06:42 -07:00
Adeeb Shihadeh addca46f68 move some tools back to root (#38235) 2026-06-24 19:21:19 -07:00
Harald Schäfer 7d325d6650 Simplify lane change logic (#38229)
* desire cleanups

* remove the finalize timeout

---------

Co-authored-by: Robbe Derks <robbe.derks@gmail.com>
2026-06-23 11:34:05 -07:00
commaci-public 288aa60bf4 [bot] Update Python packages (#38227)
* Update Python packages

* fix numpy reshape warning

* relock

---------

Co-authored-by: Vehicle Researcher <user@comma.ai>
Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2026-06-22 15:28:11 -07:00
Adeeb Shihadeh cddb6103eb webrtc: replace aiohttp with stdlib http server (#38226)
* webrtc: replace aiohttp with stdlib http server

* lil more

* simplify

* remove aiohttp error response helper

---------

Co-authored-by: stefpi <19478336+stefpi@users.noreply.github.com>
2026-06-22 14:25:19 -07:00
Adeeb Shihadeh 1474d5474d move root include to the end to prevent using stale build products 2026-06-22 09:45:00 -07:00
Adeeb Shihadeh 2e9fa514bb simple pip install test (#38218)
* fails

* lil more

* try that
2026-06-21 19:39:21 -07:00
Adeeb Shihadeh 8a9ca15464 cleanup leftovers from the shuffling (#38224)
* cleanup leftovers

* rm

* one more
2026-06-21 19:17:01 -07:00
Adeeb Shihadeh 5edc0bd89d mv root dirs into nested openpilot (#38219) 2026-06-21 18:49:46 -07:00
Adeeb Shihadeh 20e0f21b58 prefix paths with openpilot (#38223)
* prefix paths with openpilot

* fix setup root detection

* fix split branch codespell skips
2026-06-21 18:28:35 -07:00
Adeeb Shihadeh 37eda06c95 move cereal/ into nested openpilot (#38220) 2026-06-21 17:55:17 -07:00
Adeeb Shihadeh df1663c58d the one true car.capnp (#38221)
* the one true car.capnp

* fix jotpluggler capnp import path

* bump to master
2026-06-21 17:32:19 -07:00
Adeeb Shihadeh bc168bb33a rm opencv-python-headless (#38214)
* rm opencv-python-headless

* and metadrive

* skip sim
2026-06-21 12:49:12 -07:00
Fang Li d79267fa22 modem: fall back when cellular DNS is missing (#38213)
* modem: fall back when cellular DNS is missing

* rm test

* comment

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2026-06-21 12:37:56 -07:00
Adeeb Shihadeh cea5273f1f move version to common/ (#38210) 2026-06-21 09:43:53 -07:00
Adeeb Shihadeh d8bd6c784e rename androidLog -> operatingSystemLog (#38209)
not android
2026-06-21 09:11:04 -07:00
Adeeb Shihadeh ad5151b38b single IsOffroad param (#38205) 2026-06-20 16:55:48 -07:00
Adeeb Shihadeh fddb9fb31c remove statsd (#38204) 2026-06-20 16:35:28 -07:00
Adeeb Shihadeh b871364e1f remove rest of docker (#38203) 2026-06-20 16:22:37 -07:00
Adeeb Shihadeh f0831e549b rm dead updated tests 2026-06-20 16:20:05 -07:00
Adeeb Shihadeh 7cee534b72 rm dead scripts 2026-06-20 16:16:15 -07:00
Adeeb Shihadeh e0e9f1a4d3 move test_power_draw.py 2026-06-20 16:12:54 -07:00
Adeeb Shihadeh 160942dfde move HAL to common/hardware/ (#38202)
* move HAL to common/hardware/

* lint

* rm shim
2026-06-20 16:09:22 -07:00
Adeeb Shihadeh 52e25ebe4e we don't have this anymore 2026-06-20 16:08:24 -07:00
Adeeb Shihadeh 3b4077d31b system/hardware/ cleanup (#38201)
* imei cleanup

* rm get_modem_version

* cpp cleanup

* that's not in the panda!
2026-06-20 15:51:01 -07:00
Adeeb Shihadeh f0f7b877c3 athena: remove getNetworks (#38200) 2026-06-20 15:27:36 -07:00
Adeeb Shihadeh cb6e422c91 move esim to common/ (#38199)
* move esim to common/

* add pem
2026-06-20 15:16:48 -07:00
Adeeb Shihadeh f48e99b335 updated: remove (never used) casync support (#38198) 2026-06-20 14:19:25 -07:00
Adeeb Shihadeh b2a7084906 move webcam to system/camerad/ (#38196)
* move webcam to system/camerad/

* lil more
2026-06-20 13:54:47 -07:00
Adeeb Shihadeh 62b97fabf7 merge selfdrive/debug/ into tools/scripts/ (#38195)
* merge selfdrive/debug/ into tools/scripts/

* rm unused

* single timings script

* lil more

* profiling is scripts quality
2026-06-20 13:52:16 -07:00
stef f257544f1b tools: goodbye webjoystick (#38194)
remove body web tool
2026-06-19 19:08:21 -07:00
stef 45d8bcd7f3 car livestream (#38193)
* athenad and webrtcd updates

* remove feature stream services from webrtcd split

* stream encoder thread

* reduce diff

* wire webrtc to livestream camera encoder

* request livestream camera switch service

* remove camera list in favour of init camera field

* remove cors

* clean

* remove unused

* remove extra try except

* add back exception trace

* add stream road camera info to stream cameras

* fix

* clean diff

* clean diff

* add testJoystick only on body

* fix camera list

* remove reference to future service

* encode all cameras and swap in video track in webrtc

* clean

* explicitly gate bridge send

* clean leftover

* initial idea

* add a watchdog to kill the process after disconnect or onroad

* start camerad and stream encoderd as well

* add message handler even without bridge

* remove carState when offroad

* transition to onroad works for body, kill for car not working

* turn off stream processes on started car

* cereal messaging sub field allow list, carparamspersistent, ping webrtcd to see when awake

* fix imports

* fix and increase max retries

* reduce max retries, increase timeout

* timeout on wait for connection

* remove cereal sub message and update process config to run on body ignition

* debounce 5s on teardown

* update error messages

* fix tear down crash

* clean

* clean

* fix lint
2026-06-19 18:20:55 -07:00
stef 0038d84e13 webrtc: do not replace connection on prewarm (#38190)
* refactor

* remove accidental change

* clean error handler

* clean structure

* only is other stream is active and enabled

* better structure

* remove enabled None default

* update comment
2026-06-17 14:53:06 -07:00
Christopher Milan cd2c590d50 bump tinygrad (#38176)
* bump tinygrad

* wip: try this

* origin/master

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Comma Device <device@comma.ai>
2026-06-17 11:24:11 -07:00
Harald Schäfer ddc8d37136 Desire keep pulsing is dead (#38187) 2026-06-17 10:48:53 -07:00
David d4db600d4b ui diff: add multithreading and improve logging (split from #37328) (#37474)
* split out and refactor

* simplify

* rm extra line

* use threadpoolexecutor to handle exceptions

* simplify

* improve

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2026-06-17 10:27:46 -07:00
David 5ab7e48479 fix(ui): fill and center onroad camera if too small (#37673)
* ui: ensure zoom accommodates full view area in AugmentedRoadView

* fix: use _content_rect

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2026-06-17 09:54:24 -07:00
Andi Radulescu e2e3703ae4 ui: fix false "NO PANDA" flash on screen wake (#37404)
* ui: fix false "NO PANDA" flash on screen wake

* ui: use recv_time for panda timeout instead of frame counting

* ui: use sm.alive for panda timeout
2026-06-17 08:14:07 -07:00
stef 3939f1e92c webrtc: signal shutdown over datachannel (#38185)
signal shut down, fix enabled bug
2026-06-16 18:33:59 -07:00
Adeeb Shihadeh 82a945d145 scons: enforce max file size for build products (#38077)
* scons: enforce max file size for build products

* only for minimal build

* enable

* fix build product size check
2026-06-16 17:49:12 -07:00
hypery11 93224b3c05 speedup pytest collection (#37977)
Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2026-06-16 16:28:37 -07:00
Robbie Wadley 8a80bd70e7 fix: cruise faults should not disable silently (#37557)
Co-authored-by: Jason Young <46612682+jyoung8607@users.noreply.github.com>
Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2026-06-16 16:25:40 -07:00
David 29e7f362ed clip: add wide camera stream (#37677)
clip: add support for wide camera frames in clip rendering
2026-06-16 15:25:27 -07:00
eFini 3c4790c089 bugfix: should be Error not Exception (#38071)
should be Error not Exception
2026-06-16 14:58:30 -07:00
Adeeb Shihadeh c0456590f6 mici ui: add branch switcher (#38153)
* mici ui: add branch switcher

* add icon
2026-06-16 14:01:14 -07:00
stef f3b1f97afa webrtc/athena: speed up first time stream connection (#38184)
* move streamrequest to models file, prewarm in webrtcd

* cleanup

* debug

* change print to logger.info
2026-06-16 13:48:16 -07:00
Apostolos Georgiadis 4cefe7239d fetch_image_from_route: fix missing f-string in error message (#38183) 2026-06-16 09:23:30 -07:00
stef 586dd4c611 webrtc: add error handling middleware (#38182)
* webrtc add error handling middleware

* clean diff
2026-06-15 19:19:07 -07:00
stef 291315ab25 webrtc/athena: allow webrtc connection with only data channel (#38177)
* add param that requests keyframe

* set keyframe request to false on cleanup

* timing and errors

* video_enabled option and ability to add video later

* remove loggerd changes from this PR

* video enabled default to on, clean identifier

* request keyframe on start regardless of video enabled or not

* move error handling additions to different PR
2026-06-15 19:11:28 -07:00
stef 37390743c1 webrtc/athena: revert addIceCandidate changes (#38180)
* revert addIceCandidate changes

* remove accidental file
2026-06-15 16:50:07 -07:00
stef 15fb1a809d webrtc: revert prewarm and import movements (#38181) 2026-06-15 16:49:56 -07:00
stef 0240a62fd9 encoder: add ability to request keyframes from livestream encoders (#38172)
* add param that requests keyframe

* set keyframe request to false on cleanup

* clean

* timing and errors

* remove other changes

* clean diff

* clean diff

* clean up live adaptation params and functions

* better function names

* fix syntax

* remove unused var

* clean diff
2026-06-15 14:48:13 -07:00
Willem Melching fafcee04f4 replay: fix current time (#38179) 2026-06-15 08:44:01 -07:00
Shane Smiskol 2feca929ab bump opendbc (#38175)
bump
2026-06-12 19:17:09 -07:00
Harald Schäfer a40fa3a0b0 modeld: run the driving model as a single combined onnx (#38173) 2026-06-12 18:35:52 -07:00
Daniel Koepping f9cc67896c Improve lateral maneuvers (#38159)
* improve lat maneuver report

* rm torque jerk

* add jitter maneuver

* add to 30mph

* add jerk

* color

* more state

* override

* abort reason

* abort

* rm text

* pad for small text
2026-06-11 18:59:56 -07:00
stef cd052f124e webrtc: speed ups (#38163)
- patch `aioice` to only get host address of active network interface (wlan0 when on wifi, ppp0 when not)
- use common webrtc session between `startStream` and `addIceCandidates`
- first time connection speed ups
  - move imports to top of file to move delay to webrtcd server start up not first connection
  - warm up webrtc stack by creating a webrtc connection object to import all submodule classes
2026-06-11 16:10:34 -07:00
stef 14cff000e9 feat(webrtc): addIceCandidate dispatcher (#38161)
* addIceCandidate dispatcher

* clean

* fix lint
2026-06-11 12:58:04 -07:00
YassineYousfi 9ef3cfdf9a compile modeld: use tmp file name (#38170) 2026-06-11 12:29:56 -07:00
Harald Schäfer 4024d13dda Revert "Op model16 deep (#38073)" (#38166)
This reverts commit f02d134f40.
2026-06-11 09:32:24 -07:00
Armand du Parc Locmaria b2bb71b4b7 modeld: less cross device copies (#38168)
* single copy for frames

* single copy out

* missed this

* pack npy inputs to the policy

* strict zip

* strict zip
2026-06-10 22:02:28 -07:00
stef 98e5f547ea fix(athena): outbound rpc stuck behind low prio queue timeout (#38160)
* priority queue instead of 2 queues

* add assert on priority instead of default

* fix lint
2026-06-10 19:28:26 -07:00
Kacper Rączy 5a7b710d90 lagd: invalidate cached estimates on major estimator update (#38162)
* Versioning

* comment

* Tests

* fix issues
2026-06-11 02:25:23 +00:00
Shane Smiskol 7661e03d1e bump opendbc (#38167)
bump
2026-06-10 18:51:28 -07:00
Armand du Parc Locmaria bdac9efa1e modeld: compress modelV2 into drivingModelData (#38165)
* modeld: compress modelV2 into drivingModelData

* slightly better

* less diff

* whitespace

* dedupe with migrate?

* Revert "dedupe with migrate?"

This reverts commit f31fe34994f4331f45e14a035df4847905d37540.
2026-06-10 17:06:09 -07:00
Shane Smiskol 433c52f623 radar errors: only gate enable if openpilot long (#38145)
only prevent engagement for op long cars
2026-06-09 16:56:48 -07:00
Daniel Koepping 24d373062d soundd: fix long wav playback and mic feedback (#38155)
fix soundd long duration playback and mic feedback
2026-06-09 16:37:45 -07:00
Adeeb Shihadeh 41390a95b8 Jenkinsfile: use SSH ControlMaster to multiplex device connections (#38157) 2026-06-09 16:15:54 -07:00
Trey Moen 57b5eb3113 op.sh: fix arg quoting so special chars survive eval (#37967)
op.sh: don't eval CMD, run argv directly so special chars work

Previously op_run_command joined "$@" into a single string and eval'd
it, which broke on inputs containing shell metacharacters. For example,
'op esim --download "LPA:1$[rsp.truphone.com]($url)$QRF-SPEEDTEST" name'
would fail with a syntax error on the unquoted parens. Run "$@" instead
and only use $* for the printed display string. Also quote the unquoted
$@ in the other op_run_command callers.
2026-06-09 11:44:57 -07:00
Bruce Wayne 7abe0205fd modeld test: 1ms extra buffer 2026-06-08 15:17:07 -07:00
Daniel Koepping ef3ec30fe2 bump opendbc - ID.4 release (#38150)
id4 release
2026-06-08 09:58:57 -07:00
Adeeb Shihadeh b0600a4225 rm unused updated test 2026-06-07 10:00:08 -07:00
Daniel Koepping cc110d1eb1 Add driverMonitoringEscalation for stock DM trigger (#38143)
* trigger dm

* rename

* ci

* opendbc bump
2026-06-06 12:02:46 -07:00
CommaKia bfd8d4868d cabana: fix "seperated" typo in findsignal placeholders (#38142) 2026-06-05 23:32:58 -07:00
Daniel Koepping 14a00e0cc8 Add latcontrol for curvature (#38141)
* undeprecate curvature

* use pid_log

* bump

* fix test

* bump

* bump

* fix

* tune saturation

* bump

* fix torque bar

* bump

* rm redundant

* reduce sesitivity

* pid

* bump

* reset opendbc

* preserve sorting

* bump opendbc
2026-06-05 20:32:30 -07:00
Armand du Parc Locmaria 268126f376 usbgpu: pin modeld (#38123)
usbgpu: pin modeld to core 7
2026-06-03 23:32:32 -07:00
Kacper Rączy 900a896c63 lagd: higher min speed (#38133)
* Higher min vego

* Use CV

* Bump speed in simulated tests
2026-06-04 03:24:27 +00:00
Daniel Koepping c01b8be968 set max cpu core frequency (#38132)
cap cpu core frequency
2026-06-03 19:35:51 -07:00
brian 094591793a A valid livePose should not carry a timestamp produced from uninitialized Kalman filter time (#38124)
* locationd: require finite filter time for valid livePose

* cast to bool and rename
2026-06-03 23:25:39 +00:00
Harald Schäfer f02d134f40 Op model16 deep (#38073)
* modeld: RL driving model with 3-file split

Split the driving model into vision + off_policy + on_policy ONNX
files and wire up the RL policy:

- 3-file model split (vision / off_policy / on_policy), replacing the
  combined big_driving_policy/vision models
- compiler updates for the split models
- actually consume the policy action in modeld
- add desire state to the driving model
- model iterations (smoothness, off/on-policy weight updates)

* modeld: update driving model

* 1e72cf5a-785f-45ea-888f-28cdb14785de/100

* tinygrad hack

* fix parsing

* looser timing

* big

* Remove unnecessary modeld rebase changes

* Tighten modeld split cleanup

---------

Co-authored-by: Comma Device <device@comma.ai>
Co-authored-by: Armandpl <adpl33@gmail.com>
2026-06-03 12:48:58 -07:00
Daniel Koepping 5b3d5f74ed test models: add VW MEB angle exception (#38119)
* add vw meb exception

* comment
2026-06-02 19:23:31 -07:00
stef 866cd01f32 webrtc: initialize on medium bitrate (#38129)
initialize on medium quality
2026-06-02 18:07:12 -07:00
Harald Schäfer d09a411cb4 Refactor compile_modeld model setup (#38128) 2026-06-02 16:35:40 -07:00
1545 changed files with 5329 additions and 7176 deletions
-18
View File
@@ -1,18 +0,0 @@
**/.git
.DS_Store
*.dylib
*.DSYM
*.d
*.pyc
*.pyo
.*.swp
.*.swo
.*.un~
*.tmp
*.o
*.o-*
*.os
*.os-*
venv/
.venv/
+2 -2
View File
@@ -10,5 +10,5 @@
*.otf filter=lfs diff=lfs merge=lfs -text *.otf filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text openpilot/selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text openpilot/common/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
+6 -6
View File
@@ -8,24 +8,24 @@ chore:
car: car:
- changed-files: - changed-files:
- any-glob-to-all-files: '{selfdrive/car/**,opendbc_repo}' - any-glob-to-all-files: '{openpilot/selfdrive/car/**,opendbc_repo}'
simulation: simulation:
- changed-files: - changed-files:
- any-glob-to-all-files: 'tools/sim/**' - any-glob-to-all-files: 'openpilot/tools/sim/**'
ui: ui:
- changed-files: - changed-files:
- any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}' - any-glob-to-all-files: '{openpilot/selfdrive/assets/**,openpilot/selfdrive/ui/**,openpilot/system/ui/**}'
tools: tools:
- changed-files: - changed-files:
- any-glob-to-all-files: 'tools/**' - any-glob-to-all-files: 'openpilot/tools/**'
multilanguage: multilanguage:
- changed-files: - changed-files:
- any-glob-to-all-files: 'selfdrive/ui/translations/**' - any-glob-to-all-files: 'openpilot/selfdrive/ui/translations/**'
autonomy: autonomy:
- changed-files: - changed-files:
- any-glob-to-all-files: "{selfdrive/modeld/models/**,selfdrive/test/process_replay/model_replay_ref_commit,sunnypilot/modeld*/models/**}" - any-glob-to-all-files: "{openpilot/selfdrive/modeld/models/**,openpilot/selfdrive/test/process_replay/model_replay_ref_commit,openpilot/sunnypilot/modeld*/models/**}"
+2 -2
View File
@@ -44,8 +44,8 @@ Explain how you tested this bug fix.
**Checklist** **Checklist**
- [ ] added entry to CAR in selfdrive/car/*/values.py and ran `selfdrive/car/docs.py` to generate new docs - [ ] added entry to CAR in openpilot/selfdrive/car/*/values.py and ran `openpilot/selfdrive/car/docs.py` to generate new docs
- [ ] test route added to [routes.py](https://github.com/commaai/openpilot/blob/master/selfdrive/car/tests/routes.py) - [ ] test route added to [routes.py](https://github.com/commaai/openpilot/blob/master/openpilot/selfdrive/car/tests/routes.py)
- [ ] route with openpilot: - [ ] route with openpilot:
- [ ] route with stock system: - [ ] route with stock system:
- [ ] car harness used (if comma doesn't sell it, put N/A): - [ ] car harness used (if comma doesn't sell it, put N/A):
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
issues: write issues: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with: with:
submodules: false submodules: false
@@ -24,7 +24,7 @@ jobs:
# Check PR target branch # Check PR target branch
- name: check branch - name: check branch
uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5 uses: Vankka/pr-target-branch-action@5da68a42bcb7b43d39104295a876a6f3f8d7908b
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+24 -11
View File
@@ -6,7 +6,7 @@ on:
- master - master
pull_request: pull_request:
paths: paths:
- 'cereal/**' - 'openpilot/cereal/**'
workflow_dispatch: workflow_dispatch:
workflow_call: workflow_call:
inputs: inputs:
@@ -30,11 +30,25 @@ jobs:
- name: Checkout sunnypilot cereal - name: Checkout sunnypilot cereal
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
sparse-checkout: cereal sparse-checkout: |
openpilot/cereal
uv.lock
submodules: false
- name: Init sunnypilot opendbc submodule - name: Init sunnypilot opendbc submodule
run: git submodule update --init --depth 1 opendbc_repo run: git submodule update --init --depth 1 opendbc_repo
- name: Locate sunnypilot capnp import paths
id: locate-sp-capnp
run: |
SP_IMPORT_ARGS=""
SP_CAR_CAPNP=$(find opendbc_repo -maxdepth 4 -name car.capnp -path '*/opendbc/car/car.capnp' -printf '%h\n' -quit)
if [ -n "$SP_CAR_CAPNP" ]; then
SP_IMPORT_ARGS="-I $SP_CAR_CAPNP"
echo "Found sunnypilot car.capnp at: $SP_CAR_CAPNP"
fi
echo "import_args=$SP_IMPORT_ARGS" >> "$GITHUB_OUTPUT"
- name: Checkout upstream openpilot - name: Checkout upstream openpilot
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
@@ -65,20 +79,19 @@ jobs:
fi fi
echo "import_args=$IMPORT_ARGS" >> "$GITHUB_OUTPUT" echo "import_args=$IMPORT_ARGS" >> "$GITHUB_OUTPUT"
- name: Install uv - name: Install pycapnp
run: pip install uv run: |
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
pip install "pycapnp==${PYCAPNP_VER}"
- name: Generate sunnypilot schema - name: Generate sunnypilot schema
run: | run: |
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))") python3 openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py \
uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \ -g -f /tmp/sp_schema.json --cereal-dir openpilot/cereal \
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \ ${{ steps.locate-sp-capnp.outputs.import_args }}
-g -f /tmp/sp_schema.json --cereal-dir cereal
- name: Validate against upstream - name: Validate against upstream
run: | run: |
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))") python3 openpilot/cereal/messaging/tests/validate_sp_cereal_upstream.py \
uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
-r -f /tmp/sp_schema.json --cereal-dir ${{ steps.locate-capnp.outputs.cereal_dir }} \ -r -f /tmp/sp_schema.json --cereal-dir ${{ steps.locate-capnp.outputs.cereal_dir }} \
${{ steps.locate-capnp.outputs.import_args }} ${{ steps.locate-capnp.outputs.import_args }}
+24 -12
View File
@@ -17,29 +17,41 @@ jobs:
- name: Wait for process replay - name: Wait for process replay
id: wait id: wait
continue-on-error: true continue-on-error: true
uses: lewagon/wait-on-check-action@v1.3.4 uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
check-name: process replay check-name: process replay
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
allowed-conclusions: success,failure allowed-conclusions: success,failure
wait-interval: 20 wait-interval: 20
- name: Get tests run ID
if: steps.wait.outcome == 'success'
id: tests-run
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
run_id=$(gh api \
"repos/${{ github.repository }}/actions/workflows/tests.yaml/runs?event=pull_request&head_sha=${{ github.event.pull_request.head.sha }}&per_page=1" \
--jq '.workflow_runs[0].id // empty')
if [ -z "$run_id" ]; then
echo "No tests.yaml run found for ${{ github.event.pull_request.head.sha }}" >&2
exit 1
fi
echo "run-id=$run_id" >> "$GITHUB_OUTPUT"
- name: Download diff - name: Download diff
if: steps.wait.outcome == 'success' if: steps.wait.outcome == 'success'
uses: dawidd6/action-download-artifact@v6 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
workflow: tests.yaml repository: ${{ github.repository }}
workflow_conclusion: '' run-id: ${{ steps.tests-run.outputs.run-id }}
pr: ${{ github.event.number }}
name: diff_report_${{ github.event.number }} name: diff_report_${{ github.event.number }}
path: . path: .
allow_forks: true
- name: Comment on PR - name: Comment on PR
if: steps.wait.outcome == 'success' if: steps.wait.outcome == 'success'
uses: thollander/actions-comment-pull-request@v2 uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b
with: with:
filePath: diff_report.txt file-path: diff_report.txt
comment_tag: diff_report comment-tag: diff_report
pr_number: ${{ github.event.number }} pr-number: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -22,7 +22,7 @@ jobs:
steps: steps:
- uses: commaai/timeout@v1 - uses: commaai/timeout@v1
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with: with:
submodules: true submodules: true
@@ -34,7 +34,7 @@ jobs:
python scripts/docs.py build python scripts/docs.py build
# Push to docs.comma.ai # Push to docs.comma.ai
- uses: actions/checkout@v6 - uses: actions/checkout@v7
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot' if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
with: with:
path: openpilot-docs path: openpilot-docs
@@ -45,7 +45,7 @@ jobs:
run: | run: |
set -x set -x
source release/identity.sh source tools/release/identity.sh
cd openpilot-docs cd openpilot-docs
git checkout --orphan tmp git checkout --orphan tmp
+4 -4
View File
@@ -11,7 +11,7 @@ jobs:
contents: write contents: write
steps: steps:
- name: Delete stale Jenkins branches - name: Delete stale Jenkins branches
uses: actions/github-script@v8 uses: actions/github-script@v9
with: with:
script: | script: |
const cutoff = Date.now() - 24 * 60 * 60 * 1000; const cutoff = Date.now() - 24 * 60 * 60 * 1000;
@@ -52,7 +52,7 @@ jobs:
steps: steps:
- name: Check for trigger phrase - name: Check for trigger phrase
id: check_comment id: check_comment
uses: actions/github-script@v8 uses: actions/github-script@v9
with: with:
script: | script: |
const triggerPhrase = "trigger-jenkins"; const triggerPhrase = "trigger-jenkins";
@@ -72,7 +72,7 @@ jobs:
- name: Checkout repository - name: Checkout repository
if: steps.check_comment.outputs.result == 'true' if: steps.check_comment.outputs.result == 'true'
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
ref: refs/pull/${{ github.event.issue.number }}/head ref: refs/pull/${{ github.event.issue.number }}/head
@@ -86,7 +86,7 @@ jobs:
- name: Delete trigger comment - name: Delete trigger comment
if: steps.check_comment.outputs.result == 'true' && always() if: steps.check_comment.outputs.result == 'true' && always()
uses: actions/github-script@v8 uses: actions/github-script@v9
with: with:
script: | script: |
await github.rest.issues.deleteComment({ await github.rest.issues.deleteComment({
+4 -4
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
types: [opened, reopened, synchronize] types: [opened, reopened, synchronize]
paths: paths:
- 'selfdrive/modeld/models/*.onnx' - 'openpilot/selfdrive/modeld/models/*.onnx'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -16,11 +16,11 @@ jobs:
if: github.repository == 'commaai/openpilot' if: github.repository == 'commaai/openpilot'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
submodules: true submodules: true
- name: Checkout master - name: Checkout master
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
ref: master ref: master
path: base path: base
@@ -36,7 +36,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT
- name: Post model report comment - name: Post model report comment
uses: marocchino/sticky-pull-request-comment@baa7203ed60924babbe5dcd0ac8eae3b66ec5e16 uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0
with: with:
header: model-review header: model-review
message: ${{ steps.report.outputs.content }} message: ${{ steps.report.outputs.content }}
+3 -3
View File
@@ -15,17 +15,17 @@ jobs:
steps: steps:
- name: Wait for green check mark - name: Wait for green check mark
if: ${{ github.event_name == 'schedule' }} if: ${{ github.event_name == 'schedule' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
with: with:
ref: master 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 }}
check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$ check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$
- uses: actions/checkout@v4 - uses: actions/checkout@v4 # checkout version > v4 breaks nightly release script
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Push __nightly - name: Push __nightly
run: BRANCH=__nightly release/build_stripped.sh run: BRANCH=__nightly tools/release/build_stripped.sh
+48 -4
View File
@@ -3,6 +3,8 @@ name: repo maintenance
on: on:
schedule: schedule:
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST) - cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
pull_request:
types: [closed]
workflow_dispatch: workflow_dispatch:
env: env:
@@ -12,9 +14,11 @@ jobs:
package_updates: package_updates:
name: package_updates name: package_updates
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'sunnypilot/sunnypilot' if: >-
github.repository == 'sunnypilot/sunnypilot' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with: with:
submodules: true submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
@@ -50,10 +54,10 @@ jobs:
git add . git add .
- name: update car docs - name: update car docs
run: | run: |
python selfdrive/car/docs.py python openpilot/selfdrive/car/docs.py
git add docs/CARS.md git add docs/CARS.md
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1
with: with:
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com> author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }} token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
@@ -75,3 +79,43 @@ jobs:
${{ steps.pip_tree.outputs.PIP_TREE }} ${{ steps.pip_tree.outputs.PIP_TREE }}
``` ```
labels: bot labels: bot
cleanup_closed_branches:
if: github.repository == 'commaai/openpilot'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
const upstream = `${owner}/${repo}`;
for await (const response of github.paginate.iterator(github.rest.pulls.list, {
owner,
repo,
state: 'closed',
per_page: 100,
})) {
for (const pr of response.data) {
if (pr.head.repo?.full_name !== upstream) continue;
const branch = pr.head.ref;
try {
await github.rest.git.deleteRef({
owner,
repo,
ref: `heads/${branch}`,
});
console.log(`Deleted branch ${branch} (PR #${pr.number})`);
} catch (error) {
if (error.status === 422 || error.status === 403) {
console.log(`Skipping branch ${branch} (PR #${pr.number}): ${error.message}`);
continue;
}
throw error;
}
}
}
@@ -79,7 +79,7 @@ jobs:
is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')"; is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')";
echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT
stable_version=$(cat sunnypilot/common/version.h | grep SUNNYPILOT_VERSION | sed -e 's/[^0-9|.]//g'); stable_version=$(cat openpilot/sunnypilot/common/version.h | grep SUNNYPILOT_VERSION | sed -e 's/[^0-9|.]//g');
echo "version=$([ "$is_stable_branch" = "true" ] && echo "$stable_version" || echo "$BUILD")" >> $GITHUB_OUTPUT echo "version=$([ "$is_stable_branch" = "true" ] && echo "$stable_version" || echo "$BUILD")" >> $GITHUB_OUTPUT
echo "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT echo "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT
fi fi
@@ -177,7 +177,7 @@ jobs:
- name: Build Main Project - name: Build Main Project
run: | run: |
export PYTHONPATH="$BUILD_DIR" export PYTHONPATH="$BUILD_DIR"
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/ ./tools/release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
cd $BUILD_DIR cd $BUILD_DIR
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
echo "Building sunnypilot's modeld_v2..." echo "Building sunnypilot's modeld_v2..."
@@ -208,17 +208,17 @@ jobs:
--exclude='Jenkinsfile' \ --exclude='Jenkinsfile' \
--exclude='**/release/' \ --exclude='**/release/' \
--exclude='**/.github/' \ --exclude='**/.github/' \
--exclude='**/selfdrive/ui/replay/' \ --exclude='**/openpilot/selfdrive/ui/replay/' \
--exclude='**/__pycache__/' \ --exclude='**/__pycache__/' \
--exclude='${{env.SCONS_CACHE_DIR}}' \ --exclude='${{env.SCONS_CACHE_DIR}}' \
--exclude='**/.git/' \ --exclude='**/.git/' \
--exclude='**/SConstruct' \ --exclude='**/SConstruct' \
--exclude='**/SConscript' \ --exclude='**/SConscript' \
--exclude='**/.venv/' \ --exclude='**/.venv/' \
--exclude='selfdrive/modeld/models/*.onnx*' \ --exclude='openpilot/selfdrive/modeld/models/*.onnx*' \
--exclude='sunnypilot/modeld*/models/*.onnx*' \ --exclude='openpilot/sunnypilot/modeld*/models/*.onnx*' \
--exclude='third_party/*x86*' \ --exclude='openpilot/third_party/*x86*' \
--exclude='third_party/*Darwin*' \ --exclude='openpilot/third_party/*Darwin*' \
--delete-excluded \ --delete-excluded \
--chown=comma:comma \ --chown=comma:comma \
${BUILD_DIR}/ ${OUTPUT_DIR}/ ${BUILD_DIR}/ ${OUTPUT_DIR}/
+42 -45
View File
@@ -35,27 +35,27 @@ jobs:
STRIPPED_DIR: /tmp/releasepilot STRIPPED_DIR: /tmp/releasepilot
PYTHONPATH: /tmp/releasepilot PYTHONPATH: /tmp/releasepilot
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with: with:
submodules: true submodules: true
- name: Getting LFS files - name: Getting LFS files
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60
with: with:
timeout_minutes: 2 timeout_minutes: 2
max_attempts: 3 max_attempts: 3
command: git lfs pull command: git lfs pull
- name: Build devel - name: Build devel
timeout-minutes: 1 timeout-minutes: 3
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh run: TARGET_DIR=$STRIPPED_DIR tools/release/build_stripped.sh
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Build openpilot and run checks - name: Build openpilot and run checks
timeout-minutes: 30 timeout-minutes: 30
working-directory: ${{ env.STRIPPED_DIR }} working-directory: ${{ env.STRIPPED_DIR }}
run: python3 system/manager/build.py run: python3 openpilot/system/manager/build.py
- name: Run tests - name: Run tests
timeout-minutes: 1 timeout-minutes: 1
working-directory: ${{ env.STRIPPED_DIR }} working-directory: ${{ env.STRIPPED_DIR }}
run: release/check-dirty.sh run: tools/release/check-dirty.sh
- name: Check submodules - name: Check submodules
if: github.repository == 'sunnypilot/sunnypilot' if: github.repository == 'sunnypilot/sunnypilot'
timeout-minutes: 3 timeout-minutes: 3
@@ -75,15 +75,13 @@ jobs:
export CHECK_PR_REFS=true export CHECK_PR_REFS=true
fi fi
fi fi
release/check-submodules.sh tools/release/check-submodules.sh
build_mac: build_mac:
name: build macOS name: build macOS
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }} runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with:
submodules: true
- name: Remove Homebrew from environment - name: Remove Homebrew from environment
run: | run: |
FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':') FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':')
@@ -101,9 +99,7 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Static analysis - name: Static analysis
timeout-minutes: 1 timeout-minutes: 1
@@ -118,16 +114,18 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Install raylib headless dependencies
if: ${{ !contains(runner.name, 'nsc') }}
run: sudo apt-get install -y --no-install-recommends libgles2 libegl1
- name: Build openpilot - name: Build openpilot
run: scons run: scons
- name: Run unit tests - name: Run unit tests
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 999 }} timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 999 }}
env:
RAYLIB_BACKEND: headless
run: | run: |
source selfdrive/test/setup_xvfb.sh
# Pre-compile Python bytecode so each pytest worker doesn't need to # Pre-compile Python bytecode so each pytest worker doesn't need to
$PYTEST --collect-only -m 'not slow' -qq $PYTEST --collect-only -m 'not slow' -qq
MAX_EXAMPLES=1 $PYTEST -m 'not slow' MAX_EXAMPLES=1 $PYTEST -m 'not slow'
@@ -142,39 +140,37 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Build openpilot - name: Build openpilot
run: scons run: scons
- name: Run replay - name: Run replay
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }} timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
continue-on-error: ${{ github.ref == 'refs/heads/master' }} continue-on-error: ${{ github.ref == 'refs/heads/master' }}
run: selfdrive/test/process_replay/test_processes.py -j$(nproc) run: openpilot/selfdrive/test/process_replay/test_processes.py -j$(nproc)
- name: Print diff - name: Print diff
id: print-diff id: print-diff
if: always() if: always()
run: cat selfdrive/test/process_replay/diff.txt run: cat openpilot/selfdrive/test/process_replay/diff.txt
- name: Print diff report - name: Print diff report
if: always() if: always()
run: cat selfdrive/test/process_replay/diff_report.txt run: cat openpilot/selfdrive/test/process_replay/diff_report.txt
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v7
if: always() if: always()
continue-on-error: true continue-on-error: true
with: with:
name: process_replay_diff.txt name: process_replay_diff.txt
path: selfdrive/test/process_replay/diff.txt path: openpilot/selfdrive/test/process_replay/diff.txt
- name: Upload diff report - name: Upload diff report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
if: always() && github.event_name == 'pull_request' if: always() && github.event_name == 'pull_request'
continue-on-error: true continue-on-error: true
with: with:
name: diff_report_${{ github.event.number }} name: diff_report_${{ github.event.number }}
path: selfdrive/test/process_replay/diff_report.txt path: openpilot/selfdrive/test/process_replay/diff_report.txt
- name: Checkout ci-artifacts - name: Checkout ci-artifacts
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master' if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
uses: actions/checkout@v4 uses: actions/checkout@v7
with: with:
repository: commaai/ci-artifacts repository: commaai/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
@@ -187,13 +183,13 @@ jobs:
git config user.email "<>" git config user.email "<>"
git fetch origin process-replay || true git fetch origin process-replay || true
git checkout process-replay 2>/dev/null || git checkout --orphan process-replay git checkout process-replay 2>/dev/null || git checkout --orphan process-replay
cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst . cp ${{ github.workspace }}/openpilot/selfdrive/test/process_replay/fakedata/*.zst .
echo "${{ github.sha }}" > ref_commit echo "${{ github.sha }}" > ref_commit
git add . git add .
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit" git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit"
- name: Push refs - name: Push refs
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master' if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60
with: with:
timeout_minutes: 2 timeout_minutes: 2
max_attempts: 3 max_attempts: 3
@@ -203,7 +199,7 @@ jobs:
timeout-minutes: 4 timeout-minutes: 4
env: env:
ONNXCPU: 1 ONNXCPU: 1
run: $PYTEST selfdrive/test/process_replay/test_regen.py run: $PYTEST openpilot/selfdrive/test/process_replay/test_regen.py
simulator_driving: simulator_driving:
name: simulator driving name: simulator driving
@@ -215,17 +211,16 @@ jobs:
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
if: false # FIXME: Started to timeout recently if: false # FIXME: Started to timeout recently
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Build openpilot - name: Build openpilot
run: scons run: scons
- name: Driving test - name: Driving test
timeout-minutes: 2 timeout-minutes: 2
run: | env:
source selfdrive/test/setup_xvfb.sh # MetaDrive renders offscreen through panda3d's EGL pipe on llvmpipe
pytest -s tools/sim/tests/test_metadrive_bridge.py EGL_PLATFORM: surfaceless
run: pytest -s openpilot/tools/sim/tests/test_metadrive_bridge.py
create_ui_report: create_ui_report:
name: Create UI Report name: Create UI Report
@@ -236,19 +231,21 @@ jobs:
&& fromJSON('["namespace-profile-amd64-8x16"]') && fromJSON('["namespace-profile-amd64-8x16"]')
|| fromJSON('["ubuntu-24.04"]') }} || fromJSON('["ubuntu-24.04"]') }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with:
submodules: true
- run: ./tools/op.sh setup - run: ./tools/op.sh setup
- name: Install raylib headless dependencies
if: ${{ !contains(runner.name, 'nsc') }}
run: sudo apt-get install -y --no-install-recommends libgles2 libegl1
- name: Build openpilot - name: Build openpilot
run: scons run: scons
- name: Create UI Report - name: Create UI Report
env:
RAYLIB_BACKEND: headless
run: | run: |
source selfdrive/test/setup_xvfb.sh python3 openpilot/selfdrive/ui/tests/diff/replay.py
python3 selfdrive/ui/tests/diff/replay.py python3 openpilot/selfdrive/ui/tests/diff/replay.py --big
python3 selfdrive/ui/tests/diff/replay.py --big
- name: Upload UI Report - name: Upload UI Report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
path: selfdrive/ui/tests/diff/report path: openpilot/selfdrive/ui/tests/diff/report
+17 -17
View File
@@ -8,9 +8,9 @@ on:
branches: branches:
- 'master' - 'master'
paths: paths:
- 'selfdrive/assets/**' - 'openpilot/selfdrive/assets/**'
- 'selfdrive/ui/**' - 'openpilot/selfdrive/ui/**'
- 'system/ui/**' - 'openpilot/system/ui/**'
workflow_dispatch: workflow_dispatch:
env: env:
@@ -34,12 +34,12 @@ jobs:
pull-requests: write pull-requests: write
actions: read actions: read
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
with: with:
submodules: true submodules: true
- name: Waiting for ui generation to end - name: Waiting for ui generation to end
uses: lewagon/wait-on-check-action@v1.3.4 uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
with: with:
ref: ${{ env.SHA }} ref: ${{ env.SHA }}
check-name: ${{ env.UI_JOB_NAME }} check-name: ${{ env.UI_JOB_NAME }}
@@ -53,7 +53,7 @@ jobs:
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
- name: Getting proposed ui - name: Getting proposed ui
uses: dawidd6/action-download-artifact@v6 uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ steps.get_run_id.outputs.run_id }} run_id: ${{ steps.get_run_id.outputs.run_id }}
@@ -62,7 +62,7 @@ jobs:
path: ${{ github.workspace }}/pr_ui path: ${{ github.workspace }}/pr_ui
- name: Getting mici master ui - name: Getting mici master ui
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
repository: sunnypilot/ci-artifacts repository: sunnypilot/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
@@ -70,7 +70,7 @@ jobs:
ref: openpilot_master_ui_mici_raylib ref: openpilot_master_ui_mici_raylib
- name: Getting big master ui - name: Getting big master ui
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
repository: sunnypilot/ci-artifacts repository: sunnypilot/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
@@ -97,7 +97,7 @@ jobs:
done done
- name: Setup FFmpeg - name: Setup FFmpeg
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae uses: AnimMouse/setup-ffmpeg@178e0b4a408f06ce40d896c3cd79f50fa6f8b0c3
- name: Finding diffs - name: Finding diffs
if: github.event_name == 'pull_request_target' if: github.event_name == 'pull_request_target'
@@ -115,13 +115,13 @@ jobs:
cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4" cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4"
diff_exit_code=0 diff_exit_code=0
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py \ python3 ${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/diff.py \
"${{ github.workspace }}/pr_ui/${video}_master.mp4" \ "${{ github.workspace }}/pr_ui/${video}_master.mp4" \
"${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \ "${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \
"${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$? "${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/" cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/"
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/" cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/"
REPORT_URL="https://sunnypilot.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html" REPORT_URL="https://sunnypilot.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html"
if [ $diff_exit_code -eq 0 ]; then if [ $diff_exit_code -eq 0 ]; then
@@ -156,7 +156,7 @@ jobs:
for variant in $VARIANTS; do for variant in $VARIANTS; do
IFS=':' read -r name _ _ <<< "$variant" IFS=':' read -r name _ _ <<< "$variant"
diff_name="${name}_diff" diff_name="${name}_diff"
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html" cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html"
git add "${diff_name}_pr_${{ github.event.number }}.html" git add "${diff_name}_pr_${{ github.event.number }}.html"
done done
git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit" git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit"
@@ -164,12 +164,12 @@ jobs:
- name: Comment on PR - name: Comment on PR
if: github.event_name == 'pull_request_target' if: github.event_name == 'pull_request_target'
uses: thollander/actions-comment-pull-request@v2 uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b
with: with:
message: | message: |
<!-- _(run_id_ui_preview **${{ github.run_id }}**)_ --> <!-- _(run_id_ui_preview **${{ github.run_id }}**)_ -->
## UI Preview ## UI Preview
${{ steps.find_diff.outputs.COMMENT }} ${{ steps.find_diff.outputs.COMMENT }}
comment_tag: run_id_ui_preview comment-tag: run_id_ui_preview
pr_number: ${{ github.event.number }} pr-number: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
+16 -9
View File
@@ -15,6 +15,13 @@ a.out
.cache/ .cache/
bin/ bin/
# created at launch for TICI PYTHONPATH (PC uses editable installs via pyproject.toml)
/msgq
/opendbc
/rednose
/teleoprtc
/tinygrad
*.mp4 *.mp4
*.dylib *.dylib
*.DSYM *.DSYM
@@ -44,18 +51,18 @@ bin/
config.json config.json
compile_commands.json compile_commands.json
compare_runtime*.html compare_runtime*.html
selfdrive/modeld/models/tg_input_devices.json openpilot/selfdrive/modeld/models/tg_input_devices.json
# build artifacts # build artifacts
docs_site/ docs_site/
selfdrive/pandad/pandad openpilot/selfdrive/pandad/pandad
cereal/services.h openpilot/cereal/services.h
cereal/gen openpilot/cereal/gen
cereal/messaging/bridge openpilot/cereal/messaging/bridge
selfdrive/ui/translations/tmp openpilot/selfdrive/ui/translations/tmp
selfdrive/car/tests/cars_dump openpilot/selfdrive/car/tests/cars_dump
system/camerad/camerad openpilot/system/camerad/camerad
system/camerad/test/ae_gray_test openpilot/system/camerad/test/ae_gray_test
.coverage* .coverage*
coverage.xml coverage.xml
+2 -2
View File
@@ -6,7 +6,7 @@
url = https://github.com/sunnypilot/opendbc.git url = https://github.com/sunnypilot/opendbc.git
[submodule "msgq"] [submodule "msgq"]
path = msgq_repo path = msgq_repo
url = https://github.com/commaai/msgq.git url = https://github.com/sunnypilot/msgq.git
[submodule "rednose_repo"] [submodule "rednose_repo"]
path = rednose_repo path = rednose_repo
url = https://github.com/commaai/rednose.git url = https://github.com/commaai/rednose.git
@@ -17,5 +17,5 @@
path = tinygrad_repo path = tinygrad_repo
url = https://github.com/sunnypilot/tinygrad.git url = https://github.com/sunnypilot/tinygrad.git
[submodule "sunnypilot/neural_network_data"] [submodule "sunnypilot/neural_network_data"]
path = sunnypilot/neural_network_data path = openpilot/sunnypilot/neural_network_data
url = https://github.com/sunnypilot/neural-network-data.git url = https://github.com/sunnypilot/neural-network-data.git
+4 -4
View File
@@ -6,9 +6,9 @@
"type": "pickString", "type": "pickString",
"description": "Select the process to debug", "description": "Select the process to debug",
"options": [ "options": [
"selfdrive/controls/controlsd.py", "openpilot/selfdrive/controls/controlsd.py",
"system/timed/timed.py", "openpilot/system/timed/timed.py",
"tools/sim/run_bridge.py" "openpilot/tools/sim/run_bridge.py"
] ]
}, },
{ {
@@ -16,7 +16,7 @@
"type": "pickString", "type": "pickString",
"description": "Select the process to debug", "description": "Select the process to debug",
"options": [ "options": [
"selfdrive/ui/ui" "openpilot/selfdrive/ui/ui"
] ]
}, },
{ {
-5
View File
@@ -17,10 +17,5 @@
"**/.git", "**/.git",
"**/.venv", "**/.venv",
"**/__pycache__", "**/__pycache__",
// exclude directories that should be using the symlinked version
"common/**",
"selfdrive/**",
"system/**",
"tools/**",
] ]
} }
-38
View File
@@ -1,38 +0,0 @@
FROM ubuntu:24.04
ENV PYTHONUNBUFFERED=1
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends sudo tzdata locales && \
rm -rf /var/lib/apt/lists/*
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
ARG USER=batman
ARG USER_UID=1001
RUN useradd -m -s /bin/bash -u $USER_UID $USER
RUN usermod -aG sudo $USER
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER $USER
ENV OPENPILOT_PATH=/home/$USER/openpilot
RUN mkdir -p ${OPENPILOT_PATH}
WORKDIR ${OPENPILOT_PATH}
COPY --chown=$USER . ${OPENPILOT_PATH}/
ENV UV_BIN="/home/$USER/.local/bin/"
ENV VIRTUAL_ENV=${OPENPILOT_PATH}/.venv
ENV PATH="$UV_BIN:$VIRTUAL_ENV/bin:$PATH"
RUN tools/setup_dependencies.sh && \
sudo rm -rf /var/lib/apt/lists/*
USER root
RUN git config --global --add safe.directory '*'
Vendored
+28 -26
View File
@@ -12,7 +12,7 @@ def retryWithDelay(int maxRetries, int delay, Closure body) {
def device(String ip, String step_label, String cmd) { def device(String ip, String step_label, String cmd) {
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) { withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
def ssh_cmd = """ def ssh_cmd = """
ssh -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=2 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' exec /usr/bin/bash <<'END' ssh -o ControlMaster=auto -o ControlPath=/tmp/ssh_control_%C -o ControlPersist=yes -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=2 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' exec /usr/bin/bash <<'END'
set -e set -e
@@ -63,6 +63,8 @@ if [ -f /data/openpilot/launch_env.sh ]; then
source /data/openpilot/launch_env.sh source /data/openpilot/launch_env.sh
fi fi
export LD_LIBRARY_PATH="\$(python -c 'import ffmpeg; print(ffmpeg.LIB_DIR)'):/usr/local/lib:\${LD_LIBRARY_PATH:-}"
ln -snf ${env.TEST_DIR} /data/pythonpath ln -snf ${env.TEST_DIR} /data/pythonpath
cd ${env.TEST_DIR} || true cd ${env.TEST_DIR} || true
@@ -93,7 +95,7 @@ def deviceStage(String stageName, String deviceType, List extra_env, def steps)
retry (3) { retry (3) {
def date = sh(script: 'date', returnStdout: true).trim(); def date = sh(script: 'date', returnStdout: true).trim();
device(device_ip, "set time", "date -s '" + date + "'") device(device_ip, "set time", "date -s '" + date + "'")
device(device_ip, "git checkout", extra + "\n" + readFile("selfdrive/test/setup_device_ci.sh")) device(device_ip, "git checkout", extra + "\n" + readFile("openpilot/selfdrive/test/setup_device_ci.sh"))
} }
steps.each { item -> steps.each { item ->
def name = item[0] def name = item[0]
@@ -179,7 +181,7 @@ node {
try { try {
if (env.BRANCH_NAME == 'devel-staging') { if (env.BRANCH_NAME == 'devel-staging') {
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [ deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging,release-mici-staging $SOURCE_DIR/release/build_release.sh"), step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging,release-mici-staging $SOURCE_DIR/tools/release/build_release.sh"),
]) ])
} }
@@ -187,12 +189,12 @@ node {
parallel ( parallel (
'nightly': { 'nightly': {
deviceStage("build nightly", "tizi-needs-can", [], [ deviceStage("build nightly", "tizi-needs-can", [], [
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"), step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/tools/release/build_release.sh"),
]) ])
}, },
'nightly-dev': { 'nightly-dev': {
deviceStage("build nightly-dev", "tizi-needs-can", [], [ deviceStage("build nightly-dev", "tizi-needs-can", [], [
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/release/build_release.sh"), step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/tools/release/build_release.sh"),
]) ])
}, },
) )
@@ -202,51 +204,51 @@ node {
parallel ( parallel (
'onroad tests': { 'onroad tests': {
deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [ deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [
step("build openpilot", "cd system/manager && ./build.py"), step("build openpilot", "cd openpilot/system/manager && ./build.py"),
step("check dirty", "release/check-dirty.sh"), step("check dirty", "tools/release/check-dirty.sh"),
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]), step("onroad tests", "pytest openpilot/selfdrive/test/test_onroad.py -s", [timeout: 60]),
]) ])
}, },
'HW + Unit Tests': { 'HW + Unit Tests': {
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [ deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
step("build", "cd system/manager && ./build.py"), step("build", "cd openpilot/system/manager && ./build.py"),
step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"), step("test power draw", "pytest -s openpilot/selfdrive/test//test_power_draw.py"),
step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]), step("test encoder", "pytest openpilot/system/loggerd/tests/test_encoder.py", [diffPaths: ["openpilot/system/loggerd/"]]),
step("test manager", "pytest system/manager/test/test_manager.py"), step("test manager", "pytest openpilot/system/manager/test/test_manager.py"),
]) ])
}, },
'camerad OX03C10': { 'camerad OX03C10': {
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [ deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
step("build", "cd system/manager && ./build.py"), step("build", "cd openpilot/system/manager && ./build.py"),
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py"), step("test pandad", "pytest openpilot/selfdrive/pandad/tests/test_pandad.py"),
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]), step("test camerad", "pytest openpilot/system/camerad/test/test_camerad.py", [timeout: 90]),
]) ])
}, },
'camerad OS04C10': { 'camerad OS04C10': {
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [ deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
step("build", "cd system/manager && ./build.py"), step("build", "cd openpilot/system/manager && ./build.py"),
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py"), step("test pandad", "pytest openpilot/selfdrive/pandad/tests/test_pandad.py"),
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]), step("test camerad", "pytest openpilot/system/camerad/test/test_camerad.py", [timeout: 90]),
]) ])
}, },
'sensord': { 'sensord': {
deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [ deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [
step("build", "cd system/manager && ./build.py"), step("build", "cd openpilot/system/manager && ./build.py"),
step("test sensord", "pytest system/sensord/tests/test_sensord.py"), step("test sensord", "pytest openpilot/system/sensord/tests/test_sensord.py"),
]) ])
}, },
'replay': { 'replay': {
deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [ deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [
step("build", "cd system/manager && ./build.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]), step("build", "cd openpilot/system/manager && ./build.py", [diffPaths: ["openpilot/selfdrive/modeld/", "tinygrad_repo", "openpilot/selfdrive/test/process_replay/model_replay.py"]]),
step("model replay", "selfdrive/test/process_replay/model_replay.py", [diffPaths: ["selfdrive/modeld/", "tinygrad_repo", "selfdrive/test/process_replay/model_replay.py"]]), step("model replay", "openpilot/selfdrive/test/process_replay/model_replay.py", [diffPaths: ["openpilot/selfdrive/modeld/", "tinygrad_repo", "openpilot/selfdrive/test/process_replay/model_replay.py"]]),
]) ])
}, },
'tizi': { 'tizi': {
deviceStage("tizi", "tizi", ["UNSAFE=1"], [ deviceStage("tizi", "tizi", ["UNSAFE=1"], [
step("build openpilot", "cd system/manager && ./build.py"), step("build openpilot", "cd openpilot/system/manager && ./build.py"),
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"), step("test pandad loopback", "pytest openpilot/selfdrive/pandad/tests/test_pandad_loopback.py"),
step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"), step("test pandad spi", "pytest openpilot/selfdrive/pandad/tests/test_pandad_spi.py"),
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"), step("test amp", "pytest openpilot/common/hardware/tici/tests/test_amplifier.py"),
]) ])
}, },
+1 -1
View File
@@ -1104,7 +1104,7 @@ Version 0.2.1 (2016-12-14)
Version 0.2 (2016-12-12) Version 0.2 (2016-12-12)
========================= =========================
* Car/Radar abstraction layers have shipped, see cereal/car.capnp * Car/Radar abstraction layers have shipped, see openpilot/cereal/car.capnp
* controlsd has been refactored * controlsd has been refactored
* Shipped plant model and testing maneuvers * Shipped plant model and testing maneuvers
* visiond exits more gracefully now * visiond exits more gracefully now
+70 -25
View File
@@ -27,6 +27,21 @@ AddOption('--minimal',
default=(not TICI and not release), default=(not TICI and not release),
help='the minimum build to run openpilot. no tests, tools, etc.') help='the minimum build to run openpilot. no tests, tools, etc.')
submodule_python_paths = [
Dir("#").abspath,
Dir("#msgq_repo").abspath,
Dir("#opendbc_repo").abspath,
Dir("#rednose_repo").abspath,
Dir("#teleoprtc_repo").abspath,
Dir("#tinygrad_repo").abspath,
]
for p in reversed(submodule_python_paths):
if p not in sys.path:
sys.path.insert(0, p)
if external_pythonpath := os.environ.get("PYTHONPATH"):
submodule_python_paths += [p for p in external_pythonpath.split(os.pathsep) if p and p not in submodule_python_paths]
# Detect platform # Detect platform
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
if platform.system() == "Darwin": if platform.system() == "Darwin":
@@ -40,9 +55,24 @@ assert arch in [
"Darwin", # macOS arm64 (x86 not supported) "Darwin", # macOS arm64 (x86 not supported)
] ]
pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'eigen', 'ffmpeg', 'json11', 'libjpeg', 'libyuv', 'ncurses', 'zeromq', 'zstd'] pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'eigen', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd']
pkgs = [importlib.import_module(name) for name in pkg_names] pkgs = [importlib.import_module(name) for name in pkg_names]
acados = pkgs[pkg_names.index('acados')] acados = pkgs[pkg_names.index('acados')]
ffmpeg = pkgs[pkg_names.index('ffmpeg')]
# Shared package ships .so/.dylib; older device venvs still have static .a only.
# Keep static link deps (x264/z/va/drm) when the installed package is static so
# TICI CI works without upgrading the device venv yet.
# TODO: drop the static fallback once device venvs have comma-deps-ffmpeg>=7.1.0.post94
_ffmpeg_lib_names = os.listdir(ffmpeg.LIB_DIR) if os.path.isdir(ffmpeg.LIB_DIR) else []
ffmpeg_shared = any(
n.startswith('libavcodec.so') or (n.startswith('libavcodec') and n.endswith('.dylib'))
for n in _ffmpeg_lib_names
)
ffmpeg_libs = ['avformat', 'avcodec', 'swresample', 'avutil']
if not ffmpeg_shared:
ffmpeg_libs += ['x264', 'z']
if arch != "Darwin":
ffmpeg_libs += ['va', 'va-drm', 'drm']
acados_include_dirs = [ acados_include_dirs = [
acados.INCLUDE_DIR, acados.INCLUDE_DIR,
os.path.join(acados.INCLUDE_DIR, "blasfeo", "include"), os.path.join(acados.INCLUDE_DIR, "blasfeo", "include"),
@@ -91,7 +121,7 @@ def _libflags(target, source, env, for_signature):
env = Environment( env = Environment(
ENV={ ENV={
"PATH": os.environ['PATH'], "PATH": os.environ['PATH'],
"PYTHONPATH": Dir("#").abspath, "PYTHONPATH": os.pathsep.join(submodule_python_paths),
"ACADOS_SOURCE_DIR": acados.DIR, "ACADOS_SOURCE_DIR": acados.DIR,
"ACADOS_PYTHON_INTERFACE_PATH": acados.TEMPLATE_DIR, "ACADOS_PYTHON_INTERFACE_PATH": acados.TEMPLATE_DIR,
"TERA_PATH": acados.TERA_PATH "TERA_PATH": acados.TERA_PATH
@@ -112,22 +142,27 @@ env = Environment(
CFLAGS=["-std=gnu11"], CFLAGS=["-std=gnu11"],
CXXFLAGS=["-std=c++1z"], CXXFLAGS=["-std=c++1z"],
CPPPATH=[ CPPPATH=[
"#", "#openpilot",
"#msgq", "#msgq_repo", # #include "msgq/..."
"#opendbc_repo", # #include "opendbc/..."
"#rednose_repo", # #include "rednose/..."
"#rednose_repo/rednose", # #include "logger/..." (rednose package root)
"#openpilot/cereal/gen/cpp",
acados_include_dirs, acados_include_dirs,
[x.INCLUDE_DIR for x in pkgs], [x.INCLUDE_DIR for x in pkgs],
"#",
], ],
LIBPATH=[ LIBPATH=[
"#common", "#openpilot/common",
"#msgq_repo", "#msgq_repo",
"#selfdrive/pandad", "#openpilot/selfdrive/pandad",
"#rednose/helpers", "#rednose_repo/rednose/helpers",
[x.LIB_DIR for x in pkgs], [x.LIB_DIR for x in pkgs],
], ],
RPATH=[], RPATH=[ffmpeg.LIB_DIR] if ffmpeg_shared else [],
CYTHONCFILESUFFIX=".cpp", CYTHONCFILESUFFIX=".cpp",
COMPILATIONDB_USE_ABSPATH=True, COMPILATIONDB_USE_ABSPATH=True,
REDNOSE_ROOT="#", REDNOSE_ROOT="#rednose_repo",
tools=["default", "cython", "compilation_db", "rednose_filter"], tools=["default", "cython", "compilation_db", "rednose_filter"],
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"], toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
) )
@@ -190,7 +225,7 @@ else:
np_version = SCons.Script.Value(np.__version__) np_version = SCons.Script.Value(np.__version__)
Export('envCython', 'np_version') Export('envCython', 'np_version')
Export('env', 'arch', 'acados', 'release') Export('env', 'arch', 'acados', 'release', 'ffmpeg_libs')
# Setup cache dir # Setup cache dir
default_cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache' default_cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
@@ -211,7 +246,7 @@ def prune_cache_dir(target=None, source=None, env=None):
# ********** start building stuff ********** # ********** start building stuff **********
# Build common module # Build common module
SConscript(['common/SConscript']) SConscript(['openpilot/common/SConscript'])
Import('_common') Import('_common')
common = [_common, 'json11', 'zmq'] common = [_common, 'json11', 'zmq']
Export('common') Export('common')
@@ -222,7 +257,7 @@ env_swaglog = env.Clone()
env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""') env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""')
SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog}) SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog})
SConscript(['cereal/SConscript']) SConscript(['openpilot/cereal/SConscript'])
Import('socketmaster', 'msgq') Import('socketmaster', 'msgq')
messaging = [socketmaster, msgq, 'capnp', 'kj',] messaging = [socketmaster, msgq, 'capnp', 'kj',]
@@ -233,34 +268,33 @@ Export('messaging')
SConscript(['panda/SConscript']) SConscript(['panda/SConscript'])
# Build rednose library # Build rednose library
SConscript(['rednose/SConscript']) SConscript(['rednose_repo/rednose/SConscript'])
# Build system services # Build system services
SConscript([ SConscript([
'system/loggerd/SConscript', 'openpilot/system/loggerd/SConscript',
]) ])
if arch == "larch64": if arch == "larch64":
SConscript(['system/camerad/SConscript']) SConscript(['openpilot/system/camerad/SConscript'])
# Build selfdrive # Build selfdrive
SConscript([ SConscript([
'selfdrive/pandad/SConscript', 'openpilot/selfdrive/pandad/SConscript',
'selfdrive/controls/lib/lateral_mpc_lib/SConscript', 'openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript',
'selfdrive/controls/lib/longitudinal_mpc_lib/SConscript', 'openpilot/selfdrive/locationd/SConscript',
'selfdrive/locationd/SConscript', 'openpilot/selfdrive/modeld/SConscript',
'selfdrive/modeld/SConscript', 'openpilot/selfdrive/ui/SConscript',
'selfdrive/ui/SConscript',
]) ])
SConscript(['sunnypilot/SConscript']) SConscript(['openpilot/sunnypilot/SConscript'])
# Build desktop-only tools # Build desktop-only tools
if GetOption('extras') and arch != "larch64": if GetOption('extras') and arch != "larch64":
SConscript([ SConscript([
'tools/replay/SConscript', 'openpilot/tools/replay/SConscript',
'tools/cabana/SConscript', 'openpilot/tools/cabana/SConscript',
'tools/jotpluggler/SConscript', 'openpilot/tools/jotpluggler/SConscript',
]) ])
@@ -276,6 +310,8 @@ def count_scons_nodes(nodes):
if node in seen: if node in seen:
continue continue
seen.add(node) seen.add(node)
if hasattr(node, 'has_builder') and node.has_builder():
build_product_nodes.add(node)
executor = node.get_executor() executor = node.get_executor()
if executor is not None: if executor is not None:
stack += executor.get_all_prerequisites() + executor.get_all_children() stack += executor.get_all_prerequisites() + executor.get_all_children()
@@ -284,6 +320,7 @@ def count_scons_nodes(nodes):
progress_interval = 5 progress_interval = 5
progress_count = 0 progress_count = 0
build_product_nodes = set()
progress_total = max(1, count_scons_nodes(env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry))) progress_total = max(1, count_scons_nodes(env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry)))
def progress_function(node): def progress_function(node):
@@ -299,3 +336,11 @@ def progress_function(node):
Progress(progress_function, interval=progress_interval) Progress(progress_function, interval=progress_interval)
AddPostAction(BUILD_TARGETS or [Dir('.')], prune_cache_dir) AddPostAction(BUILD_TARGETS or [Dir('.')], prune_cache_dir)
def check_build_product_size(target, source, env):
limit = 50 * 1024 * 1024 # GitHub max size
for t in target:
if hasattr(t, 'isfile') and t.isfile() and (size := os.path.getsize(t.abspath)) > limit:
raise SCons.Errors.UserError(f"{t} is {size / (1024 * 1024):.1f} MiB, exceeding the {limit / (1024 * 1024):.1f} MiB limit")
if not GetOption('extras'):
AddPostAction(list(build_product_nodes), Action(check_build_product_size, None))
-20
View File
@@ -1,20 +0,0 @@
Import('env', 'common', 'msgq')
cereal_dir = Dir('.')
gen_dir = Dir('gen')
# Build cereal
schema_files = ['log.capnp', 'car.capnp', 'deprecated.capnp', 'custom.capnp']
env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files],
schema_files,
f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/")
cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files])
# Build messaging
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc', 'messaging/bridge_zmq.cc'], LIBS=[msgq, common, 'pthread'])
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
Export('cereal', 'socketmaster')
-11
View File
@@ -1,11 +0,0 @@
import os
import capnp
from importlib.resources import as_file, files
capnp.remove_import_hook()
with as_file(files("cereal")) as fspath:
CEREAL_PATH = fspath.as_posix()
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp"))
-1
View File
@@ -1 +0,0 @@
../opendbc_repo/opendbc/car/car.capnp
-27
View File
@@ -1,27 +0,0 @@
#include "catch2/catch.hpp"
#define private public
#include "common/params.h"
#include "common/util.h"
TEST_CASE("params_nonblocking_put") {
char tmp_path[] = "/tmp/asyncWriter_XXXXXX";
const std::string param_path = mkdtemp(tmp_path);
auto param_names = {"CarParams", "IsMetric"};
{
Params params(param_path);
for (const auto &name : param_names) {
params.putNonBlocking(name, "1");
// param is empty
REQUIRE(params.get(name).empty());
}
// check if thread is running
REQUIRE(params.future.valid());
REQUIRE(params.future.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout);
}
// check results
Params p(param_path);
for (const auto &name : param_names) {
REQUIRE(p.get(name) == "1");
}
}
+13 -7
View File
@@ -5,21 +5,27 @@ import pytest
from openpilot.common.prefix import OpenpilotPrefix from openpilot.common.prefix import OpenpilotPrefix
from openpilot.system.manager import manager from openpilot.system.manager import manager
from openpilot.system.hardware import TICI, HARDWARE from openpilot.common.hardware import TICI, HARDWARE
# these are heavy CI-only tests, invoked explicitly in .github/workflows/tests.yaml # these are heavy CI-only tests, invoked explicitly in .github/workflows/tests.yaml
collect_ignore = [ collect_ignore = [
"selfdrive/test/process_replay/test_processes.py", "openpilot/selfdrive/test/process_replay/test_processes.py",
"selfdrive/test/process_replay/test_regen.py", "openpilot/selfdrive/test/process_replay/test_regen.py",
"openpilot/tools/sim/",
# tinygrad JIT has process-global state. Other test files import modeld → tinygrad, # tinygrad JIT has process-global state. Other test files import modeld → tinygrad,
# which corrupts JIT captures for test_warp.py in the same process. Run separately in CI. # which corrupts JIT captures for test_warp.py in the same process. Run separately in CI.
"sunnypilot/modeld_v2/tests/test_warp.py", "openpilot/sunnypilot/modeld_v2/tests/test_warp.py",
]
collect_ignore_glob = [
"selfdrive/debug/*.py",
] ]
def pytest_sessionstart(session):
# TODO: fix tests and enable test order randomization
if session.config.pluginmanager.hasplugin('randomly'):
session.config.option.randomly_reorganize = False
@pytest.hookimpl(hookwrapper=True, trylast=True) @pytest.hookimpl(hookwrapper=True, trylast=True)
def pytest_runtest_call(item): def pytest_runtest_call(item):
# ensure we run as a hook after capturemanager's # ensure we run as a hook after capturemanager's
+1 -1
View File
@@ -68,6 +68,6 @@ A good pull request has all of the following:
### A guide for forks ### A guide for forks
In order for your fork's data to be eligible for the training set: In order for your fork's data to be eligible for the training set:
* **Your cereal messaging structs must be [compatible](../cereal#custom-forks)** * **Your cereal messaging structs must be [compatible](../openpilot/cereal#custom-forks)**
* **The definitions of all the stock messaging structs must not change**: Do not change how any of the fields are set, including everything from `selfdriveState.enabled` to `carState.steeringAngleDeg`. Instead, create your own structs and set them however you'd like. * **The definitions of all the stock messaging structs must not change**: Do not change how any of the fields are set, including everything from `selfdriveState.enabled` to `carState.steeringAngleDeg`. Instead, create your own structs and set them however you'd like.
* **Do not include cars that are not supported in upstream platforms**: Instead, create new opendbc platforms for cars that you'd like to support outside of upstream, even if it's just a trim-level difference. * **Do not include cars that are not supported in upstream platforms**: Instead, create new opendbc platforms for cars that you'd like to support outside of upstream, even if it's just a trim-level difference.
+2 -2
View File
@@ -35,8 +35,8 @@ For additional safety implementation details, refer to [panda safety model](http
### Forks of openpilot ### Forks of openpilot
* Do not disable or nerf [driver monitoring](https://github.com/commaai/openpilot/tree/master/selfdrive/monitoring) * Do not disable or nerf [driver monitoring](https://github.com/commaai/openpilot/tree/master/openpilot/selfdrive/monitoring)
* Do not disable or nerf [excessive actuation checks](https://github.com/commaai/openpilot/tree/master/selfdrive/selfdrived/helpers.py) * Do not disable or nerf [excessive actuation checks](https://github.com/commaai/openpilot/tree/master/openpilot/selfdrive/selfdrived/helpers.py)
* If your fork modifies any of the code in `opendbc/safety/`: * If your fork modifies any of the code in `opendbc/safety/`:
* your fork cannot use the openpilot trademark * your fork cannot use the openpilot trademark
* your fork must preserve the full [safety test suite](https://github.com/commaai/opendbc/tree/master/opendbc/safety/tests) and all tests must pass, including any new coverage required by the fork's changes * your fork must preserve the full [safety test suite](https://github.com/commaai/opendbc/tree/master/opendbc/safety/tests) and all tests must pass, including any new coverage required by the fork's changes
+3 -3
View File
@@ -2,13 +2,13 @@
openpilot records routes in one minute chunks called segments. A route starts on the rising edge of ignition and ends on the falling edge. openpilot records routes in one minute chunks called segments. A route starts on the rising edge of ignition and ends on the falling edge.
Check out our [Python library](https://github.com/commaai/openpilot/blob/master/tools/lib/logreader.py) for reading openpilot logs. Also checkout our [tools](https://github.com/commaai/openpilot/tree/master/tools) to replay and view your data. These are the same tools we use to debug and develop openpilot. Check out our [Python library](https://github.com/commaai/openpilot/blob/master/openpilot/tools/lib/logreader.py) for reading openpilot logs. Also checkout our [tools](https://github.com/commaai/openpilot/tree/master/openpilot/tools) to replay and view your data. These are the same tools we use to debug and develop openpilot.
For each segment, openpilot records the following log types: For each segment, openpilot records the following log types:
## rlog.zst ## rlog.zst
rlogs contain all the messages passed amongst openpilot's processes. See [cereal/services.py](https://github.com/commaai/openpilot/blob/master/cereal/services.py) for a list of all the logged services. They're a zstd archive of the serialized [Capn Proto](https://capnproto.org/) messages. rlogs contain all the messages passed amongst openpilot's processes. See [openpilot/cereal/services.py](https://github.com/commaai/openpilot/blob/master/openpilot/cereal/services.py) for a list of all the logged services. They're a zstd archive of the serialized [Capn Proto](https://capnproto.org/) messages.
## {f,e,d}camera.hevc ## {f,e,d}camera.hevc
@@ -20,7 +20,7 @@ Each camera stream is H.265 encoded and written to its respective file.
## qlog.zst & qcamera.ts ## qlog.zst & qcamera.ts
qlogs are a decimated subset of the rlogs. Check out [cereal/services.py](https://github.com/commaai/cereal/blob/master/services.py) for the decimation. qlogs are a decimated subset of the rlogs. Check out [openpilot/cereal/services.py](https://github.com/commaai/openpilot/blob/master/openpilot/cereal/services.py) for the decimation.
qcameras are H.264 encoded, lower res versions of the fcamera.hevc. The video shown in [comma connect](https://connect.comma.ai/) is from the qcameras. qcameras are H.264 encoded, lower res versions of the fcamera.hevc. The video shown in [comma connect](https://connect.comma.ai/) is from the qcameras.
+1 -1
View File
@@ -30,7 +30,7 @@ Each car brand is supported by a standard interface structure in `opendbc/car/[b
For historical reasons, openpilot still contains a small amount of car-specific logic. This will eventually be migrated to opendbc or otherwise removed. For historical reasons, openpilot still contains a small amount of car-specific logic. This will eventually be migrated to opendbc or otherwise removed.
* `selfdrive/car/car_specific.py`: Brand-specific event logic * `openpilot/selfdrive/car/car_specific.py`: Brand-specific event logic
# How do I port car? # How do I port car?
+1 -1
View File
@@ -5,7 +5,7 @@ Replaying is a critical tool for openpilot development and debugging.
## Replaying a route ## Replaying a route
*Hardware required: none* *Hardware required: none*
Just run `tools/replay/replay --demo`. Just run `openpilot/tools/replay/replay --demo`.
## Replaying CAN data ## Replaying CAN data
*Hardware required: jungle and comma four* *Hardware required: jungle and comma four*
+5 -5
View File
@@ -28,10 +28,10 @@ scons
We'll run the `replay` tool with the demo route to get data streaming for testing our UI changes. We'll run the `replay` tool with the demo route to get data streaming for testing our UI changes.
```bash ```bash
# in terminal 1 # in terminal 1
tools/replay/replay --demo openpilot/tools/replay/replay --demo
# in terminal 2 # in terminal 2
./selfdrive/ui/ui.py ./openpilot/selfdrive/ui/ui.py
``` ```
The openpilot UI should launch and show a replay of the demo route. The openpilot UI should launch and show a replay of the demo route.
@@ -45,10 +45,10 @@ Now lets update the speed display color in the UI.
Search for the function responsible for rendering the current speed: Search for the function responsible for rendering the current speed:
```bash ```bash
git grep "_draw_current_speed" selfdrive/ui/onroad/hud_renderer.py git grep "_draw_current_speed" openpilot/selfdrive/ui/onroad/hud_renderer.py
``` ```
You'll find the relevant code inside `selfdrive/ui/onroad/hud_renderer.py`, in this function: You'll find the relevant code inside `openpilot/selfdrive/ui/onroad/hud_renderer.py`, in this function:
```python ```python
def _draw_current_speed(self, rect: rl.Rectangle) -> None: def _draw_current_speed(self, rect: rl.Rectangle) -> None:
@@ -72,7 +72,7 @@ Change `COLORS.white` to make it **blue** instead of white. A nice soft blue is
After making changes, re-run the UI to see your new UI: After making changes, re-run the UI to see your new UI:
```bash ```bash
./selfdrive/ui/ui.py ./openpilot/selfdrive/ui/ui.py
``` ```
![](https://blog.comma.ai/img/blue_speed_ui.png) ![](https://blog.comma.ai/img/blue_speed_ui.png)
+12 -4
View File
@@ -19,12 +19,12 @@ function agnos_init {
# Check if AGNOS update is required # Check if AGNOS update is required
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
AGNOS_PY="$DIR/system/hardware/tici/agnos.py" AGNOS_PY="$DIR/openpilot/common/hardware/tici/agnos.py"
MANIFEST="$DIR/system/hardware/tici/agnos.json" MANIFEST="$DIR/openpilot/system/hardware/tici/agnos.json"
if $AGNOS_PY --verify $MANIFEST; then if $AGNOS_PY --verify $MANIFEST; then
sudo reboot sudo reboot
fi fi
$DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST $DIR/openpilot/common/hardware/tici/updater $AGNOS_PY $MANIFEST
fi fi
} }
@@ -70,6 +70,14 @@ function launch {
ln -sfn $(pwd) /data/pythonpath ln -sfn $(pwd) /data/pythonpath
export PYTHONPATH="$PWD" export PYTHONPATH="$PWD"
# submodule package symlinks for PYTHONPATH imports on device.
# on PC these come from editable installs via pyproject.toml / uv.
ln -sfn msgq_repo/msgq msgq
ln -sfn opendbc_repo/opendbc opendbc
ln -sfn rednose_repo/rednose rednose
ln -sfn teleoprtc_repo/teleoprtc teleoprtc
ln -sfn tinygrad_repo/tinygrad tinygrad
# hardware specific init # hardware specific init
if [ -f /AGNOS ]; then if [ -f /AGNOS ]; then
agnos_init agnos_init
@@ -79,7 +87,7 @@ function launch {
tmux capture-pane -pq -S-1000 > /tmp/launch_log tmux capture-pane -pq -S-1000 > /tmp/launch_log
# start manager # start manager
cd system/manager cd openpilot/system/manager
if [ ! -f $DIR/prebuilt ]; then if [ ! -f $DIR/prebuilt ]; then
./build.py ./build.py
fi fi
-1
View File
@@ -1 +0,0 @@
msgq_repo/msgq
-1
View File
@@ -1 +0,0 @@
opendbc_repo/opendbc
@@ -24,17 +24,17 @@ things are not. Read more details [here](https://capnproto.org/language.html).
### Custom forks ### Custom forks
Forks of [openpilot](https://github.com/commaai/openpilot) might want to add things to the messaging Forks of [openpilot](https://github.com/commaai/openpilot) might want to add things to the messaging
spec, however this could conflict with future changes made in mainline cereal/openpilot. Rebasing against mainline openpilot spec, however this could conflict with future changes made in mainline openpilot's cereal spec. Rebasing against mainline openpilot
then means breaking backwards-compatibility with all old logs of your fork. So we added reserved events in then means breaking backwards-compatibility with all old logs of your fork. So we added reserved events in
[custom.capnp](custom.capnp) that we will leave empty in mainline cereal/openpilot. **If you only modify those, you can ensure your [custom.capnp](custom.capnp) that we will leave empty in mainline openpilot's cereal spec. **If you only modify those, you can ensure your
fork will remain backwards-compatible with all versions of mainline openpilot and your fork.** fork will remain backwards-compatible with all versions of mainline openpilot and your fork.**
An example of compatible changes: An example of compatible changes:
```diff ```diff
diff --git a/cereal/custom.capnp b/cereal/custom.capnp diff --git a/openpilot/cereal/custom.capnp b/openpilot/cereal/custom.capnp
index 3348e859e..3365c7b98 100644 index 3348e859e..3365c7b98 100644
--- a/cereal/custom.capnp --- a/openpilot/cereal/custom.capnp
+++ b/cereal/custom.capnp +++ b/openpilot/cereal/custom.capnp
@@ -10,7 +10,11 @@ $Cxx.namespace("cereal"); @@ -10,7 +10,11 @@ $Cxx.namespace("cereal");
# DO rename the structs # DO rename the structs
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af) # DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
@@ -48,10 +48,10 @@ index 3348e859e..3365c7b98 100644
} }
struct CustomReserved1 @0xaedffd8f31e7b55d { struct CustomReserved1 @0xaedffd8f31e7b55d {
diff --git a/cereal/log.capnp b/cereal/log.capnp diff --git a/openpilot/cereal/log.capnp b/openpilot/cereal/log.capnp
index 1209f3fd9..b189f58b6 100644 index 1209f3fd9..b189f58b6 100644
--- a/cereal/log.capnp --- a/openpilot/cereal/log.capnp
+++ b/cereal/log.capnp +++ b/openpilot/cereal/log.capnp
@@ -2558,14 +2558,14 @@ struct Event { @@ -2558,14 +2558,14 @@ struct Event {
# DO change the name of the field # DO change the name of the field
@@ -76,7 +76,7 @@ index 1209f3fd9..b189f58b6 100644
Example Example
--- ---
```python ```python
import cereal.messaging as messaging import openpilot.cereal.messaging as messaging
# in subscriber # in subscriber
sm = messaging.SubMaster(['sensorEvents']) sm = messaging.SubMaster(['sensorEvents'])
+22
View File
@@ -0,0 +1,22 @@
Import('env', 'common', 'msgq')
cereal_dir = Dir('.')
gen_dir = Dir('gen')
# Build cereal
schema_files = ['log.capnp', 'deprecated.capnp', 'custom.capnp']
car_capnp = '#opendbc_repo/opendbc/car/car.capnp'
all_output = schema_files + ['car.capnp']
env.Command([f'gen/cpp/{s}.c++' for s in all_output] + [f'gen/cpp/{s}.h' for s in all_output],
schema_files + [car_capnp],
f"capnpc --src-prefix={cereal_dir.path} --src-prefix=opendbc_repo/opendbc/car --import-path=opendbc_repo/opendbc/car $SOURCES -o c++:{gen_dir.path}/cpp/")
cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in all_output])
# Build messaging
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc', 'messaging/bridge_zmq.cc'], LIBS=[msgq, common, 'pthread'])
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
Export('cereal', 'socketmaster')
+11
View File
@@ -0,0 +1,11 @@
import os
import capnp
from importlib.resources import as_file, files
capnp.remove_import_hook()
with as_file(files("openpilot.cereal")) as fspath, as_file(files("opendbc")) as opendbc_path:
CEREAL_PATH = fspath.as_posix()
opendbc_import_path = os.path.join(os.path.realpath(opendbc_path.as_posix()), 'car')
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"), imports=[opendbc_import_path])
custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp"), imports=[opendbc_import_path])
@@ -1,4 +1,4 @@
using Cxx = import "./include/c++.capnp"; using Cxx = import "/include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
@0xb526ba661d550a59; @0xb526ba661d550a59;
@@ -1,4 +1,4 @@
using Cxx = import "./include/c++.capnp"; using Cxx = import "/include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
@0x80ef1ec4889c2a63; @0x80ef1ec4889c2a63;
@@ -760,18 +760,6 @@ struct LateralLQRState @0x9024e2d790c82ade {
steeringAngleDesiredDeg @6 :Float32; steeringAngleDesiredDeg @6 :Float32;
} }
struct LateralCurvatureState @0xad9d8095c06f7c61 {
active @0 :Bool;
actualCurvature @1 :Float32;
desiredCurvature @2 :Float32;
error @3 :Float32;
p @4 :Float32;
i @5 :Float32;
f @6 :Float32;
output @7 :Float32;
saturated @8 :Bool;
}
struct LateralPlannerSolution @0x84caeca5a6b4acfe { struct LateralPlannerSolution @0x84caeca5a6b4acfe {
x @0 :List(Float32); x @0 :List(Float32);
y @1 :List(Float32); y @1 :List(Float32);
+90 -30
View File
@@ -1,7 +1,7 @@
using Cxx = import "./include/c++.capnp"; using Cxx = import "/include/c++.capnp";
$Cxx.namespace("cereal"); $Cxx.namespace("cereal");
using Car = import "car.capnp"; using Car = import "/car.capnp";
using Deprecated = import "deprecated.capnp"; using Deprecated = import "deprecated.capnp";
using Custom = import "custom.capnp"; using Custom = import "custom.capnp";
@@ -414,6 +414,10 @@ struct CanData {
struct DeviceState @0xa4d8b5af2aa492eb { struct DeviceState @0xa4d8b5af2aa492eb {
deviceType @45 :InitData.DeviceType; deviceType @45 :InitData.DeviceType;
# usb
chestnutPresent @51 :Bool;
usbState @52 :UsbState;
networkType @22 :NetworkType; networkType @22 :NetworkType;
networkInfo @31 :NetworkInfo; networkInfo @31 :NetworkInfo;
networkStrength @24 :NetworkStrength; networkStrength @24 :NetworkStrength;
@@ -684,33 +688,47 @@ struct PeripheralState {
} }
} }
struct UsbState {
devices @0 :List(Device);
struct Device {
busnum @0 :UInt8;
devnum @1 :UInt8;
vendorId @2 :UInt16;
productId @3 :UInt16;
speedMbps @4 :UInt16;
manufacturer @6 :Text;
product @5 :Text;
linkErrorCount @7 :UInt16;
}
}
struct RadarState @0x9a185389d6fdd05f { struct RadarState @0x9a185389d6fdd05f {
mdMonoTime @6 :UInt64; mdMonoTime @6 :UInt64; # for debugging
carStateMonoTime @11 :UInt64;
radarErrors @13 :Car.RadarData.Error; radarErrors @13 :Car.RadarData.Error;
leadOne @3 :LeadData; leadOne @3 :LeadData;
leadTwo @4 :LeadData; leadTwo @4 :LeadData;
struct LeadData { struct LeadData {
dRel @0 :Float32; dRel @0 :Float32; # m from the front bumper of the car
yRel @1 :Float32; yRel @1 :Float32; # m in car frame, left positive
vRel @2 :Float32; vRel @2 :Float32; # m/s relative longitudinal speed
aRel @3 :Float32; vLead @4 :Float32; # m/s absolute lead speed
vLead @4 :Float32; vLeadK @8 :Float32; # kalman-filtered lead speed
dPath @6 :Float32; aLeadK @9 :Float32; # kalman-filtered lead accel
vLat @7 :Float32; present @11 :Bool; # true if a lead is present
vLeadK @8 :Float32; aLeadTau @12 :Float32; # lead accel time constant
aLeadK @9 :Float32; modelProb @13 :Float32; # vision model lead probability
fcw @10 :Bool; radar @14 :Bool; # true if lead is radar-matched (vs vision-only)
status @11 :Bool; radarTrackId @15 :Int32 = -1; # for debugging
aLeadTau @12 :Float32;
modelProb @13 :Float32;
radar @14 :Bool;
radarTrackId @15 :Int32 = -1;
deprecated :group { deprecated :group {
aRel @3 :Float32;
aLead @5 :Float32; aLead @5 :Float32;
dPath @6 :Float32;
vLat @7 :Float32;
fcw @10 :Bool;
} }
} }
@@ -723,6 +741,7 @@ struct RadarState @0x9a185389d6fdd05f {
calPerc @9 :Int8; calPerc @9 :Int8;
canMonoTimes @10 :List(UInt64); canMonoTimes @10 :List(UInt64);
cumLagMs @5 :Float32; cumLagMs @5 :Float32;
carStateMonoTime @11 :UInt64;
radarErrors @12 :List(Car.RadarData.ErrorDEPRECATED); radarErrors @12 :List(Car.RadarData.ErrorDEPRECATED);
} }
} }
@@ -771,13 +790,30 @@ struct SelfdriveState {
alertStatus @5 :AlertStatus; alertStatus @5 :AlertStatus;
alertSize @6 :AlertSize; alertSize @6 :AlertSize;
alertType @7 :Text; alertType @7 :Text;
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert; alertSound @13 :AudibleAlert;
alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert; alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert;
# configurable driving settings # configurable driving settings
experimentalMode @10 :Bool; experimentalMode @10 :Bool;
personality @11 :LongitudinalPersonality; personality @11 :LongitudinalPersonality;
enum AudibleAlert {
none @0;
engage @1;
disengage @2;
refuse @3;
warningSoft @4;
warningImmediate @5;
prompt @6;
promptRepeat @7;
promptDistracted @8;
preAlert @9;
}
enum OpenpilotState @0xdbe58b96d2d1ac61 { enum OpenpilotState @0xdbe58b96d2d1ac61 {
disabled @0; disabled @0;
preEnabled @1; preEnabled @1;
@@ -798,6 +834,10 @@ struct SelfdriveState {
mid @2; mid @2;
full @3; full @3;
} }
deprecated :group {
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
}
} }
struct ControlsState @0x97ff69c53601abf1 { struct ControlsState @0x97ff69c53601abf1 {
@@ -818,7 +858,7 @@ struct ControlsState @0x97ff69c53601abf1 {
debugState @59 :LateralDebugState; debugState @59 :LateralDebugState;
torqueState @60 :LateralTorqueState; torqueState @60 :LateralTorqueState;
curvatureStateDEPRECATED @65 :Deprecated.LateralCurvatureState; curvatureState @65 :LateralCurvatureState;
lqrStateDEPRECATED @55 :Deprecated.LateralLQRState; lqrStateDEPRECATED @55 :Deprecated.LateralLQRState;
indiStateDEPRECATED @52 :Deprecated.LateralINDIState; indiStateDEPRECATED @52 :Deprecated.LateralINDIState;
} }
@@ -867,6 +907,18 @@ struct ControlsState @0x97ff69c53601abf1 {
saturated @3 :Bool; saturated @3 :Bool;
} }
struct LateralCurvatureState @0xad9d8095c06f7c61 {
active @0 :Bool;
actualCurvature @1 :Float32;
desiredCurvature @2 :Float32;
error @3 :Float32;
p @4 :Float32;
i @5 :Float32;
f @6 :Float32;
output @7 :Float32;
saturated @8 :Bool;
}
deprecated :group { deprecated :group {
vEgo @0 :Float32; vEgo @0 :Float32;
vEgoRaw @32 :Float32; vEgoRaw @32 :Float32;
@@ -906,7 +958,7 @@ struct ControlsState @0x97ff69c53601abf1 {
alertStatus @38 :SelfdriveState.AlertStatus; alertStatus @38 :SelfdriveState.AlertStatus;
alertSize @39 :SelfdriveState.AlertSize; alertSize @39 :SelfdriveState.AlertSize;
alertType @44 :Text; alertType @44 :Text;
alertSound2 @56 :Car.CarControl.HUDControl.AudibleAlert; alertSound2 @56 :SelfdriveState.AudibleAlert;
engageable @41 :Bool; # can OP be engaged? engageable @41 :Bool; # can OP be engaged?
state @31 :SelfdriveState.OpenpilotState; state @31 :SelfdriveState.OpenpilotState;
enabled @19 :Bool; enabled @19 :Bool;
@@ -989,7 +1041,6 @@ struct ModelDataV2 {
roadEdgeStds @14 :List(Float32); roadEdgeStds @14 :List(Float32);
# predicted lead cars # predicted lead cars
leads @11 :List(LeadDataV2);
leadsV3 @18 :List(LeadDataV3); leadsV3 @18 :List(LeadDataV3);
meta @12 :MetaData; meta @12 :MetaData;
@@ -999,8 +1050,9 @@ struct ModelDataV2 {
action @26: Action; action @26: Action;
lateralPlannerSolutionDEPRECATED @25: Deprecated.LateralPlannerSolution; lateralPlannerSolutionDEPRECATED @25: Deprecated.LateralPlannerSolution;
leadsDEPRECATED @11 :List(LeadDataV2DEPRECATED);
struct LeadDataV2 { struct LeadDataV2DEPRECATED {
prob @0 :Float32; # probability that car is your lead at time t prob @0 :Float32; # probability that car is your lead at time t
t @1 :Float32; t @1 :Float32;
@@ -1120,7 +1172,7 @@ struct EncodeIndex {
} }
} }
struct AndroidLogEntry { struct OperatingSystemLogEntry {
id @0 :UInt8; id @0 :UInt8;
ts @1 :UInt64; ts @1 :UInt64;
priority @2 :UInt8; priority @2 :UInt8;
@@ -1258,7 +1310,7 @@ struct LateralPlan @0xe1e9318e2ae8b51e {
struct LiveLocationKalman { struct LiveLocationKalman {
# More info on reference frames: # More info on reference frames:
# https://github.com/commaai/openpilot/tree/master/common/transformations # https://github.com/commaai/openpilot/tree/master/openpilot/common/transformations
positionECEF @0 : Measurement; positionECEF @0 : Measurement;
positionGeodetic @1 : Measurement; positionGeodetic @1 : Measurement;
@@ -1315,7 +1367,7 @@ struct LiveLocationKalman {
struct LivePose { struct LivePose {
# More info on reference frames: # More info on reference frames:
# https://github.com/commaai/openpilot/tree/master/common/transformations # https://github.com/commaai/openpilot/tree/master/openpilot/common/transformations
orientationNED @0 :XYZMeasurement; orientationNED @0 :XYZMeasurement;
velocityDevice @1 :XYZMeasurement; velocityDevice @1 :XYZMeasurement;
accelerationDevice @2 :XYZMeasurement; accelerationDevice @2 :XYZMeasurement;
@@ -2108,8 +2160,10 @@ struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
struct DriverMonitoringState { struct DriverMonitoringState {
lockout @0 :Bool; lockout @0 :Bool;
alertCountLockoutPercent @1 :Int8; lockoutRecoveryPercent @11 :Int8;
alertTimeLockoutPercent @2 :Int8; alert3Count @12 :Int8;
noResponseCount @13 :Int8;
noResponseForceDecel @14 :Bool;
alwaysOn @3 :Bool; alwaysOn @3 :Bool;
alwaysOnLockout @4 :Bool; alwaysOnLockout @4 :Bool;
@@ -2173,6 +2227,11 @@ struct DriverMonitoringState {
calibratedPercent @0 :Int8; calibratedPercent @0 :Int8;
offset @1 :Float32; offset @1 :Float32;
} }
deprecated :group {
alertCountLockoutPercent @1 :Int8;
alertTimeLockoutPercent @2 :Int8;
}
} }
struct Boot { struct Boot {
@@ -2247,6 +2306,7 @@ struct LiveDelayData {
lateralDelayEstimateStd @5 :Float32; lateralDelayEstimateStd @5 :Float32;
points @4 :List(Float32); points @4 :List(Float32);
calPerc @6 :Int8; calPerc @6 :Int8;
version @7 :Int32;
enum Status { enum Status {
unestimated @0; unestimated @0;
@@ -2504,7 +2564,7 @@ struct Event {
rawAudioData @147 :AudioData; rawAudioData @147 :AudioData;
# systems stuff # systems stuff
androidLog @20 :AndroidLogEntry; operatingSystemLog @20 :OperatingSystemLogEntry;
managerState @78 :ManagerState; managerState @78 :ManagerState;
procLog @33 :ProcLog; procLog @33 :ProcLog;
clocks @35 :Clocks; clocks @35 :Clocks;
@@ -9,8 +9,8 @@ import time
from typing import Optional, List, Union, Dict from typing import Optional, List, Union, Dict
from cereal import log from openpilot.cereal import log
from cereal.services import SERVICE_LIST from openpilot.cereal.services import SERVICE_LIST
from openpilot.common.utils import MovingAverage from openpilot.common.utils import MovingAverage
NO_TRAVERSAL_LIMIT = 2**64-1 NO_TRAVERSAL_LIMIT = 2**64-1
@@ -1,7 +1,7 @@
#include <cassert> #include <cassert>
#include "cereal/messaging/msgq_to_zmq.h" #include "openpilot/cereal/messaging/msgq_to_zmq.h"
#include "cereal/services.h" #include "openpilot/cereal/services.h"
#include "common/util.h" #include "common/util.h"
ExitHandler do_exit; ExitHandler do_exit;
@@ -1,4 +1,4 @@
#include "cereal/messaging/bridge_zmq.h" #include "openpilot/cereal/messaging/bridge_zmq.h"
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
@@ -8,7 +8,7 @@
#include <capnp/serialize.h> #include <capnp/serialize.h>
#include "cereal/gen/cpp/log.capnp.h" #include "openpilot/cereal/gen/cpp/log.capnp.h"
#include "common/timing.h" #include "common/timing.h"
#include "msgq/ipc.h" #include "msgq/ipc.h"
@@ -1,8 +1,8 @@
#include "cereal/messaging/msgq_to_zmq.h" #include "openpilot/cereal/messaging/msgq_to_zmq.h"
#include <cassert> #include <cassert>
#include "cereal/services.h" #include "openpilot/cereal/services.h"
#include "common/util.h" #include "common/util.h"
extern ExitHandler do_exit; extern ExitHandler do_exit;
@@ -8,7 +8,7 @@
#include <vector> #include <vector>
#include "msgq/impl_msgq.h" #include "msgq/impl_msgq.h"
#include "cereal/messaging/bridge_zmq.h" #include "openpilot/cereal/messaging/bridge_zmq.h"
class MsgqToZmq { class MsgqToZmq {
public: public:
@@ -3,8 +3,8 @@
#include <string> #include <string>
#include <mutex> #include <mutex>
#include "cereal/services.h" #include "openpilot/cereal/services.h"
#include "cereal/messaging/messaging.h" #include "openpilot/cereal/messaging/messaging.h"
const bool SIMULATION = (getenv("SIMULATION") != nullptr) && (std::string(getenv("SIMULATION")) == "1"); const bool SIMULATION = (getenv("SIMULATION") != nullptr) && (std::string(getenv("SIMULATION")) == "1");
@@ -8,9 +8,10 @@ import time
from openpilot.common.parameterized import parameterized from openpilot.common.parameterized import parameterized
import pytest import pytest
from cereal import log, car from openpilot.cereal import log
import cereal.messaging as messaging from opendbc.car.structs import car
from cereal.services import SERVICE_LIST import openpilot.cereal.messaging as messaging
from openpilot.cereal.services import SERVICE_LIST
events = [evt for evt in log.Event.schema.union_fields if evt in SERVICE_LIST.keys()] events = [evt for evt in log.Event.schema.union_fields if evt in SERVICE_LIST.keys()]
@@ -2,11 +2,11 @@ import random
import time import time
from typing import Sized, cast from typing import Sized, cast
import cereal.messaging as messaging import openpilot.cereal.messaging as messaging
from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \ from openpilot.cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \
random_bytes, random_carstate, assert_carstate, \ random_bytes, random_carstate, assert_carstate, \
zmq_sleep zmq_sleep
from cereal.services import SERVICE_LIST from openpilot.cereal.services import SERVICE_LIST
class TestSubMaster: class TestSubMaster:
@@ -1,10 +1,10 @@
import os import subprocess
import tempfile import tempfile
from typing import Dict
from openpilot.common.parameterized import parameterized from openpilot.common.parameterized import parameterized
import cereal.services as services import openpilot.cereal.services as services
from cereal.services import SERVICE_LIST from openpilot.cereal.services import SERVICE_LIST
class TestServices: class TestServices:
@@ -17,5 +17,5 @@ class TestServices:
def test_generated_header(self): def test_generated_header(self):
with tempfile.NamedTemporaryFile(suffix=".h") as f: with tempfile.NamedTemporaryFile(suffix=".h") as f:
ret = os.system(f"python3 {services.__file__} > {f.name} && clang++ {f.name} -std=c++11") ret = subprocess.run(f"python3 {services.__file__} > {f.name} && clang++ {f.name} -std=c++11", shell=True).returncode
assert ret == 0, "generated services header is not valid C" assert ret == 0, "generated services header is not valid C"
@@ -41,7 +41,7 @@ _services: dict[str, tuple] = {
"liveCalibration": (True, 4., 4), "liveCalibration": (True, 4., 4),
"liveTorqueParameters": (True, 4., 1), "liveTorqueParameters": (True, 4., 1),
"liveDelay": (True, 4., 1), "liveDelay": (True, 4., 1),
"androidLog": (True, 0.), "operatingSystemLog": (True, 0.),
"carState": (True, 100., 10), "carState": (True, 100., 10),
"carControl": (True, 100., 10), "carControl": (True, 100., 10),
"carOutput": (True, 100., 10), "carOutput": (True, 100., 10),
-1
View File
@@ -1 +0,0 @@
../common
@@ -5,6 +5,7 @@ common_libs = [
'swaglog.cc', 'swaglog.cc',
'util.cc', 'util.cc',
'ratekeeper.cc', 'ratekeeper.cc',
'yuv.cc',
] ]
_common = env.Library('common', common_libs, LIBS="json11") _common = env.Library('common', common_libs, LIBS="json11")
@@ -12,7 +13,7 @@ Export('_common')
if GetOption('extras'): if GetOption('extras'):
env.Program('tests/test_common', env.Program('tests/test_common',
['tests/test_runner.cc', 'tests/test_params.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc'], ['tests/test_runner.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc'],
LIBS=[_common, 'json11', 'zmq', 'pthread']) LIBS=[_common, 'json11', 'zmq', 'pthread'])
# Cython bindings # Cython bindings
@@ -3,8 +3,8 @@ import os
import requests import requests
import unicodedata import unicodedata
from datetime import datetime, timedelta, UTC from datetime import datetime, timedelta, UTC
from openpilot.system.hardware.hw import Paths from openpilot.common.hardware.hw import Paths
from openpilot.system.version import get_version from openpilot.common.version import get_version
# name: jwt signature algorithm # name: jwt signature algorithm
KEYS = {"id_rsa": "RS256", KEYS = {"id_rsa": "RS256",
@@ -1,4 +1,4 @@
import os import os
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../.."))
+3
View File
@@ -0,0 +1,3 @@
from openpilot.common.esim.base import LPABase, LPAError, LPAProfileNotFoundError, Profile
__all__ = ["LPABase", "LPAError", "LPAProfileNotFoundError", "Profile"]
+55
View File
@@ -0,0 +1,55 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
class LPAError(RuntimeError):
pass
class LPAProfileNotFoundError(LPAError):
pass
@dataclass
class Profile:
iccid: str
nickname: str
enabled: bool
provider: str
@property
def is_comma(self) -> bool:
return self.provider == 'Webbing' and self.iccid.startswith('8985235')
class LPABase(ABC):
@abstractmethod
def list_profiles(self) -> list[Profile]:
pass
@abstractmethod
def get_active_profile(self) -> Profile | None:
pass
@abstractmethod
def delete_profile(self, iccid: str) -> None:
pass
@abstractmethod
def download_profile(self, qr: str, nickname: str | None = None) -> None:
pass
@abstractmethod
def nickname_profile(self, iccid: str, nickname: str) -> None:
pass
@abstractmethod
def switch_profile(self, iccid: str) -> None:
pass
def process_notifications(self) -> None:
pass
@abstractmethod
def is_euicc(self) -> bool:
pass
@@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
from openpilot.system.hardware import HARDWARE from openpilot.common.hardware import HARDWARE
from openpilot.system.hardware.base import LPABase, Profile from openpilot.common.esim.base import LPABase, Profile
def sorted_profiles(lpa: LPABase) -> list[Profile]: def sorted_profiles(lpa: LPABase) -> list[Profile]:
@@ -6,7 +6,6 @@ import fcntl
import hashlib import hashlib
import os import os
import requests import requests
import serial
import subprocess import subprocess
import sys import sys
import termios import termios
@@ -19,7 +18,8 @@ from typing import Any
from pathlib import Path from pathlib import Path
from openpilot.common.time_helpers import system_time_valid from openpilot.common.time_helpers import system_time_valid
from openpilot.system.hardware.base import LPABase, LPAError, LPAProfileNotFoundError, Profile from openpilot.common.esim.base import LPABase, LPAError, LPAProfileNotFoundError, Profile
from openpilot.common.serial import Serial, SerialException
GSMA_CI_BUNDLE = str(Path(__file__).parent / "gsma_ci_bundle.pem") GSMA_CI_BUNDLE = str(Path(__file__).parent / "gsma_ci_bundle.pem")
@@ -133,7 +133,7 @@ class AtClient:
self._device = device self._device = device
self._baud = baud self._baud = baud
self._timeout = timeout self._timeout = timeout
self._serial: serial.Serial | None = None self._serial: Serial | None = None
def send_raw(self, data: bytes) -> None: def send_raw(self, data: bytes) -> None:
self._ensure_serial() self._ensure_serial()
@@ -185,14 +185,14 @@ class AtClient:
pass pass
self._serial = None self._serial = None
if self._serial is None: if self._serial is None:
self._serial = serial.Serial(self._device, baudrate=self._baud, timeout=self._timeout) self._serial = Serial(self._device, baudrate=self._baud, timeout=self._timeout)
def query(self, cmd: str) -> list[str]: def query(self, cmd: str) -> list[str]:
self._ensure_serial() self._ensure_serial()
try: try:
self._send(cmd) self._send(cmd)
return self._expect() return self._expect()
except serial.SerialException: except SerialException:
self._ensure_serial(reconnect=True) self._ensure_serial(reconnect=True)
self._send(cmd) self._send(cmd)
return self._expect() return self._expect()
@@ -208,7 +208,7 @@ class AtClient:
if self._serial: if self._serial:
try: try:
self._serial.reset_input_buffer() self._serial.reset_input_buffer()
except (OSError, serial.SerialException, termios.error): except (OSError, SerialException, termios.error):
self._ensure_serial(reconnect=True) self._ensure_serial(reconnect=True)
for line in self.query(f'AT+CCHO="{ISDR_AID}"'): for line in self.query(f'AT+CCHO="{ISDR_AID}"'):
if line.startswith("+CCHO:") and (ch := line.split(":", 1)[1].strip()): if line.startswith("+CCHO:") and (ch := line.split(":", 1)[1].strip()):
@@ -230,7 +230,7 @@ class AtClient:
try: try:
self._open_isdr_once() self._open_isdr_once()
return return
except (RuntimeError, TimeoutError, termios.error, serial.SerialException): except (RuntimeError, TimeoutError, termios.error, SerialException):
time.sleep(OPEN_ISDR_RETRY_DELAY_S) time.sleep(OPEN_ISDR_RETRY_DELAY_S)
if attempt == OPEN_ISDR_RESET_ATTEMPT: if attempt == OPEN_ISDR_RESET_ATTEMPT:
self._reset_modem() self._reset_modem()
@@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import io
import sys import sys
import math import math
import os import os
@@ -21,13 +22,12 @@ def get_chunk_targets(path, file_size):
def chunk_file(path, targets): def chunk_file(path, targets):
manifest_path, *chunk_paths = targets manifest_path, *chunk_paths = targets
with open(path, 'rb') as f: actual_num_chunks = max(1, math.ceil(os.path.getsize(path) / CHUNK_SIZE))
data = f.read()
actual_num_chunks = max(1, math.ceil(len(data) / CHUNK_SIZE))
assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}" assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}"
for i, chunk_path in enumerate(chunk_paths): with open(path, 'rb') as f:
with open(chunk_path, 'wb') as f: for chunk_path in chunk_paths:
f.write(data[i * CHUNK_SIZE:(i + 1) * CHUNK_SIZE]) with open(chunk_path, 'wb') as out:
out.write(f.read(CHUNK_SIZE))
Path(manifest_path).write_text(str(len(chunk_paths))) Path(manifest_path).write_text(str(len(chunk_paths)))
os.remove(path) os.remove(path)
@@ -39,14 +39,40 @@ def get_existing_chunks(path):
return _chunk_paths(path, num_chunks) return _chunk_paths(path, num_chunks)
raise FileNotFoundError(path) raise FileNotFoundError(path)
def read_file_chunked(path): class ChunkStream(io.RawIOBase):
def __init__(self, paths):
self._paths = iter(paths)
self._buf = memoryview(b'')
def readable(self):
return True
def readinto(self, b):
n = 0
while n < len(b):
if not self._buf:
p = next(self._paths, None)
if p is None:
break
with open(p, 'rb') as f:
self._buf = memoryview(f.read())
continue
take = min(len(b) - n, len(self._buf))
b[n:n + take] = self._buf[:take]
self._buf = self._buf[take:]
n += take
return n
def open_file_chunked(path):
manifest_path = get_manifest_path(path) manifest_path = get_manifest_path(path)
if os.path.isfile(manifest_path): if os.path.isfile(manifest_path):
num_chunks = int(Path(manifest_path).read_text().strip()) num_chunks = int(Path(manifest_path).read_text().strip())
return b''.join(Path(get_chunk_name(path, i, num_chunks)).read_bytes() for i in range(num_chunks)) paths = [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
if os.path.isfile(path): elif os.path.isfile(path):
return Path(path).read_bytes() paths = [path]
raise FileNotFoundError(path) else:
raise FileNotFoundError(path)
return io.BufferedReader(ChunkStream(paths))
if __name__ == "__main__": if __name__ == "__main__":
@@ -1,9 +1,9 @@
import os import os
from typing import cast from typing import cast
from openpilot.system.hardware.base import HardwareBase from openpilot.common.hardware.base import HardwareBase
from openpilot.system.hardware.tici.hardware import Tici from openpilot.common.hardware.tici.hardware import Tici
from openpilot.system.hardware.pc.hardware import Pc from openpilot.common.hardware.pc.hardware import Pc
TICI = os.path.isfile('/TICI') TICI = os.path.isfile('/TICI')
AGNOS = os.path.isfile('/AGNOS') AGNOS = os.path.isfile('/AGNOS')
@@ -5,15 +5,13 @@
#include <map> #include <map>
#include <string> #include <string>
#include "cereal/gen/cpp/log.capnp.h" #include "openpilot/cereal/gen/cpp/log.capnp.h"
// no-op base hw class // no-op base hw class
class HardwareNone { class HardwareNone {
public: public:
static std::string get_name() { return ""; } static std::string get_name() { return ""; }
static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::UNKNOWN; } static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::UNKNOWN; }
static int get_voltage() { return 0; }
static int get_current() { return 0; }
static std::string get_serial() { return "cccccc"; } static std::string get_serial() { return "cccccc"; }
@@ -24,6 +22,4 @@ public:
static void set_ir_power(int percentage) {} static void set_ir_power(int percentage) {}
static bool PC() { return false; } static bool PC() { return false; }
static bool TICI() { return false; }
static bool AGNOS() { return false; }
}; };
@@ -2,28 +2,12 @@ import os
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
from dataclasses import dataclass, fields from dataclasses import dataclass, fields
from cereal import log from openpilot.cereal import log
from openpilot.common.esim.base import LPABase
NetworkType = log.DeviceState.NetworkType NetworkType = log.DeviceState.NetworkType
NetworkStrength = log.DeviceState.NetworkStrength NetworkStrength = log.DeviceState.NetworkStrength
class LPAError(RuntimeError):
pass
class LPAProfileNotFoundError(LPAError):
pass
@dataclass
class Profile:
iccid: str
nickname: str
enabled: bool
provider: str
@property
def is_comma(self) -> bool:
return self.provider == 'Webbing' and self.iccid.startswith('8985235')
@dataclass @dataclass
class ThermalZone: class ThermalZone:
# a zone from /sys/class/thermal/thermal_zone* # a zone from /sys/class/thermal/thermal_zone*
@@ -70,38 +54,6 @@ class ThermalConfig:
ret[f.name + "TempC"] = v.read() ret[f.name + "TempC"] = v.read()
return ret return ret
class LPABase(ABC):
@abstractmethod
def list_profiles(self) -> list[Profile]:
pass
@abstractmethod
def get_active_profile(self) -> Profile | None:
pass
@abstractmethod
def delete_profile(self, iccid: str) -> None:
pass
@abstractmethod
def download_profile(self, qr: str, nickname: str | None = None) -> None:
pass
@abstractmethod
def nickname_profile(self, iccid: str, nickname: str) -> None:
pass
@abstractmethod
def switch_profile(self, iccid: str) -> None:
pass
def process_notifications(self) -> None:
pass
@abstractmethod
def is_euicc(self) -> bool:
pass
class HardwareBase(ABC): class HardwareBase(ABC):
@staticmethod @staticmethod
def get_cmdline() -> dict[str, str]: def get_cmdline() -> dict[str, str]:
@@ -133,7 +85,7 @@ class HardwareBase(ABC):
def get_device_type(self): def get_device_type(self):
pass pass
def get_imei(self, slot) -> str: def get_imei(self) -> str:
return "" return ""
def get_serial(self): def get_serial(self):
@@ -190,21 +142,12 @@ class HardwareBase(ABC):
def get_gpu_usage_percent(self): def get_gpu_usage_percent(self):
return 0 return 0
def get_modem_version(self):
return None
def get_modem_temperatures(self): def get_modem_temperatures(self):
return [] return []
def initialize_hardware(self): def initialize_hardware(self):
pass pass
def get_networks(self):
return None
def has_internal_panda(self) -> bool:
return False
def reset_internal_panda(self): def reset_internal_panda(self):
pass pass
@@ -214,11 +157,5 @@ class HardwareBase(ABC):
def get_modem_data_usage(self): def get_modem_data_usage(self):
return -1, -1 return -1, -1
def get_voltage(self) -> float:
return 0.
def get_current(self) -> float:
return 0.
def set_ir_power(self, percent: int): def set_ir_power(self, percent: int):
pass pass
@@ -2,14 +2,14 @@
#include <string> #include <string>
#include "system/hardware/base.h" #include "common/hardware/base.h"
#include "common/util.h" #include "common/util.h"
#if __TICI__ #if __TICI__
#include "system/hardware/tici/hardware.h" #include "common/hardware/tici/hardware.h"
#define Hardware HardwareTici #define Hardware HardwareTici
#else #else
#include "system/hardware/pc/hardware.h" #include "common/hardware/pc/hardware.h"
#define Hardware HardwarePC #define Hardware HardwarePC
#endif #endif
@@ -2,7 +2,7 @@ import os
import platform import platform
from pathlib import Path from pathlib import Path
from openpilot.system.hardware import PC from openpilot.common.hardware import PC
DEFAULT_DOWNLOAD_CACHE_ROOT = "/tmp/comma_download_cache" DEFAULT_DOWNLOAD_CACHE_ROOT = "/tmp/comma_download_cache"
@@ -2,13 +2,11 @@
#include <string> #include <string>
#include "system/hardware/base.h" #include "common/hardware/base.h"
class HardwarePC : public HardwareNone { class HardwarePC : public HardwareNone {
public: public:
static std::string get_name() { return "pc"; } static std::string get_name() { return "pc"; }
static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::PC; } static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::PC; }
static bool PC() { return true; } static bool PC() { return true; }
static bool TICI() { return util::getenv("TICI", 0) == 1; }
static bool AGNOS() { return util::getenv("TICI", 0) == 1; }
}; };
+10
View File
@@ -0,0 +1,10 @@
from openpilot.cereal import log
from openpilot.common.hardware.base import HardwareBase
class Pc(HardwareBase):
def get_device_type(self):
return "pc"
def get_network_type(self):
# some stuff is gated on wifi, so just assume for now
return log.DeviceState.NetworkType.wifi
@@ -0,0 +1 @@
@@ -10,12 +10,9 @@ from collections.abc import Generator
import requests import requests
import openpilot.system.updated.casync.casync as casync
SPARSE_CHUNK_FMT = struct.Struct('H2xI4x') SPARSE_CHUNK_FMT = struct.Struct('H2xI4x')
CAIBX_URL = "https://commadist.azureedge.net/agnosupdate/"
AGNOS_MANIFEST_FILE = "system/hardware/tici/agnos.json" AGNOS_MANIFEST_FILE = "openpilot/system/hardware/tici/agnos.json"
class StreamingDecompressor: class StreamingDecompressor:
@@ -187,50 +184,6 @@ def extract_compressed_image(target_slot_number: int, partition: dict, cloudlog)
os.sync() os.sync()
def extract_casync_image(target_slot_number: int, partition: dict, cloudlog):
path = get_partition_path(target_slot_number, partition)
seed_path = path[:-1] + ('b' if path[-1] == 'a' else 'a')
target = casync.parse_caibx(partition['casync_caibx'])
sources: list[tuple[str, casync.ChunkReader, casync.ChunkDict]] = []
# First source is the current partition.
try:
raw_hash = get_raw_hash(seed_path, partition['size'])
caibx_url = f"{CAIBX_URL}{partition['name']}-{raw_hash}.caibx"
try:
cloudlog.info(f"casync fetching {caibx_url}")
sources += [('seed', casync.FileChunkReader(seed_path), casync.build_chunk_dict(casync.parse_caibx(caibx_url)))]
except requests.RequestException:
cloudlog.error(f"casync failed to load {caibx_url}")
except Exception:
cloudlog.exception("casync failed to hash seed partition")
# Second source is the target partition, this allows for resuming
sources += [('target', casync.FileChunkReader(path), casync.build_chunk_dict(target))]
# Finally we add the remote source to download any missing chunks
sources += [('remote', casync.RemoteChunkReader(partition['casync_store']), casync.build_chunk_dict(target))]
last_p = 0
def progress(cur):
nonlocal last_p
p = int(cur / partition['size'] * 100)
if p != last_p:
last_p = p
print(f"Installing {partition['name']}: {p}", flush=True)
stats = casync.extract(target, sources, path, progress)
cloudlog.error(f'casync done {json.dumps(stats)}')
os.sync()
if not verify_partition(target_slot_number, partition, force_full_check=True):
raise Exception(f"Raw hash mismatch '{partition['hash_raw'].lower()}'")
def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalone=False): def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalone=False):
cloudlog.info(f"Downloading and writing {partition['name']}") cloudlog.info(f"Downloading and writing {partition['name']}")
@@ -245,10 +198,7 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalo
path = get_partition_path(target_slot_number, partition) path = get_partition_path(target_slot_number, partition)
if ('casync_caibx' in partition) and not standalone: extract_compressed_image(target_slot_number, partition, cloudlog)
extract_casync_image(target_slot_number, partition, cloudlog)
else:
extract_compressed_image(target_slot_number, partition, cloudlog)
# Write hash after successful flash # Write hash after successful flash
if not full_check: if not full_check:
@@ -278,7 +228,7 @@ def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, st
cloudlog.info(f"Target slot {target_slot_number}") cloudlog.info(f"Target slot {target_slot_number}")
# set target slot as unbootable # set target slot as unbootable
os.system(f"abctl --set_unbootable {target_slot_number}") subprocess.run(f"abctl --set_unbootable {target_slot_number}", shell=True)
for partition in update: for partition in update:
success = False success = False
@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdlib>
#include <cassert> #include <cassert>
#include <fstream> #include <fstream>
#include <map> #include <map>
@@ -8,7 +7,7 @@
#include <algorithm> // for std::clamp #include <algorithm> // for std::clamp
#include "common/util.h" #include "common/util.h"
#include "system/hardware/base.h" #include "common/hardware/base.h"
class HardwareTici : public HardwareNone { class HardwareTici : public HardwareNone {
public: public:
@@ -22,7 +21,6 @@ public:
static cereal::InitData::DeviceType get_device_type() { static cereal::InitData::DeviceType get_device_type() {
static const std::map<std::string, cereal::InitData::DeviceType> device_map = { static const std::map<std::string, cereal::InitData::DeviceType> device_map = {
{"tici", cereal::InitData::DeviceType::TICI},
{"tizi", cereal::InitData::DeviceType::TIZI}, {"tizi", cereal::InitData::DeviceType::TIZI},
{"mici", cereal::InitData::DeviceType::MICI} {"mici", cereal::InitData::DeviceType::MICI}
}; };
@@ -31,9 +29,6 @@ public:
return it->second; return it->second;
} }
static int get_voltage() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()); }
static int get_current() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str()); }
static std::string get_serial() { static std::string get_serial() {
static std::string serial(""); static std::string serial("");
if (serial.empty()) { if (serial.empty()) {
@@ -54,8 +49,7 @@ public:
static void set_ir_power(int percent) { static void set_ir_power(int percent) {
auto device = get_device_type(); auto device = get_device_type();
if (device == cereal::InitData::DeviceType::TICI || if (device == cereal::InitData::DeviceType::TIZI) {
device == cereal::InitData::DeviceType::TIZI) {
return; return;
} }
@@ -7,14 +7,14 @@ import time
from functools import cached_property, lru_cache from functools import cached_property, lru_cache
from pathlib import Path from pathlib import Path
from cereal import log from openpilot.cereal import log
from openpilot.common.utils import sudo_read, sudo_write from openpilot.common.utils import sudo_read, sudo_write
from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action
from openpilot.system.hardware.base import HardwareBase, LPABase, ThermalConfig, ThermalZone from openpilot.common.esim.base import LPABase
from openpilot.system.hardware.tici import iwlist from openpilot.common.hardware.base import HardwareBase, ThermalConfig, ThermalZone
from openpilot.system.hardware.tici.lpa import TiciLPA from openpilot.common.esim.lpa import TiciLPA
from openpilot.system.hardware.tici.pins import GPIO from openpilot.common.hardware.tici.pins import GPIO
from openpilot.system.hardware.tici.amplifier import Amplifier from openpilot.common.hardware.tici.amplifier import Amplifier
MODEM_STATE_PATH = "/dev/shm/modem" MODEM_STATE_PATH = "/dev/shm/modem"
@@ -147,9 +147,7 @@ class Tici(HardwareBase):
def get_sim_lpa(self) -> LPABase: def get_sim_lpa(self) -> LPABase:
return TiciLPA() return TiciLPA()
def get_imei(self, slot): def get_imei(self):
if slot != 0:
return ""
return self.get_modem_state().get('imei', '') return self.get_modem_state().get('imei', '')
def get_network_info(self): def get_network_info(self):
@@ -233,9 +231,6 @@ class Tici(HardwareBase):
return super().get_network_metered(network_type) return super().get_network_metered(network_type)
def get_modem_version(self):
return self.get_modem_state().get('modem_version') or None
def get_modem_temperatures(self): def get_modem_temperatures(self):
return self.get_modem_state().get('temperatures', []) return self.get_modem_state().get('temperatures', [])
@@ -246,7 +241,7 @@ class Tici(HardwareBase):
return (self.read_param_file("/sys/class/power_supply/bms/voltage_now", int) * self.read_param_file("/sys/class/power_supply/bms/current_now", int) / 1e12) return (self.read_param_file("/sys/class/power_supply/bms/voltage_now", int) * self.read_param_file("/sys/class/power_supply/bms/current_now", int) / 1e12)
def shutdown(self): def shutdown(self):
os.system("sudo poweroff") subprocess.run("sudo poweroff", shell=True)
def get_thermal_config(self): def get_thermal_config(self):
intake, exhaust, gnss, bottomSoc = None, None, None, None intake, exhaust, gnss, bottomSoc = None, None, None, None
@@ -313,6 +308,9 @@ class Tici(HardwareBase):
continue continue
gov = 'ondemand' if powersave_enabled else 'performance' gov = 'ondemand' if powersave_enabled else 'performance'
sudo_write(gov, f'/sys/devices/system/cpu/cpufreq/policy{n}/scaling_governor') sudo_write(gov, f'/sys/devices/system/cpu/cpufreq/policy{n}/scaling_governor')
if not powersave_enabled:
# cap max core freq to 1689 Mhz
sudo_write('1689600', f'/sys/devices/system/cpu/cpufreq/policy{n}/scaling_max_freq')
# *** IRQ config *** # *** IRQ config ***
@@ -337,7 +335,7 @@ class Tici(HardwareBase):
self.amplifier.initialize_configuration() self.amplifier.initialize_configuration()
# Allow hardwared to write engagement status to kmsg # Allow hardwared to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg") subprocess.run("sudo chmod a+w /dev/kmsg", shell=True)
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL # Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
gpio_init(GPIO.SOM_ST_IO, True) gpio_init(GPIO.SOM_ST_IO, True)
@@ -380,43 +378,13 @@ class Tici(HardwareBase):
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip() pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid]) subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
subprocess.call(["sudo", "taskset", "-pc", "3", pid]) subprocess.call(["sudo", "taskset", "-pc", "3", pid])
except subprocess.CalledProcessException as e: except subprocess.CalledProcessError as e:
print(str(e)) print(str(e))
def get_networks(self):
r = {}
wlan = iwlist.scan()
if wlan is not None:
r['wlan'] = wlan
lte_info = self.get_network_info()
if lte_info is not None:
extra = lte_info['extra']
# <state>,"LTE",<is_tdd>,<mcc>,<mnc>,<cellid>,<pcid>,<earfcn>,<freq_band_ind>,
# <ul_bandwidth>,<dl_bandwidth>,<tac>,<rsrp>,<rsrq>,<rssi>,<sinr>,<srxlev>
if 'LTE' in extra:
extra = extra.split(',')
try:
r['lte'] = [{
"mcc": int(extra[3]),
"mnc": int(extra[4]),
"cid": int(extra[5], 16),
"nmr": [{"pci": int(extra[6]), "earfcn": int(extra[7])}],
}]
except (ValueError, IndexError):
pass
return r
def get_modem_data_usage(self): def get_modem_data_usage(self):
ms = self.get_modem_state() ms = self.get_modem_state()
return ms.get('tx_bytes', -1), ms.get('rx_bytes', -1) return ms.get('tx_bytes', -1), ms.get('rx_bytes', -1)
def has_internal_panda(self):
return True
def reset_internal_panda(self): def reset_internal_panda(self):
gpio_init(GPIO.STM_RST_N, True) gpio_init(GPIO.STM_RST_N, True)
gpio_init(GPIO.STM_BOOT0, True) gpio_init(GPIO.STM_BOOT0, True)
@@ -3,7 +3,6 @@ import fcntl
import json import json
import logging import logging
import os import os
import serial
import signal import signal
import subprocess import subprocess
import tempfile import tempfile
@@ -13,6 +12,8 @@ from ipaddress import IPv4Address, AddressValueError
from enum import Enum from enum import Enum
from openpilot.common.serial import Serial
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
format="%(asctime)s.%(msecs)03d %(levelname)-7s modem: %(message)s", format="%(asctime)s.%(msecs)03d %(levelname)-7s modem: %(message)s",
@@ -96,7 +97,7 @@ class PPPSession:
def reset_data_port(): def reset_data_port():
"""Drop DTR on PPP_PORT so the modem terminates any stuck PPP session.""" """Drop DTR on PPP_PORT so the modem terminates any stuck PPP session."""
try: try:
with serial.Serial(PPP_PORT, 460800, timeout=1) as s: with Serial(PPP_PORT, baudrate=460800, timeout=1) as s:
s.dtr = False s.dtr = False
time.sleep(0.2) time.sleep(0.2)
s.dtr = True s.dtr = True
@@ -220,7 +221,7 @@ class Modem:
os.close(fd) os.close(fd)
return [] return []
try: try:
with serial.Serial(AT_PORT, 9600, timeout=5) as ser: with Serial(AT_PORT, baudrate=9600, timeout=5) as ser:
ser.reset_input_buffer() ser.reset_input_buffer()
ser.write((cmd + "\r").encode()) ser.write((cmd + "\r").encode())
lines = [] lines = []
@@ -505,7 +506,11 @@ class Modem:
except (AddressValueError, ValueError): except (AddressValueError, ValueError):
pass pass
if not dns_servers: if not dns_servers:
logging.warning(f"no cellular DNS servers reported by modem: {v!r}") dns_servers = [
"8.8.8.8", # Google
"1.1.1.1", # Cloudflare
]
logging.warning(f"no cellular DNS servers reported by modem: {v!r}; using fallback {dns_servers}")
return dns_servers return dns_servers
def _poll_byte_counters(self) -> dict: def _poll_byte_counters(self) -> dict:
@@ -0,0 +1 @@
@@ -1,11 +1,10 @@
import pytest import pytest
import time import time
import random
import subprocess import subprocess
from panda import Panda from panda import Panda
from openpilot.system.hardware import TICI, HARDWARE from openpilot.common.hardware import TICI, HARDWARE
from openpilot.system.hardware.tici.amplifier import Amplifier from openpilot.common.hardware.tici.amplifier import Amplifier
class TestAmplifier: class TestAmplifier:
@@ -57,7 +56,7 @@ class TestAmplifier:
time.sleep(0.1) time.sleep(0.1)
self.panda.set_siren(True) self.panda.set_siren(True)
time.sleep(random.randint(0, 5)) time.sleep(0.1)
amp = Amplifier(debug=True) amp = Amplifier(debug=True)
r = amp.initialize_configuration() r = amp.initialize_configuration()

Some files were not shown because too many files have changed in this diff Show More