diff --git a/.github/workflows/jenkins-pr-trigger.yaml b/.github/workflows/jenkins-pr-trigger.yaml new file mode 100644 index 0000000000..db1d524018 --- /dev/null +++ b/.github/workflows/jenkins-pr-trigger.yaml @@ -0,0 +1,45 @@ +name: jenkins scan + +on: + issue_comment: + types: [created, edited] + +jobs: + # TODO: gc old branches in a separate job in this workflow + scan-comments: + runs-on: ubuntu-latest + if: ${{ github.event.issue.pull_request }} + steps: + - name: Check for trigger phrase + id: check_comment + uses: actions/github-script@v7 + with: + script: | + const triggerPhrase = "trigger-jenkins"; + const comment = context.payload.comment.body; + const commenter = context.payload.comment.user.login; + + const { data: permissions } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: commenter + }); + + const hasWriteAccess = permissions.permission === 'write' || permissions.permission === 'admin'; + + return (hasWriteAccess && comment.includes(triggerPhrase)); + result-encoding: json + + - name: Checkout repository + if: steps.check_comment.outputs.result == 'true' + uses: actions/checkout@v4 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + + - name: Push to tmp-jenkins branch + if: steps.check_comment.outputs.result == 'true' + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b tmp-jenkins-${{ github.event.issue.number }} + GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }} diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 42c1dbb5eb..1154948ace 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -338,6 +338,7 @@ jobs: }) create_ui_report: + # This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml name: Create UI Report runs-on: ubuntu-latest steps: @@ -355,55 +356,5 @@ jobs: - name: Upload Test Report uses: actions/upload-artifact@v4 with: - name: report-${{ inputs.run_number || '1' }} - path: selfdrive/ui/tests/test_ui/report_${{ inputs.run_number || '1' }} - - name: Get changes to selfdrive/ui - if: ${{ github.event_name == 'pull_request' }} - id: changed-files - uses: tj-actions/changed-files@v44 - with: - files: | - selfdrive/ui/** - - name: Checkout ci-artifacts - if: ${{ github.event_name == 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }} - uses: actions/checkout@v4 - with: - repository: commaai/ci-artifacts - ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} - path: ${{ github.workspace }}/ci-artifacts - ref: master - - name: Push Screenshots - if: ${{ github.event_name == 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }} - working-directory: ${{ github.workspace }}/ci-artifacts - run: | - git checkout -b openpilot/pr-${{ github.event.pull_request.number }} - git config user.name "GitHub Actions Bot" - git config user.email "<>" - sudo mv ${{ github.workspace }}/selfdrive/ui/tests/test_ui/report_1/screenshots/* . - git add . - git commit -m "screenshots for PR #${{ github.event.pull_request.number }}" - git push origin openpilot/pr-${{ github.event.pull_request.number }} --force - - name: Comment Screenshots on PR - if: ${{ github.event_name == 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: | - - ## UI Screenshots - - - - - - - - - - - - - -
- comment_tag: run_id_screenshots - pr_number: ${{ github.event.pull_request.number }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + name: report-${{ github.event.number }} + path: selfdrive/ui/tests/test_ui/report_1/screenshots diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index b64963fb60..9085556392 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -73,12 +73,6 @@ jobs: scons-${{ runner.arch }}-ubuntu2004 - name: Building openpilot run: uv run scons -u -j$(nproc) - - name: Saving scons cache - uses: actions/cache/save@v4 - if: github.ref == 'refs/heads/master' - with: - path: /tmp/scons_cache - key: scons-${{ runner.arch }}-ubuntu2004-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} devcontainer: name: devcontainer diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml new file mode 100644 index 0000000000..bc19e23778 --- /dev/null +++ b/.github/workflows/ui_preview.yaml @@ -0,0 +1,91 @@ +name: "ui preview" +on: + pull_request_target: + types: [assigned, opened, synchronize, reopened, edited] + branches: + - 'master' + paths: + - 'selfdrive/ui/**' + +env: + UI_JOB_NAME: "Create UI Report" + +jobs: + preview: + if: github.repository == 'commaai/openpilot' + name: preview + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: write + actions: read + steps: + - name: Waiting for ui test to start + run: sleep 30 + + - name: Wait for ui report + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ github.event.pull_request.head.sha }} + check-name: ${{ env.UI_JOB_NAME }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + allowed-conclusions: success + wait-interval: 20 + + - name: Get workflow run ID + id: get_run_id + run: | + echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT + + - name: Checkout ci-artifacts + uses: actions/checkout@v4 + with: + repository: commaai/ci-artifacts + ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} + path: ${{ github.workspace }}/ci-artifacts + ref: master + + - name: Download artifact + id: download-artifact + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + run_id: ${{ steps.get_run_id.outputs.run_id }} + search_artifacts: true + name: report-${{ github.event.number }} + path: ${{ github.workspace }}/ci-artifacts + + - name: Push Screenshots + working-directory: ${{ github.workspace }}/ci-artifacts + run: | + git checkout -b openpilot/pr-${{ github.event.number }} + git config user.name "GitHub Actions Bot" + git config user.email "<>" + git add ${{ github.workspace }}/ci-artifacts/* + git commit -m "screenshots for PR #${{ github.event.number }}" + git push origin openpilot/pr-${{ github.event.number }} --force + + - name: Comment Screenshots on PR + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + + ## UI Screenshots + + + + + + + + + + + + + +
+ comment_tag: run_id_screenshots + pr_number: ${{ github.event.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index db74ee6a1a..5afaf6e7d6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ To start using openpilot in a car To use openpilot in a car, you need four things: 1. **Supported Device:** a comma 3/3X, available at [comma.ai/shop](https://comma.ai/shop/comma-3x). 2. **Software:** The setup procedure for the comma 3/3X allows users to enter a URL for custom software. Use the URL `openpilot.comma.ai` to install the release version. -3. **Supported Car:** Ensure that you have one of [the 250+ supported cars](docs/CARS.md). +3. **Supported Car:** Ensure that you have one of [the 275+ supported cars](docs/CARS.md). 4. **Car Harness:** You will also need a [car harness](https://comma.ai/shop/car-harness) to connect your comma 3/3X to your car. We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup). Note that it's possible to run openpilot on [other hardware](https://blog.comma.ai/self-driving-car-for-free/), although it's not plug-and-play. diff --git a/SConstruct b/SConstruct index 0c4dbee18e..1f83b1e43d 100644 --- a/SConstruct +++ b/SConstruct @@ -102,7 +102,6 @@ if arch == "larch64": libpath = [ "/usr/local/lib", - "/usr/lib", "/system/vendor/lib64", f"#third_party/acados/{arch}/lib", ] diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index e4841c705f..efeb76c4a8 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -22,22 +22,26 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, long_control_state = LongCtrlState.off else: - if long_control_state in (LongCtrlState.off, LongCtrlState.pid): - long_control_state = LongCtrlState.pid - if stopping_condition: + if long_control_state == LongCtrlState.off: + if not starting_condition: long_control_state = LongCtrlState.stopping + else: + if starting_condition and CP.startingState: + long_control_state = LongCtrlState.starting + else: + long_control_state = LongCtrlState.pid + elif long_control_state == LongCtrlState.stopping: if starting_condition and CP.startingState: long_control_state = LongCtrlState.starting elif starting_condition: long_control_state = LongCtrlState.pid - elif long_control_state == LongCtrlState.starting: + elif long_control_state in [LongCtrlState.starting, LongCtrlState.pid]: if stopping_condition: long_control_state = LongCtrlState.stopping elif started_condition: long_control_state = LongCtrlState.pid - return long_control_state class LongControl: diff --git a/selfdrive/controls/tests/test_longcontrol.py b/selfdrive/controls/tests/test_longcontrol.py new file mode 100644 index 0000000000..ab50810d89 --- /dev/null +++ b/selfdrive/controls/tests/test_longcontrol.py @@ -0,0 +1,56 @@ +from cereal import car +from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState, long_control_state_trans + + + + +class TestLongControlStateTransition: + + def test_stay_stopped(self): + CP = car.CarParams.new_message() + active = True + current_state = LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=True, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=False, brake_pressed=True, cruise_standstill=False) + assert next_state == LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=False, brake_pressed=False, cruise_standstill=True) + assert next_state == LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=1.0, + should_stop=False, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.pid + active = False + next_state = long_control_state_trans(CP, active, current_state, v_ego=1.0, + should_stop=False, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.off + +def test_engage(): + CP = car.CarParams.new_message() + active = True + current_state = LongCtrlState.off + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=True, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=False, brake_pressed=True, cruise_standstill=False) + assert next_state == LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=False, brake_pressed=False, cruise_standstill=True) + assert next_state == LongCtrlState.stopping + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=False, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.pid + +def test_starting(): + CP = car.CarParams.new_message(startingState=True, vEgoStarting=0.5) + active = True + current_state = LongCtrlState.starting + next_state = long_control_state_trans(CP, active, current_state, v_ego=0.1, + should_stop=False, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.starting + next_state = long_control_state_trans(CP, active, current_state, v_ego=1.0, + should_stop=False, brake_pressed=False, cruise_standstill=False) + assert next_state == LongCtrlState.pid diff --git a/selfdrive/modeld/models/commonmodel_pyx.pyx b/selfdrive/modeld/models/commonmodel_pyx.pyx index e292bb0d2d..ecbe644e09 100644 --- a/selfdrive/modeld/models/commonmodel_pyx.pyx +++ b/selfdrive/modeld/models/commonmodel_pyx.pyx @@ -1,5 +1,5 @@ # distutils: language = c++ -# cython: c_string_encoding=ascii +# cython: c_string_encoding=ascii, language_level=3 import numpy as np cimport numpy as cnp diff --git a/selfdrive/modeld/runners/runmodel_pyx.pyx b/selfdrive/modeld/runners/runmodel_pyx.pyx index e1b201a6a9..12b8ec10ff 100644 --- a/selfdrive/modeld/runners/runmodel_pyx.pyx +++ b/selfdrive/modeld/runners/runmodel_pyx.pyx @@ -1,5 +1,5 @@ # distutils: language = c++ -# cython: c_string_encoding=ascii +# cython: c_string_encoding=ascii, language_level=3 from libcpp.string cimport string diff --git a/selfdrive/modeld/runners/snpemodel_pyx.pyx b/selfdrive/modeld/runners/snpemodel_pyx.pyx index c3b2b7e9bd..f83b7c8cff 100644 --- a/selfdrive/modeld/runners/snpemodel_pyx.pyx +++ b/selfdrive/modeld/runners/snpemodel_pyx.pyx @@ -1,5 +1,5 @@ # distutils: language = c++ -# cython: c_string_encoding=ascii +# cython: c_string_encoding=ascii, language_level=3 import os from libcpp cimport bool diff --git a/selfdrive/modeld/runners/thneedmodel_pyx.pyx b/selfdrive/modeld/runners/thneedmodel_pyx.pyx index 53487afa1b..6f8fdd255f 100644 --- a/selfdrive/modeld/runners/thneedmodel_pyx.pyx +++ b/selfdrive/modeld/runners/thneedmodel_pyx.pyx @@ -1,5 +1,5 @@ # distutils: language = c++ -# cython: c_string_encoding=ascii +# cython: c_string_encoding=ascii, language_level=3 from libcpp cimport bool from libcpp.string cimport string diff --git a/selfdrive/ui/qt/qt_window.cc b/selfdrive/ui/qt/qt_window.cc index f71cea04e9..8d3d7cf72e 100644 --- a/selfdrive/ui/qt/qt_window.cc +++ b/selfdrive/ui/qt/qt_window.cc @@ -18,7 +18,9 @@ void setMainWindow(QWidget *w) { wl_surface *s = reinterpret_cast(native->nativeResourceForWindow("surface", w->windowHandle())); wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270); wl_surface_commit(s); - w->showFullScreen(); + + w->setWindowState(Qt::WindowFullScreen); + w->setVisible(true); // ensure we have a valid eglDisplay, otherwise the ui will silently fail void *egl = native->nativeResourceForWindow("egldisplay", w->windowHandle()); diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 47ea5ded4d..7c1f8e44e7 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -469,6 +469,16 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num enabled = enabled_; if (!enabled) return; + if (!openSensor()) { + return; + } + + configISP(); + configCSIPHY(); + linkDevices(); +} + +bool CameraState::openSensor() { sensor_fd = open_v4l_by_name_and_index("cam-sensor-driver", camera_num); assert(sensor_fd >= 0); LOGD("opened sensor for %d", camera_num); @@ -493,7 +503,7 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num !init_sensor_lambda(new OS04C10)) { LOGE("** sensor %d FAILED bringup, disabling", camera_num); enabled = false; - return; + return false; } LOGD("-- Probing sensor %d success", camera_num); @@ -512,7 +522,10 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num LOG("-- Configuring sensor"); sensors_i2c(ci->init_reg_array.data(), ci->init_reg_array.size(), CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, ci->data_word); + return true; +} +void CameraState::configISP() { // NOTE: to be able to disable road and wide road, we still have to configure the sensor over i2c // If you don't do this, the strobe GPIO is an output (even in reset it seems!) if (!enabled) return; @@ -570,6 +583,13 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num isp_dev_handle = *isp_dev_handle_; LOGD("acquire isp dev"); + // config ISP + alloc_w_mmu_hdl(multi_cam_state->video0_fd, 984480, (uint32_t*)&buf0_handle, 0x20, CAM_MEM_FLAG_HW_READ_WRITE | CAM_MEM_FLAG_KMD_ACCESS | + CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, multi_cam_state->device_iommu, multi_cam_state->cdm_iommu); + config_isp(0, 0, 1, buf0_handle, 0); +} + +void CameraState::configCSIPHY() { csiphy_fd = open_v4l_by_name_and_index("cam-csiphy-driver", camera_num); assert(csiphy_fd >= 0); LOGD("opened csiphy for %d", camera_num); @@ -580,11 +600,6 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num csiphy_dev_handle = *csiphy_dev_handle_; LOGD("acquire csiphy dev"); - // config ISP - alloc_w_mmu_hdl(multi_cam_state->video0_fd, 984480, (uint32_t*)&buf0_handle, 0x20, CAM_MEM_FLAG_HW_READ_WRITE | CAM_MEM_FLAG_KMD_ACCESS | - CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, multi_cam_state->device_iommu, multi_cam_state->cdm_iommu); - config_isp(0, 0, 1, buf0_handle, 0); - // config csiphy LOG("-- Config CSI PHY"); { @@ -612,15 +627,16 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num int ret_ = device_config(csiphy_fd, session_handle, csiphy_dev_handle, cam_packet_handle); assert(ret_ == 0); } +} - // link devices +void CameraState::linkDevices() { LOG("-- Link devices"); struct cam_req_mgr_link_info req_mgr_link_info = {0}; req_mgr_link_info.session_hdl = session_handle; req_mgr_link_info.num_devices = 2; req_mgr_link_info.dev_hdls[0] = isp_dev_handle; req_mgr_link_info.dev_hdls[1] = sensor_dev_handle; - ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_LINK, &req_mgr_link_info, sizeof(req_mgr_link_info)); + int ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_LINK, &req_mgr_link_info, sizeof(req_mgr_link_info)); link_handle = req_mgr_link_info.link_hdl; LOGD("link: %d session: 0x%X isp: 0x%X sensors: 0x%X link: 0x%X", ret, session_handle, isp_dev_handle, sensor_dev_handle, link_handle); diff --git a/system/camerad/cameras/camera_qcom2.h b/system/camerad/cameras/camera_qcom2.h index 0b15c9c3f0..b702d1bb69 100644 --- a/system/camerad/cameras/camera_qcom2.h +++ b/system/camerad/cameras/camera_qcom2.h @@ -86,6 +86,11 @@ public: void sensors_i2c(const struct i2c_random_wr_payload* dat, int len, int op_code, bool data_word); private: + bool openSensor(); + void configISP(); + void configCSIPHY(); + void linkDevices(); + // for debugging Params params; }; diff --git a/tools/install_python_dependencies.sh b/tools/install_python_dependencies.sh index 276de58db7..a90462888f 100755 --- a/tools/install_python_dependencies.sh +++ b/tools/install_python_dependencies.sh @@ -34,7 +34,7 @@ update_uv # TODO: remove --no-cache once this is fixed: https://github.com/astral-sh/uv/issues/4378 echo "installing python packages..." -uv --no-cache sync --all-extras +uv --no-cache sync --frozen --all-extras source .venv/bin/activate echo "PYTHONPATH=${PWD}" > $ROOT/.env diff --git a/tools/op.sh b/tools/op.sh new file mode 100755 index 0000000000..111a06e81c --- /dev/null +++ b/tools/op.sh @@ -0,0 +1,313 @@ +#!/bin/bash + +RED='\033[0;31m' +GREEN='\033[0;32m' +UNDERLINE='\033[4m' +BOLD='\033[1m' +NC='\033[0m' + +function op_first_install() { + (set -e + + echo "Installing op system-wide..." + RC_FILE="${HOME}/.$(basename ${SHELL})rc" + if [ "$(uname)" == "Darwin" ] && [ $SHELL == "/bin/bash" ]; then + RC_FILE="$HOME/.bash_profile" + fi + op_run_command printf "\nalias op='source "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )/op.sh" \"\$@\"'\n" >> $RC_FILE + echo -e " ↳ [${GREEN}✔${NC}] op installed successfully. Open a new shell to use it.\n" + + ) +} + +# be default, assume openpilot dir is in current directory +OPENPILOT_ROOT=$(pwd) +function op_check_openpilot_dir() { + while [[ "$OPENPILOT_ROOT" != '/' ]]; + do + if find "$OPENPILOT_ROOT/launch_openpilot.sh" -maxdepth 1 -mindepth 1 &> /dev/null; then + return 0 + fi + OPENPILOT_ROOT="$(readlink -f "$OPENPILOT_ROOT/"..)" + done + + echo "openpilot directory not found! Make sure that you are inside openpilot" + echo "directory or specify one with the --dir option!" + return 1 +} + +function op_run_command() { + CMD="$@" + echo -e "${BOLD}Running:${NC} $CMD" + if [[ -z "$DRY" ]]; then + $CMD + fi +} + +function op_check_git() { + (set -e + + cd $OPENPILOT_ROOT + + echo "Checking for git..." + if ! command -v "git" > /dev/null 2>&1; then + echo -e " ↳ [${RED}✗${NC}] git not found on your system!" + return 1 + else + echo -e " ↳ [${GREEN}✔${NC}] git found on your system.\n" + fi + + echo "Checking for git lfs files..." + if [[ $(file -b $(git lfs ls-files -n | grep "\.so" | head -n 1)) == "ASCII text" ]]; then + echo -e " ↳ [${RED}✗${NC}] git lfs files not found! Run git lfs pull" + return 1 + else + echo -e " ↳ [${GREEN}✔${NC}] git lfs files found on your system.\n" + fi + + echo "Checking for git submodules..." + if $(git submodule foreach --quiet --recursive 'return 1' 2&> /dev/null); then + echo -e " ↳ [${RED}✗${NC}] git submodules not found! Run 'git submodule update --init --recursive'" + return 1 + else + echo -e " ↳ [${GREEN}✔${NC}] git submodules found on your system.\n" + fi + + ) +} + +function op_check_os() { + (set -e + + echo "Checking for compatible os version..." + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + + if [ -f "/etc/os-release" ]; then + source /etc/os-release + case "$VERSION_CODENAME" in + "jammy" | "kinetic" | "noble" | "focal") + echo -e " ↳ [${GREEN}✔${NC}] Ubuntu $VERSION_CODENAME detected.\n" + ;; + * ) + echo -e " ↳ [${RED}✗${NC}] Incompatible Ubuntu version $VERSION_CODENAME detected!" + return 1 + ;; + esac + else + echo -e " ↳ [${RED}✗${NC}] No /etc/os-release on your system. Make sure you're running on Ubuntu, or similar!" + return 1 + fi + + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo -e " ↳ [${GREEN}✔${NC}] macos detected.\n" + else + echo -e " ↳ [${RED}✗${NC}] OS type $OSTYPE not supported!" + return 1 + fi + + ) +} + +function op_check_python() { + (set -e + + echo "Checking for compatible python version..." + export REQUIRED_PYTHON_VERSION=$(grep "requires-python" pyproject.toml | cut -d= -f3- | tr -d '"' | tr -d ' ') + if ! command -v "python3" > /dev/null 2>&1; then + echo -e " ↳ [${RED}✗${NC}] python3 not found on your system. You need python version at least $REQUIRED_PYTHON_VERSION to continue!" + return 1 + else + if $(python3 -c "import sys; quit(not sys.version_info >= tuple(map(int, \"$REQUIRED_PYTHON_VERSION\".split('.'))))"); then + echo -e " ↳ [${GREEN}✔${NC}] $(python3 --version) detected.\n" + else + echo -e " ↳ [${RED}✗${NC}] You need python version at least $REQUIRED_PYTHON_VERSION to continue!" + return 1 + fi + fi + + ) +} + +# this must be run in the same shell as the user calling "op" +function op_venv() { + op_check_openpilot_dir || return 1 + op_run_command source $OPENPILOT_ROOT/.venv/bin/activate || (echo -e "\nCan't activate venv. Have you ran 'op install' ?" && return 1) +} + +function op_check() { + (set -e + + op_check_openpilot_dir + cd $OPENPILOT_ROOT + op_check_git + op_check_os + op_check_python + + ) +} + +function op_run() { + (set -e + + op_venv + cd $OPENPILOT_ROOT + + op_run_command $OPENPILOT_ROOT/launch_openpilot.sh + + ) +} + +function op_install() { + (set -e + + op_check_openpilot_dir + cd $OPENPILOT_ROOT + + op_check_os + op_check_python + + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + $OPENPILOT_ROOT/tools/ubuntu_setup.sh + elif [[ "$OSTYPE" == "darwin"* ]]; then + $OPENPILOT_ROOT/tools/mac_setup.sh + fi + + git submodule update --init --recursive + git lfs pull + + ) +} + +function op_build() { + (set -e + + op_venv + cd $OPENPILOT_ROOT + + op_run_command scons $@ + + ) +} + +function op_juggle() { + (set -e + + op_venv + cd $OPENPILOT_ROOT + + op_run_command $OPENPILOT_ROOT/tools/plotjuggler/juggle.py $@ + + ) +} + +function op_linter() { + (set -e + + op_venv + cd $OPENPILOT_ROOT + + op_run_command pre-commit run --all $@ + + ) +} + +function op_replay() { + (set -e + op_check_openpilot_dir + cd $OPENPILOT_ROOT + + op_run_command $OPENPILOT_ROOT/tools/replay/replay $@ + + ) +} + +function op_default() { + echo "An openpilot helper" + echo "" + echo -e "${BOLD}${UNDERLINE}Description:${NC}" + echo " op is your entry point for all things related to openpilot development." + echo " op is only a wrapper for scripts, tools and commands already existing." + echo " op will always show you what it will run on your system." + echo "" + echo " op will try to find your openpilot directory in the following order:" + echo " 1: use the directory specified with the --dir option" + echo " 2: use the current working directory" + echo " 3: go up the file tree non-recursively" + echo "" + echo -e "${BOLD}${UNDERLINE}Usage:${NC} op [OPTIONS] " + echo "" + echo -e "${BOLD}${UNDERLINE}Commands:${NC}" + echo -e " ${BOLD}venv${NC} Activate the virtual environment" + echo -e " ${BOLD}check${NC} Check system requirements (git, os, python) to start using openpilot" + echo -e " ${BOLD}install${NC} Install requirements to use openpilot" + echo -e " ${BOLD}build${NC} Build openpilot" + echo -e " ${BOLD}run${NC} Run openpilot" + echo -e " ${BOLD}juggle${NC} Run Plotjuggler" + echo -e " ${BOLD}replay${NC} Run replay" + echo -e " ${BOLD}linter${NC} Run all the pre-commit checks" + echo -e " ${BOLD}help${NC} Show this message" + echo -e " ${BOLD}--install${NC} Install this tool system wide" + echo "" + echo -e "${BOLD}${UNDERLINE}Options:${NC}" + echo -e " ${BOLD}-d, --dir${NC}" + echo " Specify the openpilot directory you want to use" + echo -e " ${BOLD}--dry${NC}" + echo " Don't actually run anything, just print what would be" + echo "" + echo -e "${BOLD}${UNDERLINE}Examples:${NC}" + echo " op --dir /tmp/openpilot check" + echo " Run the check command on openpilot located in /tmp/openpilot" + echo "" + echo " op juggle --install" + echo " Install plotjuggler in the openpilot located in your current" + echo " working directory" + echo "" + echo " op --dir /tmp/openpilot build -j4" + echo " Run the build command on openpilot located in /tmp/openpilot" + echo " on 4 cores" +} + + +function _op() { + # parse Options + case $1 in + -d | --dir ) shift 1; OPENPILOT_ROOT="$1"; shift 1 ;; + --dry ) shift 1; DRY="1" ;; + esac + + # parse Commands + case $1 in + venv ) shift 1; op_venv "$@" ;; + check ) shift 1; op_check "$@" ;; + install ) shift 1; op_install "$@" ;; + build ) shift 1; op_build "$@" ;; + run ) shift 1; op_run "$@" ;; + juggle ) shift 1; op_juggle "$@" ;; + linter ) shift 1; op_linter "$@" ;; + replay ) shift 1; op_replay "$@" ;; + --install ) shift 1; op_first_install "$@" ;; + * ) op_default "$@" ;; + esac +} + +_op $@ + +# remove from env +unset -f _op +unset -f op_check +unset -f op_install +unset -f op_build +unset -f op_run +unset -f op_juggle +unset -f op_venv +unset -f op_check_openpilot_dir +unset -f op_check_git +unset -f op_check_python +unset -f op_check_os +unset -f op_first_install +unset -f op_default +unset -f op_run_command +unset -f op_linter +unset -f op_replay +unset DRY +unset OPENPILOT_ROOT diff --git a/tools/rerun/run.py b/tools/rerun/run.py index 421785a2d5..6d9554038c 100755 --- a/tools/rerun/run.py +++ b/tools/rerun/run.py @@ -18,6 +18,12 @@ RR_TIMELINE_NAME = "Timeline" RR_WIN = "rerun_test" +""" +Relevant upstream Rerun issues: +- large time series: https://github.com/rerun-io/rerun/issues/5967 +- loading videos directly: https://github.com/rerun-io/rerun/issues/6532 +""" + class Rerunner: def __init__(self, route, segment_range, camera_config, enabled_services): self.enabled_services = [s.lower() for s in enabled_services]