From e24271aa2b1a24eb6586b75eadf02f3358fa74e4 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Mon, 8 Jul 2024 19:10:26 +0200 Subject: [PATCH 01/12] Processor definition check for __APPLE__ has a typo on replay (#32930) (cherry picked from commit 3c74ad145e0145429d782219ac255a26aa2f6135) --- tools/replay/util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/util.cc b/tools/replay/util.cc index a08b3b3d5e..aaf73749d5 100644 --- a/tools/replay/util.cc +++ b/tools/replay/util.cc @@ -323,7 +323,7 @@ void precise_nano_sleep(int64_t nanoseconds, std::atomic &should_exit) { req.tv_sec = nanoseconds / 1000000000; req.tv_nsec = nanoseconds % 1000000000; while (!should_exit) { -#ifdef __APPLE_ +#ifdef __APPLE__ int ret = nanosleep(&req, &rem); if (ret == 0 || errno != EINTR) break; From 1fb0cb59a61bcbee96d6b3e287979f9545a0d38a Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 16 Jul 2024 02:59:02 +0000 Subject: [PATCH 02/12] Scons: Build sunnypilot elements with added GPG keys --- .gitlab-ci.yml | 2 ++ SConstruct | 47 ++++++++++++++++++++++++++++++ selfdrive/ui/SConscript | 6 ++++ selfdrive/ui/sunnypilot/SConscript | 10 +++++++ 4 files changed, 65 insertions(+) create mode 100644 selfdrive/ui/sunnypilot/SConscript diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 21958355c5..de50202454 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -129,6 +129,8 @@ build: --exclude='**/selfdrive/ui/**/*.h' --exclude='**/selfdrive/ui/qt/offroad/sunnypilot/' --exclude='**/.git/' + --exclude='**/SConstruct' + --exclude='**/SConscript' --delete-excluded --chown=comma:comma ${BUILD_DIR}/ ${OUTPUT_DIR}/ diff --git a/SConstruct b/SConstruct index da70e4e587..13da37c7a8 100644 --- a/SConstruct +++ b/SConstruct @@ -7,6 +7,8 @@ import numpy as np import SCons.Errors +from openpilot.common.basedir import BASEDIR + SCons.Warnings.warningAsException(True) # pending upstream fix - https://github.com/SCons/scons/issues/4461 @@ -16,6 +18,45 @@ TICI = os.path.isfile('/TICI') AGNOS = TICI UBUNTU_FOCAL = int(subprocess.check_output('[ -f /etc/os-release ] && . /etc/os-release && [ "$ID" = "ubuntu" ] && [ "$VERSION_ID" = "20.04" ] && echo 1 || echo 0', shell=True, encoding='utf-8').rstrip()) Export('UBUNTU_FOCAL') +_DEBUG = False + +def is_internal_developer(debug=False): + def collect_required_gpg_key_ids(keys_dir): + try: + key_ids = [f.split('.')[0] for f in os.listdir(keys_dir) if f.endswith(".gpg")] + if debug: + print(f"SP: Required GPG key IDs: {key_ids}") + return key_ids + except OSError as e: + if debug: + print(f"SP: Failed to read GPG key IDs from {keys_dir}. Error: {e}") + return [] + + def is_key_available(required_gpg_key_ids): + for key_id in required_gpg_key_ids: + try: + result = subprocess.check_output(['gpg', '--list-keys', key_id], stderr=subprocess.STDOUT) + if key_id in result.decode(): + if debug: + print(f"SP: GPG key {key_id} is available.") + return True + except subprocess.CalledProcessError as e: + if debug: + print(f"SP: Failed to list GPG key {key_id}. Error:", e.output.decode().strip()) + return False + + keys_dir = os.path.join(BASEDIR, ".git-crypt/keys/default/0") + required_gpg_key_ids = collect_required_gpg_key_ids(keys_dir) + + sunnypilot = is_key_available(required_gpg_key_ids) + + if sunnypilot: + print("SP: Confirmed sunnypilot internal developer.") + print("SP: Loading sunnypilot elements ...") + elif debug: + print("SP: None of the required GPG keys are available.") + + return sunnypilot Decider('MD5-timestamp') @@ -72,6 +113,12 @@ AddOption('--minimal', default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') +AddOption('--sunnypilot', + action='store_true', + dest='sunnypilot', + default=is_internal_developer(_DEBUG), # check if the current user is a sunnypilot developer + help='build sunnypilot elements and other sunnypilot-specific items that are meant for internal development') + ## Architecture name breakdown (arch) ## - larch64: linux tici aarch64 ## - aarch64: linux pc aarch64 diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e1233b5cbc..f4f5a13919 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -17,6 +17,12 @@ if arch == "Darwin": # FIXME: remove this once we're on 5.15 (24.04) qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] +sp_widgets_src = [] +sp_qt_src = [] +if GetOption('sunnypilot'): + SConscript(['sunnypilot/SConscript']) + Import('sp_widgets_src', 'sp_qt_src') + qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript new file mode 100644 index 0000000000..4431fc3ea0 --- /dev/null +++ b/selfdrive/ui/sunnypilot/SConscript @@ -0,0 +1,10 @@ +widgets_src = [] + +network_src = [] + +qt_src = [] + +sp_widgets_src = widgets_src + network_src +sp_qt_src = qt_src + +Export('sp_widgets_src', 'sp_qt_src') From fb04ddc9a1090a08cb8da8c7c40e91f11d2b098e Mon Sep 17 00:00:00 2001 From: Jaosn Wen Date: Tue, 16 Jul 2024 00:07:29 -0400 Subject: [PATCH 03/12] Scons: Set `SUNNYPILOT` to `CPPDEFINES` --- selfdrive/ui/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index f4f5a13919..99ec3c161d 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -43,7 +43,7 @@ widgets_src += ["qt/offroad/sunnypilot/display_settings.cc", "qt/offroad/sunnypi widgets_src += ["qt/network/sunnylink/sunnylink_client.cc", "qt/network/sunnylink/services/base_device_service.cc", "qt/network/sunnylink/services/role_service.cc", "qt/network/sunnylink/services/user_service.cc"] -qt_env['CPPDEFINES'] = [] +qt_env['CPPDEFINES'] = ["SUNNYPILOT"] if GetOption('sunnypilot') else [] if maps: base_libs += ['QMapLibre'] widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc", From e994c2cb9634480ba43cdd89c1c84035707911c8 Mon Sep 17 00:00:00 2001 From: Jaosn Wen Date: Tue, 16 Jul 2024 00:20:45 -0400 Subject: [PATCH 04/12] Scons: Split sunnypilot/Sconscript --- selfdrive/ui/SConscript | 20 +++---------------- selfdrive/ui/sunnypilot/SConscript | 32 +++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 99ec3c161d..3d49b3df14 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,6 @@ import os import json -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations', 'UBUNTU_FOCAL') +Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -28,20 +28,7 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] - -widgets_src += ["qt/offroad/sunnypilot/display_settings.cc", "qt/offroad/sunnypilot/sunnypilot_settings.cc", - "qt/offroad/sunnypilot/vehicle_settings.cc", "qt/offroad/sunnypilot/visuals_settings.cc", - "qt/offroad/sunnypilot/trips_settings.cc", "qt/offroad/sunnypilot/mads_settings.cc", - "qt/offroad/sunnypilot/lane_change_settings.cc", "qt/offroad/sunnypilot/speed_limit_control_settings.cc", - "qt/offroad/sunnypilot/monitoring_settings.cc", "qt/offroad/sunnypilot/osm_settings.cc", - "qt/offroad/sunnypilot/custom_offsets_settings.cc", "qt/widgets/sunnypilot/drive_stats.cc", - "qt/offroad/sunnypilot/software_settings_sp.cc", "qt/offroad/sunnypilot/models_fetcher.cc", - "qt/offroad/sunnypilot/speed_limit_warning_settings.cc", "qt/offroad/sunnypilot/speed_limit_policy_settings.cc", - "qt/offroad/sunnypilot/sunnylink_settings.cc"] - -widgets_src += ["qt/network/sunnylink/sunnylink_client.cc", "qt/network/sunnylink/services/base_device_service.cc", - "qt/network/sunnylink/services/role_service.cc", "qt/network/sunnylink/services/user_service.cc"] + "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src qt_env['CPPDEFINES'] = ["SUNNYPILOT"] if GetOption('sunnypilot') else [] if maps: @@ -59,8 +46,7 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc", "qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", - "qt/onroad_settings.cc", "qt/onroad_settings_panel.cc"] + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc"] + sp_qt_src # build translation files with open(File("translations/languages.json").abspath) as f: diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 4431fc3ea0..9290fcea58 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -1,8 +1,34 @@ -widgets_src = [] +widgets_src = [ + "qt/offroad/sunnypilot/custom_offsets_settings.cc", + "qt/offroad/sunnypilot/display_settings.cc", + "qt/offroad/sunnypilot/lane_change_settings.cc", + "qt/offroad/sunnypilot/mads_settings.cc", + "qt/offroad/sunnypilot/models_fetcher.cc", + "qt/offroad/sunnypilot/monitoring_settings.cc", + "qt/offroad/sunnypilot/osm_settings.cc", + "qt/offroad/sunnypilot/software_settings_sp.cc", + "qt/offroad/sunnypilot/speed_limit_control_settings.cc", + "qt/offroad/sunnypilot/speed_limit_policy_settings.cc", + "qt/offroad/sunnypilot/speed_limit_warning_settings.cc", + "qt/offroad/sunnypilot/sunnypilot_settings.cc", + "qt/offroad/sunnypilot/sunnylink_settings.cc", + "qt/offroad/sunnypilot/trips_settings.cc", + "qt/offroad/sunnypilot/vehicle_settings.cc", + "qt/offroad/sunnypilot/visuals_settings.cc", + "qt/widgets/sunnypilot/drive_stats.cc" +] -network_src = [] +network_src = [ + "qt/network/sunnylink/services/base_device_service.cc", + "qt/network/sunnylink/services/role_service.cc", + "qt/network/sunnylink/services/user_service.cc", + "qt/network/sunnylink/sunnylink_client.cc" +] -qt_src = [] +qt_src = [ + "qt/onroad_settings.cc", + "qt/onroad_settings_panel.cc" +] sp_widgets_src = widgets_src + network_src sp_qt_src = qt_src From 05536bf4395ea80ae04f3b32a267c0ca99e1de42 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Jul 2024 13:06:34 +0000 Subject: [PATCH 05/12] Driving Model Selector v5: Bug fixes --- selfdrive/modeld/custom_model_metadata.py | 4 ++-- selfdrive/modeld/fill_model_msg.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/selfdrive/modeld/custom_model_metadata.py b/selfdrive/modeld/custom_model_metadata.py index 360f7046ad..0469bd189c 100644 --- a/selfdrive/modeld/custom_model_metadata.py +++ b/selfdrive/modeld/custom_model_metadata.py @@ -42,14 +42,14 @@ class CustomModelMetadata: self.params: Params = params self.generation: ModelGeneration = self.read_model_generation_param() - self.capabilities: int = self.get_model_capabilities() + self.capabilities: ModelCapabilities = self.get_model_capabilities() self.valid: bool = self.params.get_bool("CustomDrivingModel") and not SIMULATION and \ self.capabilities != ModelCapabilities.Default def read_model_generation_param(self) -> ModelGeneration: return int(self.params.get('DrivingModelGeneration') or ModelGeneration.default) - def get_model_capabilities(self) -> int: + def get_model_capabilities(self) -> ModelCapabilities: """Returns the model capabilities for a given generation.""" if self.generation == ModelGeneration.five: return ModelCapabilities.DesiredCurvatureV2 diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 39a161ed89..8d2bcc0ef4 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -68,7 +68,9 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D driving_model_data.frameDropPerc = frame_drop_perc action = driving_model_data.action - action.desiredCurvature = float(net_output_data['desired_curvature'][0,0]) + model_use_lateral_planner = custom_model_valid and custom_model_capabilities & ModelCapabilities.LateralPlannerSolution + if not model_use_lateral_planner: + action.desiredCurvature = float(net_output_data['desired_curvature'][0,0]) modelV2 = extended_msg.modelV2 modelV2.frameId = vipc_frame_id @@ -100,7 +102,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyz_poly(poly_path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) # lateral planning - if custom_model_valid and custom_model_capabilities & ModelCapabilities.LateralPlannerSolution: + if model_use_lateral_planner: solution = modelV2.lateralPlannerSolutionDEPRECATED solution.x, solution.y, solution.yaw, solution.yawRate = [net_output_data['lat_planner_solution'][0,:,i].tolist() for i in range(4)] solution.xStd, solution.yStd, solution.yawStd, solution.yawRateStd = [net_output_data['lat_planner_solution_stds'][0,:,i].tolist() for i in range(4)] From 2f3d999c67ef5ead9a7be9b96d67ab6f804305c5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 25 Jul 2024 17:16:57 +0000 Subject: [PATCH 06/12] ui: Lead car chevron: Time to Lead Car --- CHANGELOGS.md | 5 +++++ .../ui/qt/offroad/sunnypilot/visuals_settings.cc | 4 ++-- selfdrive/ui/qt/onroad/annotated_camera.cc | 14 +++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 06a2961de7..2fc99ab8d0 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -40,6 +40,11 @@ sunnypilot - 0.9.8.0 (2024-xx-xx) * Auto Unlock by Shift to P: All doors are automatically unlocked when shifting the shift lever to P * FIXED: Driving Personality: * Maniac mode now correctly enforced when selected +* UI Updates + * Display Metrics Below Chevron + * NEW❗: Time to Lead Car + * Displays the time to reach the position previously occupied by the lead car + * NEW❗: Display Distance, Speed, and Time to Lead Car simultaneously * Kia Ceed Plug-in Hybrid Non-SCC 2022 support thanks to TerminatorNL! sunnypilot - 0.9.7.1 (2024-06-13) diff --git a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc index 46bd84b6d9..742725e8ab 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc +++ b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc @@ -82,12 +82,12 @@ VisualsPanel::VisualsPanel(QWidget *parent) : ListWidget(parent) { dev_ui_settings->showDescription(); // Visuals: Display Metrics above Chevron - std::vector chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Distance\nSpeed")}; + std::vector chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Time"), tr("All")}; chevron_info_settings = new ButtonParamControl( "ChevronInfo", tr("Display Metrics Below Chevron"), tr("Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control)."), "../assets/offroad/icon_blank.png", chevron_info_settings_texts, - 340 + 300 ); chevron_info_settings->showDescription(); diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index 009ff76138..bb19798e92 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -1525,20 +1525,28 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState painter.drawPolygon(chevron, std::size(chevron)); if (num == 0) { // Display metrics to the 0th lead car - int chevron_types = 2; + const int chevron_types = 3; + const int chevron_all = chevron_types + 1; // All metrics QStringList chevron_text[chevron_types]; int position; float val; - if (chevron_data == 1 || chevron_data == 3) { + if (chevron_data == 1 || chevron_data == chevron_all) { position = 0; val = std::max(0.0f, d_rel); chevron_text[position].append(QString::number(val,'f', 0) + " " + "m"); } - if (chevron_data == 2 || chevron_data == 3) { + if (chevron_data == 2 || chevron_data == chevron_all) { position = (chevron_data == 2) ? 0 : 1; val = std::max(0.0f, (v_rel + v_ego) * (is_metric ? static_cast(MS_TO_KPH) : static_cast(MS_TO_MPH))); chevron_text[position].append(QString::number(val,'f', 0) + " " + (is_metric ? "km/h" : "mph")); } + if (chevron_data == 3 || chevron_data == chevron_all) { + position = (chevron_data == 3) ? 0 : 2; + val = (d_rel > 0 && v_ego > 0) ? std::max(0.0f, d_rel / v_ego) : 0.0f; + + QString ttc_str = (val > 0 && val < 200) ? QString::number(val, 'f', 1) + "s" : "---"; + chevron_text[position].append(ttc_str); + } float str_w = 200; // Width of the text box, might need adjustment float str_h = 50; // Height of the text box, adjust as necessary From ca8c74bc0d2444934ba62e6c8005a23ed20e664f Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 25 Jul 2024 17:23:33 +0000 Subject: [PATCH 07/12] MADS: Use modern button events parsing --- selfdrive/car/__init__.py | 26 ++++++++++++--- selfdrive/car/chrysler/carcontroller.py | 12 ++++--- selfdrive/car/chrysler/carstate.py | 21 ++++++------ selfdrive/car/chrysler/interface.py | 35 ++++++-------------- selfdrive/car/chrysler/values.py | 14 ++++---- selfdrive/car/ford/carstate.py | 24 +++++++------- selfdrive/car/ford/interface.py | 36 ++++++--------------- selfdrive/car/ford/values.py | 21 ++++++------ selfdrive/car/gm/interface.py | 26 ++++++--------- selfdrive/car/honda/interface.py | 11 ++++--- selfdrive/car/hyundai/interface.py | 23 +++++-------- selfdrive/car/interfaces.py | 8 ++++- selfdrive/car/mazda/carcontroller.py | 9 +++--- selfdrive/car/mazda/carstate.py | 21 ++++++------ selfdrive/car/mazda/interface.py | 38 +++++++--------------- selfdrive/car/mazda/values.py | 18 ++++++----- selfdrive/car/nissan/interface.py | 27 ++++------------ selfdrive/car/subaru/interface.py | 27 ++++------------ selfdrive/car/toyota/interface.py | 28 ++++------------ selfdrive/car/volkswagen/carstate.py | 26 ++------------- selfdrive/car/volkswagen/interface.py | 43 ++++++------------------- selfdrive/car/volkswagen/values.py | 10 ------ 22 files changed, 195 insertions(+), 309 deletions(-) diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index 6aebd2bd90..207c90678d 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -43,10 +43,28 @@ def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, ca return events -def create_mads_event(mads_event_lock: bool) -> capnp.lib.capnp._DynamicStructBuilder: - be = car.CarState.ButtonEvent(pressed=mads_event_lock) - be.type = ButtonType.altButton1 - return be +class ButtonEvents: + def __init__(self) -> None: + self.is_mads: bool = False + + @staticmethod + def create_cancel_event(long_enabled: bool, prev_long_enabled: bool) -> list[capnp.lib.capnp._DynamicStructBuilder]: + events: list[capnp.lib.capnp._DynamicStructBuilder] = [] + + if not long_enabled and prev_long_enabled: + events.append(car.CarState.ButtonEvent(pressed=True, + type=ButtonType.cancel)) + return events + + def create_mads_event(self, mads_enabled: bool, prev_mads_enabled: bool) -> list[capnp.lib.capnp._DynamicStructBuilder]: + events: list[capnp.lib.capnp._DynamicStructBuilder] = [] + + mads_changed = prev_mads_enabled != mads_enabled + if (mads_changed and not self.is_mads) or (not mads_changed and self.is_mads): + events.append(car.CarState.ButtonEvent(pressed=mads_changed, type=ButtonType.altButton1)) + self.is_mads = not self.is_mads + + return events def gen_empty_fingerprint(): diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py index f9277af62e..54bed40b51 100644 --- a/selfdrive/car/chrysler/carcontroller.py +++ b/selfdrive/car/chrysler/carcontroller.py @@ -1,3 +1,5 @@ +from cereal import car + import cereal.messaging as messaging from common.conversions import Conversions as CV from opendbc.can.packer import CANPacker @@ -9,7 +11,7 @@ from openpilot.selfdrive.car.chrysler.values import RAM_CARS, RAM_DT, CarControl from openpilot.selfdrive.car.interfaces import CarControllerBase, FORWARD_GEARS from openpilot.selfdrive.controls.lib.drive_helpers import FCA_V_CRUISE_MIN -BUTTONS_STATES = ["accelCruise", "decelCruise", "cancel", "resumeCruise"] +ButtonType = car.CarState.ButtonEvent.Type class CarController(CarControllerBase): @@ -104,7 +106,7 @@ class CarController(CarControllerBase): self.last_button_frame = CS.button_counter if ram_cars: - if CS.buttonStates["cancel"]: + if any(b.type == ButtonType.cancel for b in CS.out.buttonEvents): can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter, das_bus, self.CP, cancel=True)) else: can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter, das_bus, self.CP, @@ -189,8 +191,10 @@ class CarController(CarControllerBase): # multikyd methods, sunnyhaibin logic def get_cruise_buttons_status(self, CS): - if not CS.out.cruiseState.enabled or any(CS.buttonStates[button_state] for button_state in BUTTONS_STATES): - self.timer = 40 + if not CS.out.cruiseState.enabled: + for be in CS.out.buttonEvents: + if be.type in (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.resumeCruise) and be.pressed: + self.timer = 40 elif self.timer: self.timer -= 1 else: diff --git a/selfdrive/car/chrysler/carstate.py b/selfdrive/car/chrysler/carstate.py index d29be9226e..a58681175e 100644 --- a/selfdrive/car/chrysler/carstate.py +++ b/selfdrive/car/chrysler/carstate.py @@ -3,7 +3,7 @@ from openpilot.common.conversions import Conversions as CV from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine from openpilot.selfdrive.car.interfaces import CarStateBase -from openpilot.selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS, BUTTON_STATES +from openpilot.selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS, BUTTONS class CarState(CarStateBase): @@ -29,8 +29,7 @@ class CarState(CarStateBase): self.lkas_heartbit = None self.lkas_disabled = False - self.buttonStates = BUTTON_STATES.copy() - self.buttonStatesPrev = BUTTON_STATES.copy() + self.button_states = {button.event_type: False for button in BUTTONS} def update(self, cp, cp_cam): @@ -41,7 +40,6 @@ class CarState(CarStateBase): self.prev_mads_enabled = self.mads_enabled self.prev_lkas_enabled = self.lkas_enabled - self.buttonStatesPrev = self.buttonStates.copy() # lock info ret.doorOpen = any([cp.vl["BCM_1"]["DOOR_OPEN_FL"], @@ -76,6 +74,16 @@ class CarState(CarStateBase): unit=1, ) + # Buttons + for button in BUTTONS: + state = (cp.vl[button.can_addr][button.can_msg] in button.values) + if self.button_states[button.event_type] != state: + event = car.CarState.ButtonEvent.new_message() + event.type = button.event_type + event.pressed = state + self.button_events.append(event) + self.button_states[button.event_type] = state + # button presses ret.leftBlinker, ret.rightBlinker = ret.leftBlinkerOn, ret.rightBlinkerOn = self.update_blinker_from_stalk(200, cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 1, cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 2) @@ -116,11 +124,6 @@ class CarState(CarStateBase): ret.leftBlindspot = cp.vl["BSM_1"]["LEFT_STATUS"] == 1 ret.rightBlindspot = cp.vl["BSM_1"]["RIGHT_STATUS"] == 1 - self.buttonStates["accelCruise"] = bool(cp.vl["CRUISE_BUTTONS"]["ACC_Accel"]) - self.buttonStates["decelCruise"] = bool(cp.vl["CRUISE_BUTTONS"]["ACC_Decel"]) - self.buttonStates["cancel"] = bool(cp.vl["CRUISE_BUTTONS"]["ACC_Cancel"]) - self.buttonStates["resumeCruise"] = bool(cp.vl["CRUISE_BUTTONS"]["ACC_Resume"]) - self.lkas_car_model = cp_cam.vl["DAS_6"]["CAR_MODEL"] self.button_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"] self.cruise_buttons = cp.vl["CRUISE_BUTTONS"] diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index d09fa7ac56..d4911375e7 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 from cereal import car from panda import Panda -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event -from openpilot.selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerFlagsSP, BUTTON_STATES +from openpilot.selfdrive.car import create_button_events, get_safety_config +from openpilot.selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerFlagsSP from openpilot.selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type @@ -13,7 +13,6 @@ GearShifter = car.CarState.GearShifter class CarInterface(CarInterfaceBase): def __init__(self, CP, CarController, CarState): super().__init__(CP, CarController, CarState) - self.buttonStatesPrev = BUTTON_STATES.copy() @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): @@ -94,19 +93,12 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam) self.sp_update_params() - buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) - - for button in self.CS.buttonStates: - if self.CS.buttonStates[button] != self.buttonStatesPrev[button]: - be = car.CarState.ButtonEvent.new_message() - be.type = button - be.pressed = self.CS.buttonStates[button] - buttonEvents.append(be) + self.CS.button_events.extend(create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})) self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise, + self.CS.button_events, c.vCruise, enable_buttons=(ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.resumeCruise) if not self.CP.pcmCruiseSpeed else (ButtonType.accelCruise, ButtonType.decelCruise), resume_button=(ButtonType.resumeCruise,) if not self.CP.pcmCruiseSpeed else @@ -125,7 +117,7 @@ class CarInterface(CarInterfaceBase): self.CS.madsEnabled = self.get_sp_started_mads(ret, self.CS) if not self.CP.pcmCruise or (self.CP.pcmCruise and self.CP.minEnableSpeed > 0) or not self.CP.pcmCruiseSpeed: - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if self.get_sp_pedal_disengage(ret): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) @@ -143,17 +135,10 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(self.CS.distance_button)) - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] # events events = self.create_common_events(ret, c, extra_gears=[car.CarState.GearShifter.low], pcm_enable=False) @@ -180,6 +165,4 @@ class CarInterface(CarInterfaceBase): ret.events = events.to_msg() - self.buttonStatesPrev = self.CS.buttonStates.copy() - return ret diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 85835d453a..c681040023 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -1,3 +1,4 @@ +from collections import namedtuple from enum import IntFlag from dataclasses import dataclass, field @@ -8,6 +9,7 @@ from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarPar from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 Ecu = car.CarParams.Ecu +Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) class ChryslerFlags(IntFlag): @@ -113,12 +115,12 @@ class CarControllerParams: self.STEER_MAX = 261 # higher than this faults the EPS -BUTTON_STATES = { - "accelCruise": False, - "decelCruise": False, - "cancel": False, - "resumeCruise": False, -} +BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.accelCruise, "CRUISE_BUTTONS", "ACC_Accel", [1]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "CRUISE_BUTTONS", "ACC_Decel", [1]), + Button(car.CarState.ButtonEvent.Type.cancel, "CRUISE_BUTTONS", "ACC_Cancel", [1]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "CRUISE_BUTTONS", "ACC_Resume", [1]), +] STEER_THRESHOLD = 120 diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index 21e2f12def..e52454f81d 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -3,7 +3,7 @@ from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.ford.fordcan import CanBus -from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags, BUTTON_STATES +from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags, BUTTONS from openpilot.selfdrive.car.interfaces import CarStateBase GearShifter = car.CarState.GearShifter @@ -24,15 +24,14 @@ class CarState(CarStateBase): self.lkas_enabled = None self.prev_lkas_enabled = None - self.buttonStates = BUTTON_STATES.copy() - self.buttonStatesPrev = BUTTON_STATES.copy() + + self.button_states = {button.event_type: False for button in BUTTONS} def update(self, cp, cp_cam): ret = car.CarState.new_message() self.prev_mads_enabled = self.mads_enabled self.prev_lkas_enabled = self.lkas_enabled - self.buttonStatesPrev = self.buttonStates.copy() # Occasionally on startup, the ABS module recalibrates the steering pinion offset, so we need to block engagement # The vehicle usually recovers out of this state within a minute of normal driving @@ -87,6 +86,16 @@ class CarState(CarStateBase): else: ret.gearShifter = GearShifter.drive + # Buttons + for button in BUTTONS: + state = (cp.vl[button.can_addr][button.can_msg] in button.values) + if self.button_states[button.event_type] != state: + event = car.CarState.ButtonEvent.new_message() + event.type = button.event_type + event.pressed = state + self.button_events.append(event) + self.button_states[button.event_type] = state + # safety ret.stockFcw = bool(cp_cam.vl["ACCDATA_3"]["FcwVisblWarn_B_Rq"]) ret.stockAeb = bool(cp_cam.vl["ACCDATA_2"]["CmbbBrkDecel_B_Rq"]) @@ -112,13 +121,6 @@ class CarState(CarStateBase): self.lkas_enabled = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"]) - self.buttonStates["accelCruise"] = bool(cp.vl["Steering_Data_FD1"]["CcAslButtnSetIncPress"]) - self.buttonStates["decelCruise"] = bool(cp.vl["Steering_Data_FD1"]["CcAslButtnSetDecPress"]) - self.buttonStates["cancel"] = bool(cp.vl["Steering_Data_FD1"]["CcAslButtnCnclPress"]) - self.buttonStates["setCruise"] = bool(cp.vl["Steering_Data_FD1"]["CcAslButtnSetPress"]) - self.buttonStates["resumeCruise"] = bool(cp.vl["Steering_Data_FD1"]["CcAsllButtnResPress"]) - self.buttonStates["gapAdjustCruise"] = bool(cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"]) - # Stock steering buttons so that we can passthru blinkers etc. self.buttons_stock_values = cp.vl["Steering_Data_FD1"] # Stock values from IPMA so that we can retain some stock functionality diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 0c3449dc14..18dd7b4fa3 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -1,9 +1,9 @@ from cereal import car from panda import Panda from openpilot.common.conversions import Conversions as CV -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.ford.fordcan import CanBus -from openpilot.selfdrive.car.ford.values import Ecu, FordFlags, BUTTON_STATES +from openpilot.selfdrive.car.ford.values import Ecu, FordFlags from openpilot.selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type @@ -15,8 +15,6 @@ class CarInterface(CarInterfaceBase): def __init__(self, CP, CarController, CarState): super().__init__(CP, CarController, CarState) - self.buttonStatesPrev = BUTTON_STATES.copy() - @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): ret.carName = "ford" @@ -76,19 +74,12 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam) self.sp_update_params() - buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) - - for button in self.CS.buttonStates: - if self.CS.buttonStates[button] != self.buttonStatesPrev[button]: - be = car.CarState.ButtonEvent.new_message() - be.type = button - be.pressed = self.CS.buttonStates[button] - buttonEvents.append(be) + self.CS.button_events.extend(create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})) self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise) + self.CS.button_events, c.vCruise) if ret.cruiseState.available: if self.enable_mads: @@ -101,7 +92,7 @@ class CarInterface(CarInterfaceBase): self.CS.madsEnabled = False if not self.CP.pcmCruise or (self.CP.pcmCruise and self.CP.minEnableSpeed > 0): - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if self.get_sp_pedal_disengage(ret): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) @@ -114,16 +105,10 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(self.CS.distance_button)) - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] events = self.create_common_events(ret, c, extra_gears=[GearShifter.manumatic], pcm_enable=False) @@ -134,7 +119,4 @@ class CarInterface(CarInterfaceBase): ret.events = events.to_msg() - # update previous car states - self.buttonStatesPrev = self.CS.buttonStates.copy() - return ret diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index edfee3ea1b..04bd592e22 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -1,5 +1,6 @@ import copy import re +from collections import namedtuple from dataclasses import dataclass, field, replace from enum import Enum, IntFlag @@ -11,6 +12,7 @@ from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, Ca from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16 Ecu = car.CarParams.Ecu +Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) class CarControllerParams: @@ -46,16 +48,6 @@ class FordFlags(IntFlag): CANFD = 1 -BUTTON_STATES = { - "accelCruise": False, - "decelCruise": False, - "cancel": False, - "setCruise": False, - "resumeCruise": False, - "gapAdjustCruise": False -} - - class RADAR: DELPHI_ESR = 'ford_fusion_2018_adas' DELPHI_MRR = 'FORD_CADS' @@ -154,6 +146,15 @@ class CAR(Platforms): ) +BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.accelCruise, "Steering_Data_FD1", "CcAslButtnSetIncPress", [1]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "Steering_Data_FD1", "CcAslButtnSetDecPress", [1]), + Button(car.CarState.ButtonEvent.Type.cancel, "Steering_Data_FD1", "CcAslButtnCnclPress", [1]), + Button(car.CarState.ButtonEvent.Type.setCruise, "Steering_Data_FD1", "CcAslButtnSetPress", [1]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "Steering_Data_FD1", "CcAsllButtnResPress", [1]), +] + + # FW response contains a combined software and part number # A-Z except no I, O or W # e.g. NZ6A-14C204-AAA diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 22a8d619a6..0e428bc13b 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -6,7 +6,7 @@ from panda import Panda from openpilot.common.basedir import BASEDIR from openpilot.common.conversions import Conversions as CV -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel @@ -206,12 +206,11 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam, self.cp_loopback) self.sp_update_params() - buttonEvents = [] distance_button = 0 # Don't add event if transitioning from INIT, unless it's to an actual button if self.CS.cruise_buttons != CruiseButtons.UNPRESS or self.CS.prev_cruise_buttons != CruiseButtons.INIT: - buttonEvents = [ + self.CS.button_events = [ *create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT, unpressed_btn=CruiseButtons.UNPRESS), *create_button_events(self.CS.distance_button, self.CS.prev_distance_button, @@ -222,11 +221,11 @@ class CarInterface(CarInterfaceBase): self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) if not self.CP.pcmCruise: - if any(b.type == ButtonType.accelCruise and b.pressed for b in buttonEvents): + if any(b.type == ButtonType.accelCruise and b.pressed for b in self.CS.button_events): self.CS.accEnabled = True self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise) + self.CS.button_events, c.vCruise) if ret.cruiseState.available: if self.enable_mads: @@ -239,7 +238,7 @@ class CarInterface(CarInterfaceBase): self.CS.madsEnabled = False if not self.CP.pcmCruise or (self.CP.pcmCruise and self.CP.minEnableSpeed > 0): - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if self.get_sp_pedal_disengage(ret): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) @@ -252,17 +251,10 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(distance_button)) - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] # The ECM allows enabling on falling edge of set, but only rising edge of resume events = self.create_common_events(ret, c, extra_gears=[GearShifter.sport, GearShifter.low, diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 457b095e6a..72ab68647f 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -268,7 +268,7 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam, self.cp_body) self.sp_update_params() - buttonEvents = [ + self.CS.button_events = [ *create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT), *create_button_events(self.CS.cruise_setting, self.CS.prev_cruise_setting, SETTINGS_BUTTONS_DICT), ] @@ -276,7 +276,7 @@ class CarInterface(CarInterfaceBase): self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise) + self.CS.button_events, c.vCruise) if ret.cruiseState.available: if self.enable_mads: @@ -289,7 +289,7 @@ class CarInterface(CarInterfaceBase): self.CS.madsEnabled = False if not self.CP.pcmCruise or (self.CP.pcmCruise and self.CP.minEnableSpeed > 0) or not self.CP.pcmCruiseSpeed: - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if self.get_sp_pedal_disengage(ret): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) @@ -304,7 +304,10 @@ class CarInterface(CarInterfaceBase): min_enable_speed_pcm=(self.CP.pcmCruise and self.CP.minEnableSpeed > 0 and self.CP.pcmCruiseSpeed), gap_button=(self.CS.cruise_setting == 3)) - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] # events events = self.create_common_events(ret, c, extra_gears=[GearShifter.sport, GearShifter.low], pcm_enable=False) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index e801b47771..dbe0b62895 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -7,7 +7,7 @@ from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, HyundaiFlagsSP, CANFD_UNSUPPORTED_LONGITUDINAL_CAR, NON_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \ UNSUPPORTED_LONGITUDINAL_CAR, Buttons from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.disable_ecu import disable_ecu @@ -205,10 +205,10 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam) self.sp_update_params() - buttonEvents = create_button_events(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT) + self.CS.button_events = create_button_events(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT) self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise) + self.CS.button_events, c.vCruise) self.CS.mads_enabled = False if not self.mads_main_toggle else self.CS.mads_enabled @@ -231,7 +231,7 @@ class CarInterface(CarInterfaceBase): if not self.CP.pcmCruise or not self.CP.pcmCruiseSpeed: if not self.CP.pcmCruise: - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if not self.CP.pcmCruiseSpeed: if not ret.cruiseState.enabled: @@ -242,17 +242,10 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=(self.CS.cruise_buttons[-1] == 3)) - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] # On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state # To avoid re-engaging when openpilot cancels, check user engagement intention via buttons diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 5fe64832c8..b8ca13ec86 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -1,3 +1,4 @@ +import capnp import json import os import numpy as np @@ -17,7 +18,7 @@ from openpilot.common.simple_kalman import KF1D, get_kalman_gain from openpilot.common.numpy_fast import clip from openpilot.common.params import Params from openpilot.common.realtime import DT_CTRL -from openpilot.selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG +from openpilot.selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG, ButtonEvents from openpilot.selfdrive.car.values import PLATFORMS from openpilot.selfdrive.controls.lib.desire_helper import get_min_lateral_speed from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, V_CRUISE_UNSET, get_friction @@ -253,6 +254,7 @@ class CarInterfaceBase(ABC): self.last_mads_init = 0. self.madsEnabledInit = False self.madsEnabledInitPrev = False + self.button_events = ButtonEvents() self.lat_torque_nn_model = None eps_firmware = str(next((fw.fwVersion for fw in CP.carFw if fw.ecu == "eps"), "")) @@ -425,6 +427,8 @@ class CarInterfaceBase(ABC): if cp is not None: cp.update_strings(can_strings) + self.CS.button_events = [] + # get CarState ret = self._update(c) @@ -780,6 +784,8 @@ class CarStateBase(ABC): self.prev_mads_enabled = False self.control_initialized = False + self.button_events: list[capnp.lib.capnp._DynamicStructBuilder] = [] + Q = [[0.0, 0.0], [0.0, 100.0]] R = 0.3 A = [[1.0, DT_CTRL], [0.0, 1.0]] diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py index 9832cebe46..368198e1c8 100644 --- a/selfdrive/car/mazda/carcontroller.py +++ b/selfdrive/car/mazda/carcontroller.py @@ -11,8 +11,7 @@ from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons from openpilot.selfdrive.controls.lib.drive_helpers import MAZDA_V_CRUISE_MIN VisualAlert = car.CarControl.HUDControl.VisualAlert - -BUTTONS_STATES = ["accelCruise", "decelCruise", "cancel", "resumeCruise"] +ButtonType = car.CarState.ButtonEvent.Type class CarController(CarControllerBase): @@ -141,8 +140,10 @@ class CarController(CarControllerBase): # multikyd methods, sunnyhaibin logic def get_cruise_buttons_status(self, CS): if not CS.out.cruiseState.enabled: - if any(CS.buttonStates[button_state] for button_state in BUTTONS_STATES): - self.timer = 40 + for be in CS.out.buttonEvents: + if be.type in (ButtonType.accelCruise, ButtonType.resumeCruise, + ButtonType.decelCruise, ButtonType.setCruise) and be.pressed: + self.timer = 40 elif self.timer: self.timer -= 1 else: diff --git a/selfdrive/car/mazda/carstate.py b/selfdrive/car/mazda/carstate.py index dea768c03a..e5808238f8 100644 --- a/selfdrive/car/mazda/carstate.py +++ b/selfdrive/car/mazda/carstate.py @@ -3,7 +3,7 @@ from openpilot.common.conversions import Conversions as CV from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from openpilot.selfdrive.car.interfaces import CarStateBase -from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags, BUTTON_STATES +from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags, BUTTONS class CarState(CarStateBase): def __init__(self, CP): @@ -24,8 +24,7 @@ class CarState(CarStateBase): self.lkas_enabled = False self.prev_lkas_enabled = False - self.buttonStates = BUTTON_STATES.copy() - self.buttonStatesPrev = BUTTON_STATES.copy() + self.button_states = {button.event_type: False for button in BUTTONS} def update(self, cp, cp_cam): @@ -36,7 +35,6 @@ class CarState(CarStateBase): self.prev_mads_enabled = self.mads_enabled self.prev_lkas_enabled = self.lkas_enabled - self.buttonStatesPrev = self.buttonStates.copy() ret.wheelSpeeds = self.get_wheel_speeds( cp.vl["WHEEL_SPEEDS"]["FL"], @@ -56,6 +54,16 @@ class CarState(CarStateBase): can_gear = int(cp.vl["GEAR"]["GEAR"]) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) + # Buttons + for button in BUTTONS: + state = (cp.vl[button.can_addr][button.can_msg] in button.values) + if self.button_states[button.event_type] != state: + event = car.CarState.ButtonEvent.new_message() + event.type = button.event_type + event.pressed = state + self.button_events.append(event) + self.button_states[button.event_type] = state + ret.genericToggle = bool(cp.vl["BLINK_INFO"]["HIGH_BEAMS"]) ret.leftBlindspot = cp.vl["BSM"]["LEFT_BS_STATUS"] != 0 ret.rightBlindspot = cp.vl["BSM"]["RIGHT_BS_STATUS"] != 0 @@ -114,11 +122,6 @@ class CarState(CarStateBase): self.acc_active_last = ret.cruiseState.enabled - self.buttonStates["accelCruise"] = bool(cp.vl["CRZ_BTNS"]["SET_P"]) - self.buttonStates["decelCruise"] = bool(cp.vl["CRZ_BTNS"]["SET_M"]) - self.buttonStates["cancel"] = bool(cp.vl["CRZ_BTNS"]["CAN_OFF"]) - self.buttonStates["resumeCruise"] = bool(cp.vl["CRZ_BTNS"]["RES"]) - self.crz_btns_counter = cp.vl["CRZ_BTNS"]["CTR"] # camera signals diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py index bb9a3f93a0..9447803ae6 100755 --- a/selfdrive/car/mazda/interface.py +++ b/selfdrive/car/mazda/interface.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 from cereal import car from openpilot.common.conversions import Conversions as CV -from openpilot.selfdrive.car.mazda.values import CAR, LKAS_LIMITS, BUTTON_STATES -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event +from openpilot.selfdrive.car.mazda.values import CAR, LKAS_LIMITS +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type @@ -12,7 +12,6 @@ GearShifter = car.CarState.GearShifter class CarInterface(CarInterfaceBase): def __init__(self, CP, CarController, CarState): super().__init__(CP, CarController, CarState) - self.buttonStatesPrev = BUTTON_STATES.copy() @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): @@ -41,19 +40,15 @@ class CarInterface(CarInterfaceBase): self.sp_update_params() # TODO: add button types for inc and dec - buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) - - for button in self.CS.buttonStates: - if self.CS.buttonStates[button] != self.buttonStatesPrev[button]: - be = car.CarState.ButtonEvent.new_message() - be.type = button - be.pressed = self.CS.buttonStates[button] - buttonEvents.append(be) + self.CS.button_events = [ + *self.CS.button_events, + *create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) + ] self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise) + self.CS.button_events, c.vCruise) if ret.cruiseState.available: if self.enable_mads: @@ -66,7 +61,7 @@ class CarInterface(CarInterfaceBase): self.CS.madsEnabled = False if not self.CP.pcmCruise or (self.CP.pcmCruise and self.CP.minEnableSpeed > 0) or not self.CP.pcmCruiseSpeed: - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if self.get_sp_pedal_disengage(ret): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) @@ -79,17 +74,10 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(self.CS.distance_button)) - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] # events events = self.create_common_events(ret, c, extra_gears=[GearShifter.sport, GearShifter.low, GearShifter.brake], @@ -108,6 +96,4 @@ class CarInterface(CarInterfaceBase): ret.events = events.to_msg() - self.buttonStatesPrev = self.CS.buttonStates.copy() - return ret diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index 2aac951dbb..2889cbb7b2 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -1,3 +1,4 @@ +from collections import namedtuple from dataclasses import dataclass, field from enum import IntFlag @@ -8,6 +9,7 @@ from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarPar from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu +Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) # Steer torque limits @@ -26,14 +28,6 @@ class CarControllerParams: pass -BUTTON_STATES = { - "accelCruise": False, - "decelCruise": False, - "cancel": False, - "resumeCruise": False, -} - - @dataclass class MazdaCarDocs(CarDocs): package: str = "All" @@ -98,6 +92,14 @@ class Buttons: CANCEL = 4 +BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.accelCruise, "CRZ_BTNS", "SET_P", [1]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "CRZ_BTNS", "SET_M", [1]), + Button(car.CarState.ButtonEvent.Type.cancel, "CRZ_BTNS", "CAN_OFF", [1]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "CRZ_BTNS", "RES", [1]), +] + + FW_QUERY_CONFIG = FwQueryConfig( requests=[ # TODO: check data to ensure ABS does not skip ISO-TP frames on bus 0 diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py index 3d744a41a0..4a9e5a9125 100644 --- a/selfdrive/car/nissan/interface.py +++ b/selfdrive/car/nissan/interface.py @@ -1,6 +1,6 @@ from cereal import car from panda import Panda -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.nissan.values import CAR @@ -34,7 +34,7 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_adas, self.cp_cam) self.sp_update_params() - buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) + self.CS.button_events = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) @@ -53,24 +53,11 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(self.CS.distance_button)) - # CANCEL - if self.CS.out.cruiseState.enabled and not ret.cruiseState.enabled: - be = car.CarState.ButtonEvent.new_message() - be.pressed = True - be.type = ButtonType.cancel - buttonEvents.append(be) - - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_cancel_event(ret.cruiseState.enabled, self.CS.out.cruiseState.enabled), + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] events = self.create_common_events(ret, c, extra_gears=[GearShifter.sport, GearShifter.low, GearShifter.brake], pcm_enable=False) diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 744c017a96..7eefeb66eb 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -1,6 +1,6 @@ from cereal import car from panda import Panda -from openpilot.selfdrive.car import get_safety_config, create_mads_event +from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFlags, SubaruFlagsSP @@ -118,8 +118,6 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam, self.cp_body) self.sp_update_params() - buttonEvents = [] - self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) if ret.cruiseState.available: @@ -145,24 +143,11 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS) - # CANCEL - if self.CS.out.cruiseState.enabled and not ret.cruiseState.enabled: - be = car.CarState.ButtonEvent.new_message() - be.pressed = True - be.type = ButtonType.cancel - buttonEvents.append(be) - - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_cancel_event(ret.cruiseState.enabled, self.CS.out.cruiseState.enabled), + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] events = self.create_common_events(ret, c, extra_gears=[GearShifter.sport, GearShifter.low], pcm_enable=False) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 451265f9cd..d8b2516aba 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -5,7 +5,7 @@ from panda import Panda from panda.python import uds from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, ToyotaFlagsSP, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \ MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR -from openpilot.selfdrive.car import create_button_events, get_safety_config, create_mads_event +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.interfaces import CarInterfaceBase @@ -212,11 +212,10 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam) self.sp_update_params() - buttonEvents = [] distance_button = 0 if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER): - buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) + self.CS.button_events = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) distance_button = self.CS.distance_button self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) @@ -245,24 +244,11 @@ class CarInterface(CarInterfaceBase): ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(distance_button)) - # CANCEL - if self.CS.out.cruiseState.enabled and not ret.cruiseState.enabled: - be = car.CarState.ButtonEvent.new_message() - be.pressed = True - be.type = ButtonType.cancel - buttonEvents.append(be) - - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_cancel_event(ret.cruiseState.enabled, self.CS.out.cruiseState.enabled), + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] # events events = self.create_common_events(ret, c, extra_gears=[GearShifter.sport, GearShifter.low, GearShifter.brake], diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py index 6ec27b8be0..1dc4c0e430 100644 --- a/selfdrive/car/volkswagen/carstate.py +++ b/selfdrive/car/volkswagen/carstate.py @@ -4,7 +4,7 @@ from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \ - CarControllerParams, VolkswagenFlags, BUTTON_STATES, VolkswagenFlagsSP + CarControllerParams, VolkswagenFlags, VolkswagenFlagsSP class CarState(CarStateBase): @@ -17,8 +17,6 @@ class CarState(CarStateBase): self.esp_hold_confirmation = False self.upscale_lead_car_signal = False self.eps_stock_values = False - self.buttonStates = BUTTON_STATES.copy() - self.buttonStatesPrev = BUTTON_STATES.copy() def create_button_events(self, pt_cp, buttons): button_events = [] @@ -41,7 +39,6 @@ class CarState(CarStateBase): ret = car.CarState.new_message() self.prev_mads_enabled = self.mads_enabled - self.buttonStatesPrev = self.buttonStates.copy() # Update vehicle speed and acceleration from ABS wheel speeds. ret.wheelSpeeds = self.get_wheel_speeds( @@ -150,18 +147,10 @@ class CarState(CarStateBase): if ret.cruiseState.speed > 90: ret.cruiseState.speed = 0 - # Update control button states for turn signals and ACC controls. - self.buttonStates["accelCruise"] = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Tip_Hoch"]) - self.buttonStates["decelCruise"] = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Tip_Runter"]) - self.buttonStates["cancel"] = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Abbrechen"]) - self.buttonStates["setCruise"] = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Tip_Setzen"]) - self.buttonStates["resumeCruise"] = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Tip_Wiederaufnahme"]) - self.buttonStates["gapAdjustCruise"] = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Verstellung_Zeitluecke"]) - # Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough ret.leftBlinker = ret.leftBlinkerOn = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Left"]) ret.rightBlinker = ret.rightBlinkerOn = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Right"]) - ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS) + self.button_events = self.create_button_events(pt_cp, self.CCP.BUTTONS) self.gra_stock_values = pt_cp.vl["GRA_ACC_01"] # Additional safety checks performed in CarInterface. @@ -177,7 +166,6 @@ class CarState(CarStateBase): ret = car.CarState.new_message() self.prev_mads_enabled = self.mads_enabled - self.buttonStatesPrev = self.buttonStates.copy() # Update vehicle speed and acceleration from ABS wheel speeds. ret.wheelSpeeds = self.get_wheel_speeds( @@ -265,18 +253,10 @@ class CarState(CarStateBase): if ret.cruiseState.speed > 70: # 255 kph in m/s == no current setpoint ret.cruiseState.speed = 0 - # Update control button states for turn signals and ACC controls. - self.buttonStates["accelCruise"] = bool(pt_cp.vl["GRA_Neu"]["GRA_Up_kurz"]) - self.buttonStates["decelCruise"] = bool(pt_cp.vl["GRA_Neu"]["GRA_Down_kurz"]) - self.buttonStates["cancel"] = bool(pt_cp.vl["GRA_Neu"]["GRA_Abbrechen"]) - self.buttonStates["setCruise"] = bool(pt_cp.vl["GRA_Neu"]["GRA_Neu_Setzen"]) - self.buttonStates["resumeCruise"] = bool(pt_cp.vl["GRA_Neu"]["GRA_Recall"]) - self.buttonStates["gapAdjustCruise"] = bool(pt_cp.vl["GRA_Neu"]["GRA_Zeitluecke"]) - # Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough ret.leftBlinker, ret.rightBlinker = ret.leftBlinkerOn, ret.rightBlinkerOn = self.update_blinker_from_stalk(300, pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_li"], pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_re"]) - ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS) + self.button_events = self.create_button_events(pt_cp, self.CCP.BUTTONS) self.gra_stock_values = pt_cp.vl["GRA_Neu"] # Additional safety checks performed in CarInterface. diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index 31dea25cbb..d965cf2e8b 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -1,10 +1,10 @@ from cereal import car from panda import Panda from openpilot.common.params import Params -from openpilot.selfdrive.car import get_safety_config, create_mads_event +from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.volkswagen.values import CAR, CANBUS, CarControllerParams, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags, \ - BUTTON_STATES, VolkswagenFlagsSP + VolkswagenFlagsSP ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName @@ -21,8 +21,6 @@ class CarInterface(CarInterfaceBase): self.ext_bus = CANBUS.cam self.cp_ext = self.cp_cam - self.buttonStatesPrev = BUTTON_STATES.copy() - @staticmethod def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs): ret.carName = "volkswagen" @@ -117,21 +115,10 @@ class CarInterface(CarInterfaceBase): ret = self.CS.update(self.cp, self.cp_cam, self.cp_ext, self.CP.transmissionType) self.sp_update_params() - buttonEvents = [] - - # Check for and process state-change events (button press or release) from - # the turn stalk switch or ACC steering wheel/control stalk buttons. - for button in self.CS.buttonStates: - if self.CS.buttonStates[button] != self.buttonStatesPrev[button]: - be = car.CarState.ButtonEvent.new_message() - be.type = button - be.pressed = self.CS.buttonStates[button] - buttonEvents.append(be) - self.CS.mads_enabled = self.get_sp_cruise_main_state(ret, self.CS) self.CS.accEnabled = self.get_sp_v_cruise_non_pcm_state(ret, self.CS.accEnabled, - buttonEvents, c.vCruise, + self.CS.button_events, c.vCruise, enable_buttons=(ButtonType.setCruise, ButtonType.resumeCruise)) if ret.cruiseState.available: @@ -144,7 +131,7 @@ class CarInterface(CarInterfaceBase): self.CS.madsEnabled = self.get_sp_started_mads(ret, self.CS) if not self.CP.pcmCruise or (self.CP.pcmCruise and self.CP.minEnableSpeed > 0) or not self.CP.pcmCruiseSpeed: - if any(b.type == ButtonType.cancel for b in buttonEvents): + if any(b.type == ButtonType.cancel for b in self.CS.button_events): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) if self.get_sp_pedal_disengage(ret): self.CS.madsEnabled, self.CS.accEnabled = self.get_sp_cancel_cruise_state(self.CS.madsEnabled) @@ -155,19 +142,13 @@ class CarInterface(CarInterfaceBase): self.CS.accEnabled = False self.CS.accEnabled = ret.cruiseState.enabled or self.CS.accEnabled - ret, self.CS = self.get_sp_common_state(ret, self.CS, gap_button=bool(self.CS.buttonStates["gapAdjustCruise"])) + ret, self.CS = self.get_sp_common_state(ret, self.CS, + gap_button=any(b.type == ButtonType.gapAdjustCruise and b.pressed for b in self.CS.button_events)) - # MADS BUTTON - if self.CS.out.madsEnabled != self.CS.madsEnabled: - if self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = False - else: - if not self.mads_event_lock: - buttonEvents.append(create_mads_event(self.mads_event_lock)) - self.mads_event_lock = True - - ret.buttonEvents = buttonEvents + ret.buttonEvents = [ + *self.CS.button_events, + *self.button_events.create_mads_event(self.CS.madsEnabled, self.CS.out.madsEnabled) # MADS BUTTON + ] events = self.create_common_events(ret, c, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic], pcm_enable=False, @@ -199,8 +180,4 @@ class CarInterface(CarInterfaceBase): ret.events = events.to_msg() - # update previous car states - self.buttonStatesPrev = self.CS.buttonStates.copy() - return ret - diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 2996db602f..80b7ca0b0b 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -147,16 +147,6 @@ class VolkswagenFlagsSP(IntFlag): SP_CC_ONLY_NO_RADAR = 2 -BUTTON_STATES = { - "accelCruise": False, - "decelCruise": False, - "cancel": False, - "setCruise": False, - "resumeCruise": False, - "gapAdjustCruise": False -} - - @dataclass class VolkswagenMQBPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None)) From c26fd9d7c0889055f5cc59d649551add35e92ab6 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Fri, 26 Jul 2024 09:24:27 +0000 Subject: [PATCH 08/12] Add new parameters for enabling GitLab runner and Sunnylink uploader --- .gitlab-ci.yml | 4 ++-- common/params.cc | 2 ++ system/manager/manager.py | 2 ++ system/manager/process_config.py | 25 ++++++++++++------------- system/manager/sunnylink.py | 5 +++++ 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de50202454..2704a7bb28 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,13 +29,13 @@ default: - 'git config --global user.name "${GIT_CONFIG_USER_NAME}"' -workflow: # If running on any branch other than main, use the `aws-datacontracts-dev` account; otherwise use `aws-datacontracts` +workflow: # If running on any branch other than main. rules: # We are an MR, but it's a draft, we won't proceed with anything. - if: '$CI_MERGE_REQUEST_TITLE =~ /^wip:/i || $CI_MERGE_REQUEST_TITLE =~ /^draft:/i' when: never # We are a merge request - - if: $CI_MERGE_REQUEST_IID #|| $CI_COMMIT_REF_NAME == "gitlab-pipelines" # TBD once merged + - if: $CI_MERGE_REQUEST_IID variables: EXTRA_VERSION_IDENTIFIER: "-${CI_PIPELINE_IID}" NEW_BRANCH: ${CI_COMMIT_REF_NAME}-prebuilt diff --git a/common/params.cc b/common/params.cc index 054c5d2355..86f18a0a5e 100644 --- a/common/params.cc +++ b/common/params.cc @@ -336,6 +336,8 @@ std::unordered_map keys = { {"SunnylinkCache_Users", PERSISTENT}, {"SunnylinkCache_Roles", PERSISTENT}, + {"EnableGitlabRunner", PERSISTENT | BACKUP}, + {"EnableSunnylinkUploader", PERSISTENT | BACKUP}, // PFEIFER - MAPD {{ {"MapdVersion", PERSISTENT}, diff --git a/system/manager/manager.py b/system/manager/manager.py index 388198736c..032fc0a667 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -117,6 +117,8 @@ def manager_init() -> None: ("CustomDrivingModel", "0"), ("DrivingModelGeneration", "4"), ("LastSunnylinkPingTime", "0"), + ("EnableGitlabRunner", "0"), + ("EnableSunnylinkUploader", "0"), ] if not PC: default_params.append(("LastUpdateTime", datetime.datetime.now(datetime.UTC).replace(tzinfo=None).isoformat().encode('utf8'))) diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 8818d3b851..a718ea021b 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -6,7 +6,7 @@ from openpilot.system.hardware import PC, TICI from openpilot.selfdrive.modeld.custom_model_metadata import CustomModelMetadata, ModelCapabilities from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess from openpilot.system.mapd_manager import MAPD_PATH, COMMON_DIR -from openpilot.system.manager.sunnylink import sunnylink_need_register, sunnylink_ready +from openpilot.system.manager.sunnylink import sunnylink_need_register, sunnylink_ready, use_sunnylink_uploader WEBCAM = os.getenv("USE_WEBCAM") is not None @@ -44,6 +44,10 @@ def only_onroad(started: bool, params, CP: car.CarParams) -> bool: def only_offroad(started, params, CP: car.CarParams) -> bool: return not started +def use_gitlab_runner(started, params, CP: car.CarParams) -> bool: + return (not PC and params.get_bool("EnableGitlabRunner") and only_offroad(started, params, CP) + and os.path.exists("./gitlab_runner.sh")) + def model_use_nav(started, params, CP: car.CarParams) -> bool: custom_model_metadata = CustomModelMetadata(params=params, init_only=True) return started and custom_model_metadata.valid and custom_model_metadata.capabilities & ModelCapabilities.NoO @@ -56,6 +60,10 @@ def sunnylink_need_register_shim(started, params, CP: car.CarParams) -> bool: """Shim for sunnylink_need_register to match the process manager signature.""" return sunnylink_need_register(params) +def use_sunnylink_uploader_shim(started, params, CP: car.CarParams) -> bool: + """Shim for use_sunnylink_uploader to match the process manager signature.""" + return use_sunnylink_uploader(params) and os.path.exists("../loggerd/sunnylink_uploader.py") + procs = [ DaemonProcess("manage_athenad", "system.athena.manage_athenad", "AthenadPid"), @@ -112,21 +120,12 @@ procs = [ PythonProcess("webrtcd", "system.webrtc.webrtcd", notcar), PythonProcess("webjoystick", "tools.bodyteleop.web", notcar), + # Sunnypilot devs + NativeProcess("gitlab_runner_start", "system/manager", ["./gitlab_runner.sh", "start"], use_gitlab_runner, sigkill=False), # Sunnylink <3 DaemonProcess("manage_sunnylinkd", "system.athena.manage_sunnylinkd", "SunnylinkdPid"), PythonProcess("sunnylink_registration", "system.manager.sunnylink", sunnylink_need_register_shim), + PythonProcess("sunnylink_uploader", "system.loggerd.sunnylink_uploader", use_sunnylink_uploader_shim), ] -if os.path.exists("../loggerd/sunnylink_uploader.py"): - procs += [ - PythonProcess("sunnylink_uploader", "system.loggerd.sunnylink_uploader", sunnylink_ready_shim), - ] - -if os.path.exists("./gitlab_runner.sh") and not PC: - # Only devs! - procs += [ - NativeProcess("gitlab_runner_start", "system/manager", ["./gitlab_runner.sh", "start"], only_offroad, sigkill=False), - NativeProcess("gitlab_runner_stop", "system/manager", ["./gitlab_runner.sh", "stop"], only_onroad, sigkill=False) - ] - managed_processes = {p.name: p for p in procs} diff --git a/system/manager/sunnylink.py b/system/manager/sunnylink.py index 84161fb817..10a5f95a3d 100755 --- a/system/manager/sunnylink.py +++ b/system/manager/sunnylink.py @@ -27,6 +27,11 @@ def sunnylink_ready(params=Params()) -> bool: return is_sunnylink_enabled and is_registered +def use_sunnylink_uploader(params) -> bool: + """Check if the device is ready to use Sunnylink and the uploader is enabled.""" + return sunnylink_ready(params) and params.get_bool("EnableSunnylinkUploader") + + def sunnylink_need_register(params=Params()) -> bool: """Check if the device needs to be registered with Sunnylink.""" is_sunnylink_enabled, is_registered = get_sunnylink_status(params) From 7e31333b36066235801a077d9a39e5d1a5614635 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 29 Jul 2024 06:40:23 -0400 Subject: [PATCH 09/12] Add Custom MIT License --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..e7062d77cf --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# Custom MIT License + +Copyright 2024 Haibin Wen, SUNNYPILOT LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to view and modify the Software, subject to the following conditions: + +1. **Permission Required**: Use of the Software, in whole or in part, for any purpose requires explicit written permission from the original author(s). + +2. **Redistribution**: Any redistribution of the Software, modified or unmodified, must retain this license notice and the following acknowledgment: + "This software is licensed under a custom license requiring permission for use." + +3. **Visibility**: Any project that uses the Software must visibly mention the following acknowledgment: + "This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use." + +4. **No Warranty**: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the Software or the use or other dealings in the Software. + +Contact sunnypilot Support for permission requests. + +--- + +Haibin Wen, SUNNYPILOT LLC From be72a8ed063cf5d595be0a30c9b7aa4da7027c1b Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 29 Jul 2024 06:26:22 -0400 Subject: [PATCH 10/12] Revert "Toyota: Auto Brake Hold" This reverts commit 15d94a01 --- CHANGELOGS.md | 3 - cereal/car.capnp | 1 - common/params.cc | 1 - panda | 2 +- selfdrive/car/card.py | 3 - selfdrive/car/toyota/carcontroller.py | 31 - selfdrive/car/toyota/carstate.py | 9 - selfdrive/car/toyota/interface.py | 8 - selfdrive/car/toyota/toyotacan.py | 34 - selfdrive/car/toyota/values.py | 1 - selfdrive/controls/lib/events.py | 8 - .../qt/offroad/sunnypilot/vehicle_settings.cc | 15 +- selfdrive/ui/translations/main_ar.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_de.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_es.ts | 2211 ++++++++++++++++- selfdrive/ui/translations/main_fr.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_ja.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_ko.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_pt-BR.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_th.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_tr.ts | 2125 +++++++++++++++- selfdrive/ui/translations/main_zh-CHS.ts | 2129 +++++++++++++++- selfdrive/ui/translations/main_zh-CHT.ts | 2129 +++++++++++++++- system/manager/manager.py | 1 - 24 files changed, 23383 insertions(+), 231 deletions(-) diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 2fc99ab8d0..650d149843 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -32,9 +32,6 @@ sunnypilot - 0.9.8.0 (2024-xx-xx) * In response to the official deprecation of support for Smart DSU (SDSU) and Radar CAN Filter in the upstream ([commaai/openpilot#32777](https://github.com/commaai/openpilot/pull/32777)), sunnypilot will continue maintaining software support for Smart DSU (SDSU) and Radar CAN Filter * UPDATED: Continued support for Mapbox navigation * In response to the official temporary deprecation of support for Mapbox navigation in the upstream ([commaai/openpilot#32773](https://github.com/commaai/openpilot/pull/32773)), sunnypilot will continue maintaining software support for Mapbox navigation -* NEW❗: Toyota - Automatic Brake Hold (AHB) thanks to AlexandreSato! - * When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold - * NOTE: Only for Toyota/Lexus vehicles with TSS2/LSS2 * NEW❗: Toyota - Automatic Door Locking and Unlocking thanks to AlexandreSato, cydia2020, and dragonpilot-community! * Auto Lock by Speed: All doors are automatically locked when vehicle speed is approximately 6 mph (10 km/h) or higher * Auto Unlock by Shift to P: All doors are automatically unlocked when shifting the shift lever to P diff --git a/cereal/car.capnp b/cereal/car.capnp index 0ee2be7499..e77e8c2943 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -137,7 +137,6 @@ struct CarEvent @0x9b1657f34caf3ad3 { speedLimitPreActive @139; speedLimitConfirmed @140; torqueNNLoad @141; - spAutoBrakeHold @142; radarCanErrorDEPRECATED @15; communityFeatureDisallowedDEPRECATED @62; diff --git a/common/params.cc b/common/params.cc index 86f18a0a5e..7d12b9937e 100644 --- a/common/params.cc +++ b/common/params.cc @@ -318,7 +318,6 @@ std::unordered_map keys = { {"TorqueFriction", PERSISTENT | BACKUP}, {"TorqueMaxLatAccel", PERSISTENT | BACKUP}, {"TorquedOverride", PERSISTENT | BACKUP}, - {"ToyotaAutoHold", PERSISTENT | BACKUP}, {"ToyotaAutoLockBySpeed", PERSISTENT | BACKUP}, {"ToyotaAutoUnlockByShifter", PERSISTENT | BACKUP}, {"ToyotaEnhancedBsm", PERSISTENT | BACKUP}, diff --git a/panda b/panda index ff1ef85735..5d92bdc6de 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit ff1ef85735c053529a840436d33ab5f16840724e +Subproject commit 5d92bdc6dee1f1bba5062f551bf6938f40723d81 diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 63e7d22da4..98be4bf82f 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -56,7 +56,6 @@ class Car: self.mads_disengage_lateral_on_brake = self.params.get_bool("DisengageLateralOnBrake") self.mads_dlob = self.enable_mads and self.mads_disengage_lateral_on_brake self.mads_ndlob = self.enable_mads and not self.mads_disengage_lateral_on_brake - self.sp_toyota_auto_brake_hold = self.params.get_bool("ToyotaAutoHold") self.CP.alternativeExperience = 0 if not self.disengage_on_accelerator: self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS @@ -64,8 +63,6 @@ class Car: self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS elif self.mads_ndlob: self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_DISABLE_DISENGAGE_LATERAL_ON_BRAKE - if self.sp_toyota_auto_brake_hold: - self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALLOW_AEB if self.CP.customStockLongAvailable and self.CP.pcmCruise and self.params.get_bool("CustomStockLong"): self.CP.pcmCruiseSpeed = False diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 210ae23fa1..89f5992961 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -61,12 +61,6 @@ class CarController(CarControllerBase): self.right_blindspot_debug_enabled = False self.last_blindspot_frame = 0 - if CP.spFlags & ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD: - self.brake_hold_active: bool = False - self._brake_hold_counter: int = 0 - self._brake_hold_reset: bool = False - self._prev_brake_pressed: bool = False - self._auto_lock_by_speed = self.param_s.get_bool("ToyotaAutoLockBySpeed") self._auto_unlock_by_shifter = self.param_s.get_bool("ToyotaAutoUnlockByShifter") self._auto_lock_speed = 10 * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS) @@ -187,9 +181,6 @@ class CarController(CarControllerBase): self.last_standstill = CS.out.standstill - if self.CP.spFlags & ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD: - can_sends.extend(self.create_auto_brake_hold_messages(CS)) - # handle UI messages fcw_alert = hud_control.visualAlert == VisualAlert.fcw steer_alert = hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) @@ -308,25 +299,3 @@ class CarController(CarControllerBase): # print("bsm poll right") return can_sends - - # auto brake hold (https://github.com/AlexandreSato/) - def create_auto_brake_hold_messages(self, CS: car.CarState, brake_hold_allowed_timer: int = 100): - can_sends = [] - disallowed_gears = [GearShifter.park, GearShifter.reverse] - brake_hold_allowed = CS.out.standstill and CS.out.cruiseState.available and not CS.out.gasPressed and \ - not CS.out.cruiseState.enabled and (CS.out.gearShifter not in disallowed_gears) - - if brake_hold_allowed: - self._brake_hold_counter += 1 - self.brake_hold_active = self._brake_hold_counter > brake_hold_allowed_timer and not self._brake_hold_reset - self._brake_hold_reset = not self._prev_brake_pressed and CS.out.brakePressed and not self._brake_hold_reset - else: - self._brake_hold_counter = 0 - self.brake_hold_active = False - self._brake_hold_reset = False - self._prev_brake_pressed = CS.out.brakePressed - - if self.frame % 2 == 0: - can_sends.append(toyotacan.create_brake_hold_command(self.packer, self.frame, CS.pre_collision_2, self.brake_hold_active)) - - return can_sends diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 456282982b..fded06c1dc 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -79,9 +79,6 @@ class CarState(CarStateBase): self._right_blindspot_d2 = 0 self._right_blindspot_counter = 0 - if CP.spFlags & ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD: - self.pre_collision_2 = {} - self.frame = 0 def update(self, cp, cp_cam): @@ -263,9 +260,6 @@ class CarState(CarStateBase): if self.CP.spFlags & ToyotaFlagsSP.SP_ENHANCED_BSM and self.frame > 199: ret.leftBlindspot, ret.rightBlindspot = self.sp_get_enhanced_bsm(cp) - if self.CP.spFlags & ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD: - self.pre_collision_2 = copy.copy(cp_cam.vl["PRE_COLLISION_2"]) - self._update_traffic_signals(cp_cam) ret.cruiseState.speedLimit = self._calculate_speed_limit() self.frame += 1 @@ -469,7 +463,4 @@ class CarState(CarStateBase): ("PCS_HUD", 1), ] - if CP.spFlags & ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD: - messages.append(("PRE_COLLISION_2", 33)) - return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index d8b2516aba..1b0528d12c 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -195,9 +195,6 @@ class CarInterface(CarInterfaceBase): if candidate == CAR.TOYOTA_PRIUS_TSS2: ret.spFlags |= ToyotaFlagsSP.SP_NEED_DEBUG_BSM.value - if Params().get_bool("ToyotaAutoHold") and candidate in (TSS2_CAR - RADAR_ACC_CAR): - ret.spFlags |= ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD.value - return ret @staticmethod @@ -275,11 +272,6 @@ class CarInterface(CarInterfaceBase): # while in standstill, send a user alert events.add(EventName.manualRestart) - # auto brake hold - if self.CP.spFlags & ToyotaFlagsSP.SP_AUTO_BRAKE_HOLD: - if self.CC.brake_hold_active and not ret.brakeHoldActive: - events.add(EventName.spAutoBrakeHold) - ret.events = events.to_msg() return ret diff --git a/selfdrive/car/toyota/toyotacan.py b/selfdrive/car/toyota/toyotacan.py index 979879e368..abde32bc07 100644 --- a/selfdrive/car/toyota/toyotacan.py +++ b/selfdrive/car/toyota/toyotacan.py @@ -130,37 +130,3 @@ def create_set_bsm_debug_mode(lr_blindspot, enabled): def create_bsm_polling_status(lr_blindspot): return make_can_msg(0x750, lr_blindspot + b"\x02\x21\x69\x00\x00\x00\x00", 0) - - -# auto brake hold -def create_brake_hold_command(packer, frame, pre_collision_2, brake_hold_active): - # forward PRE_COLLISION_2 when auto brake hold is not active - values = {s: pre_collision_2[s] for s in [ - "DSS1GDRV", - "DS1STAT2", - "DS1STBK2", - "PCSWAR", - "PCSALM", - "PCSOPR", - "PCSABK", - "PBATRGR", - "PPTRGR", - "IBTRGR", - "CLEXTRGR", - "IRLT_REQ", - "BRKHLD", - "AVSTRGR", - "VGRSTRGR", - "PREFILL", - "PBRTRGR", - "PCSDIS", - "PBPREPMP", - ]} - - if brake_hold_active: - values = { - "DSS1GDRV": 0x3FF, - "PBRTRGR": frame % 730 < 727, # cut actuation for 3 frames - } - - return packer.make_can_msg("PRE_COLLISION_2", 0, values) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index b4b277cc08..6952bb41bd 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -64,7 +64,6 @@ class ToyotaFlagsSP(IntFlag): SP_ZSS = 1 SP_ENHANCED_BSM = 2 SP_NEED_DEBUG_BSM = 4 - SP_AUTO_BRAKE_HOLD = 8 class Footnote(Enum): diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 0ef232f5fe..50a092445c 100755 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -744,14 +744,6 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"), }, - EventName.spAutoBrakeHold: { - ET.PERMANENT: Alert( - "sunnypilot Brake Hold Active", - "", - AlertStatus.normal, AlertSize.small, - Priority.LOWEST, VisualAlert.none, AudibleAlert.prompt, 0.), - }, - EventName.parkBrake: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Parking Brake Engaged"), diff --git a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc index ef0954c1ed..dd5d457926 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc +++ b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc @@ -124,19 +124,7 @@ SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(VehiclePanel *parent) : ListWidge toyotaTss2LongTune->setConfirmation(true, false); addItem(toyotaTss2LongTune); - auto toyotaAbh = new ParamControl( - "ToyotaAutoHold", - tr("Enable Automatic Brake Hold (AHB)"), - QString("%1

%2

%3") - .arg(tr("WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK.")) - .arg(tr("When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold.")) - .arg(tr("Changing this setting takes effect when the car is powered off.")), - "../assets/offroad/icon_blank.png" - ); - toyotaAbh->setConfirmation(true, false); - addItem(toyotaAbh); - - toyotaEnhancedBsm = new ParamControl( + toyotaEnhancedBsm = new ParamControlSP( "ToyotaEnhancedBsm", tr("Enable Enhanced Blind Spot Monitor"), "", @@ -188,7 +176,6 @@ SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(VehiclePanel *parent) : ListWidge is_onroad = !offroad; hkgSmoothStop->setEnabled(offroad); toyotaTss2LongTune->setEnabled(offroad); - toyotaAbh->setEnabled(offroad); toyotaEnhancedBsm->setEnabled(offroad); toyotaSngHack->setEnabled(offroad); volkswagenCCOnly->setEnabled(offroad); diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index d8146723a4..7dae31a53e 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -87,6 +87,89 @@ من أجل "%1" + + AdvancedNetworkingSP + + Back + السابق + + + Enable Tethering + تمكين الربط + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + كلمة مرور الربط + + + EDIT + تعديل + + + Enter new tethering password + أدخل كلمة مرور الربط الجديدة + + + IP Address + عنوان IP + + + Enable Roaming + تمكين التجوال + + + APN Setting + إعدادات APN + + + Enter APN + إدخال APN + + + leave blank for automatic configuration + اتركه فارغاً من أجل التكوين التلقائي + + + Cellular Metered + محدود بالاتصال الخلوي + + + Prevent large data uploads when on a metered connection + منع تحميل البيانات الكبيرة عندما يكون الاتصال محدوداً + + + Hidden Network + شبكة مخفية + + + CONNECT + الاتصال + + + Enter SSID + أدخل SSID + + + Enter password + أدخل كلمة المرور + + + for "%1" + من أجل "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - SPEED + SPEED LIMIT - LIMIT + LIMIT + + + + AnnotatedCameraWidgetSP + + km/h + كم/س + + + mph + ميل/س + + + MAX + MAX + + + SPEED + SPEED + + + LIMIT + LIMIT + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ إلغاء + + CustomOffsetsSettings + + Back + السابق + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - يجب عليك قبول الشروط والأحكام من أجل استخدام openpilot. + يجب عليك قبول الشروط والأحكام من أجل استخدام openpilot. Back @@ -135,6 +333,10 @@ Decline, uninstall %1 رفض، إلغاء التثبيت %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - يحتاج openpilot أن يتم ضبط الجهاز ضمن حدود 4 درجات يميناً أو يساراً و5 درجات نحو الأعلى أو 9 نحو الأسفل. يقوم openpilot بالمعايرة باستمرار، ونادراً ما يحتاج إلى عملية إعادة الضبط. + يحتاج openpilot أن يتم ضبط الجهاز ضمن حدود 4 درجات يميناً أو يساراً و5 درجات نحو الأعلى أو 9 نحو الأسفل. يقوم openpilot بالمعايرة باستمرار، ونادراً ما يحتاج إلى عملية إعادة الضبط. Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR إقران + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + عرض + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + إعادة الضبط + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + إعادة الضبط + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -349,6 +700,79 @@ جارٍ التثبيت... + + LaneChangeSettings + + Back + السابق + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -390,6 +814,63 @@ بانتظار الطريق + + MapWindowSP + + Map Loading + تحميل الخريطة + + + Waiting for GPS + بانتظار GPS + + + Waiting for route + بانتظار الطريق + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + م + + + hr + س + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -420,6 +901,33 @@ كلمة مرور خاطئة + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + متقدم + + + Enter password + أدخل كلمة المرور + + + for "%1" + من أجل "%1" + + + Wrong password + كلمة مرور خاطئة + + OffroadAlert @@ -472,6 +980,16 @@ openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. لقد اكتشف openpilot تغييراً في موقع تركيب الجهاز. تأكد من تثبيت الجهاز بشكل كامل في موقعه وتثبيته بإحكام على الزجاج الأمامي. + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -511,6 +1029,186 @@ إعادة التشغيل + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + د + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + التحقق + + + Country + + + + SELECT + اختيار + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + تحديث + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -541,6 +1239,51 @@ إلغاء + + ParamControlSP + + Enable + تمكين + + + Cancel + إلغاء + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + كم/س + + + mph + ميل/س + + PrimeAdWidget @@ -595,7 +1338,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -650,6 +1393,30 @@ now الآن + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -692,6 +1459,131 @@ This may take up to a minute. تم تفعيل إعادة ضبط النظام. اضغط على تأكيد لمسح جميع المحتويات والإعدادات. اضغط على إلغاء لاستئناف التمهيد. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -715,6 +1607,61 @@ This may take up to a minute. البرنامج + + SettingsWindowSP + + × + × + + + Device + الجهاز + + + Network + الشبكة + + + sunnylink + + + + Toggles + المثبتتات + + + Software + البرنامج + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -908,6 +1855,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + درجة الحرارة + + + HIGH + مرتفع + + + GOOD + جيد + + + OK + موافق + + + DISABLED + + + + OFFLINE + غير متصل + + + REGIST... + + + + ONLINE + متصل + + + ERROR + خطأ + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -983,6 +2024,233 @@ This may take up to a minute. أحدث نسخة، آخر تحقق %1 + + SoftwarePanelSP + + Driving Model + + + + SELECT + اختيار + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + إعادة ضبط المعايرة + + + Warning: You are on a metered connection! + + + + Continue + متابعة + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + كم/س + + + mph + ميل/س + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + غير متاح + + + km/h + كم/س + + + mph + ميل/س + + SshControl @@ -1029,6 +2297,399 @@ This may take up to a minute. تمكين SSH + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + غير متاح + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + إقران + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1048,15 +2709,30 @@ This may take up to a minute. أوافق + + TermsPageSP + + Terms & Conditions + الشروط والأحكام + + + Decline + رفض + + + Scroll to accept + قم بالتمرير للقبول + + TogglesPanel Enable openpilot - تمكين openpilot + تمكين openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - استخدم نظام openpilot من أجل الضبط التكيفي للسرعة والحفاظ على مساعدة السائق للبقاء في المسار. انتباهك مطلوب في جميع الأوقات مع استخدام هذه الميزة. يعمل هذا التغيير في الإعدادات عند إيقاف تشغيل السيارة. + استخدم نظام openpilot من أجل الضبط التكيفي للسرعة والحفاظ على مساعدة السائق للبقاء في المسار. انتباهك مطلوب في جميع الأوقات مع استخدام هذه الميزة. يعمل هذا التغيير في الإعدادات عند إيقاف تشغيل السيارة. Enable Lane Departure Warnings @@ -1092,19 +2768,19 @@ This may take up to a minute. Show ETA in 24h Format - إظهار الوقت المقدر للوصول بصيغة 24 ساعة + إظهار الوقت المقدر للوصول بصيغة 24 ساعة Use 24h format instead of am/pm - استخدام صيغة 24 ساعة بدلاً من صباحاً/مساء + استخدام صيغة 24 ساعة بدلاً من صباحاً/مساء Show Map on Left Side of UI - عرض الخريطة على الجانب الأيسر من واجهة المستخدم + عرض الخريطة على الجانب الأيسر من واجهة المستخدم Show map on left side when in split screen view. - عرض الخريطة عل الجانب الأيسر عندما تكون وضعية العرض بطريقة الشاشة المنقسمة. + عرض الخريطة عل الجانب الأيسر عندما تكون وضعية العرض بطريقة الشاشة المنقسمة. openpilot Longitudinal Control (Alpha) @@ -1186,6 +2862,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + التحكم الطولي openpilot (ألفا) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + الوضع التجريبي + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + فك الارتباط عن دواسة الوقود + + + When enabled, pressing the accelerator pedal will disengage openpilot. + عند تمكين هذه الميزة، فإن الضغط على دواسة الوقود سيؤدي إلى فك ارتباط openpilot. + + + Enable Lane Departure Warnings + قم بتمكين تحذيرات مغادرة المسار + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + تلقي التنبيهات من أجل الالتفاف للعودة إلى المسار عندما تنحرف سيارتك فوق الخط المحدد للمسار دون تشغيل إشارة الانعطاف عند القيادة لمسافة تزيد عن 31 ميل/سا (50 كم/سا). + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + تسجيل وتحميل كاميرا السائق + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + تحميل البيانات من الكاميرا المواجهة للسائق، والمساعدة في تحسين خوارزمية مراقبة السائق. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + استخدام النظام المتري + + + Display speed in km/h instead of mph. + عرض السرعة بواحدات كم/سا بدلاً من ميل/سا. + + + Show ETA in 24h Format + إظهار الوقت المقدر للوصول بصيغة 24 ساعة + + + Use 24h format instead of am/pm + استخدام صيغة 24 ساعة بدلاً من صباحاً/مساء + + + Show Map on Left Side of UI + عرض الخريطة على الجانب الأيسر من واجهة المستخدم + + + Show map on left side when in split screen view. + عرض الخريطة عل الجانب الأيسر عندما تكون وضعية العرض بطريقة الشاشة المنقسمة. + + + Aggressive + الهجومي + + + Moderate + + + + Standard + القياسي + + + Relaxed + الراحة + + + Driving Personality + شخصية القيادة + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + يتم وضع openpilot بشكل قياسي في <b>وضعية الراحة</b>. يمكن الوضع التجريبي <b>ميزات المستوى ألفا</b> التي لا تكون جاهزة في وضع الراحة: + + + End-to-End Longitudinal Control + التحكم الطولي من طرف إلى طرف + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + تصور القيادة الديد + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + الوضع التجريبي غير متوفر حالياً في هذه السيارة نظراً لاستخدام رصيد التحكم التكيفي بالسرعة من أجل التحكم الطولي. + + + openpilot longitudinal control may come in a future update. + قد يتم الحصول على التحكم الطولي في openpilot في عمليات التحديث المستقبلية. + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1222,6 +3144,168 @@ This may take up to a minute. فشل التحديث + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1268,4 +3352,27 @@ This may take up to a minute. نسيان + + WifiUISP + + Scanning for networks... + يتم البحث عن شبكات... + + + CONNECTING... + يتم الاتصال... + + + FORGET + نسيان هذه الشبكة + + + Forget Wi-Fi Network "%1"? + هل تريد نسيان شبكة الواي فاي "%1"؟ + + + Forget + نسيان + + diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 010aa4d304..6b8fd1efc1 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -87,6 +87,89 @@ für "%1" + + AdvancedNetworkingSP + + Back + Zurück + + + Enable Tethering + Tethering aktivieren + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + Tethering Passwort + + + EDIT + ÄNDERN + + + Enter new tethering password + Neues tethering Passwort eingeben + + + IP Address + IP Adresse + + + Enable Roaming + Roaming aktivieren + + + APN Setting + APN Einstellungen + + + Enter APN + APN eingeben + + + leave blank for automatic configuration + für automatische Konfiguration leer lassen + + + Cellular Metered + Getaktete Verbindung + + + Prevent large data uploads when on a metered connection + Hochladen großer Dateien über getaktete Verbindungen unterbinden + + + Hidden Network + + + + CONNECT + CONNECT + + + Enter SSID + SSID eingeben + + + Enter password + Passwort eingeben + + + for "%1" + für "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - Geschwindigkeit + Geschwindigkeit LIMIT - LIMIT + LIMIT + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + MAX + + + SPEED + Geschwindigkeit + + + LIMIT + LIMIT + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ Abbrechen + + CustomOffsetsSettings + + Back + Zurück + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - Du musst die Nutzungsbedingungen akzeptieren, um Openpilot zu benutzen. + Du musst die Nutzungsbedingungen akzeptieren, um Openpilot zu benutzen. Back @@ -135,6 +333,10 @@ Decline, uninstall %1 Ablehnen, deinstallieren %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - Damit Openpilot funktioniert, darf die Installationsposition nicht mehr als 4° nach rechts/links, 5° nach oben und 9° nach unten abweichen. Openpilot kalibriert sich durchgehend, ein Zurücksetzen ist selten notwendig. + Damit Openpilot funktioniert, darf die Installationsposition nicht mehr als 4° nach rechts/links, 5° nach oben und 9° nach unten abweichen. Openpilot kalibriert sich durchgehend, ein Zurücksetzen ist selten notwendig. Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + ANSEHEN + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + RESET + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + Zurücksetzen + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -345,6 +696,79 @@ Installiere... + + LaneChangeSettings + + Back + Zurück + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -386,6 +810,63 @@ + + MapWindowSP + + Map Loading + Karte wird geladen + + + Waiting for GPS + Warten auf GPS + + + Waiting for route + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + std + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -416,6 +897,33 @@ Falsches Passwort + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + Erweitert + + + Enter password + Passwort eingeben + + + for "%1" + für "%1" + + + Wrong password + Falsches Passwort + + OffroadAlert @@ -467,6 +975,16 @@ Device temperature too high. System cooling down before starting. Current internal component temperature: %1 + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -506,6 +1024,186 @@ + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + min + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + ÜBERPRÜFEN + + + Country + + + + SELECT + AUSWÄHLEN + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + Aktualisieren + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -536,6 +1234,51 @@ Aktivieren + + ParamControlSP + + Enable + Aktivieren + + + Cancel + Abbrechen + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -590,7 +1333,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -633,6 +1376,30 @@ now + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -674,6 +1441,131 @@ This may take up to a minute. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -697,6 +1589,61 @@ This may take up to a minute. Software + + SettingsWindowSP + + × + x + + + Device + Gerät + + + Network + Netzwerk + + + sunnylink + + + + Toggles + Schalter + + + Software + Software + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -891,6 +1838,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + TEMP + + + HIGH + HOCH + + + GOOD + GUT + + + OK + OK + + + DISABLED + + + + OFFLINE + OFFLINE + + + REGIST... + + + + ONLINE + ONLINE + + + ERROR + FEHLER + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -967,6 +2008,233 @@ This may take up to a minute. + + SoftwarePanelSP + + Driving Model + + + + SELECT + AUSWÄHLEN + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + Neu kalibrieren + + + Warning: You are on a metered connection! + + + + Continue + Fortsetzen + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + Nicht verfügbar + + + km/h + km/h + + + mph + mph + + SshControl @@ -1013,6 +2281,399 @@ This may take up to a minute. SSH aktivieren + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + Nicht verfügbar + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1032,15 +2693,30 @@ This may take up to a minute. Zustimmen + + TermsPageSP + + Terms & Conditions + Benutzungsbedingungen + + + Decline + Ablehnen + + + Scroll to accept + Scrolle herunter um zu akzeptieren + + TogglesPanel Enable openpilot - Openpilot aktivieren + Openpilot aktivieren Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Benutze das Openpilot System als adaptiven Tempomaten und Spurhalteassistenten. Deine Aufmerksamkeit ist jederzeit erforderlich, um diese Funktion zu nutzen. Diese Einstellung wird übernommen, wenn das Auto aus ist. + Benutze das Openpilot System als adaptiven Tempomaten und Spurhalteassistenten. Deine Aufmerksamkeit ist jederzeit erforderlich, um diese Funktion zu nutzen. Diese Einstellung wird übernommen, wenn das Auto aus ist. Enable Lane Departure Warnings @@ -1072,21 +2748,21 @@ This may take up to a minute. Use 24h format instead of am/pm - Benutze das 24Stunden Format anstatt am/pm + Benutze das 24Stunden Format anstatt am/pm Show Map on Left Side of UI Too long for UI - Zeige die Karte auf der linken Seite + Zeige die Karte auf der linken Seite Show map on left side when in split screen view. - Zeige die Karte auf der linken Seite der Benutzeroberfläche bei geteilten Bildschirm. + Zeige die Karte auf der linken Seite der Benutzeroberfläche bei geteilten Bildschirm. Show ETA in 24h Format Too long for UI - Zeige die Ankunftszeit im 24 Stunden Format + Zeige die Ankunftszeit im 24 Stunden Format Experimental Mode @@ -1172,6 +2848,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + Experimenteller Modus + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + Bei Gasbetätigung ausschalten + + + When enabled, pressing the accelerator pedal will disengage openpilot. + Wenn aktiviert, deaktiviert sich Openpilot sobald das Gaspedal betätigt wird. + + + Enable Lane Departure Warnings + Spurverlassenswarnungen aktivieren + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + Erhalte Warnungen, zurück in die Spur zu lenken, wenn dein Auto über eine erkannte Fahrstreifenmarkierung ohne aktivierten Blinker mit mehr als 50 km/h fährt. + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + Fahrerkamera aufnehmen und hochladen + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + Lade Daten der Fahreraufmerksamkeitsüberwachungskamera hoch, um die Fahreraufmerksamkeitsüberwachungsalgorithmen zu verbessern. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + Benutze das metrische System + + + Display speed in km/h instead of mph. + Zeige die Geschwindigkeit in km/h anstatt von mph. + + + Show ETA in 24h Format + Zeige die Ankunftszeit im 24 Stunden Format + + + Use 24h format instead of am/pm + Benutze das 24Stunden Format anstatt am/pm + + + Show Map on Left Side of UI + Zeige die Karte auf der linken Seite + + + Show map on left side when in split screen view. + Zeige die Karte auf der linken Seite der Benutzeroberfläche bei geteilten Bildschirm. + + + Aggressive + + + + Moderate + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + Openpilot fährt standardmäßig im <b>entspannten Modus</b>. Der Experimentelle Modus aktiviert<b>Alpha-level Funktionen</b>, die noch nicht für den entspannten Modus bereit sind. Die experimentellen Funktionen sind die Folgenden: + + + End-to-End Longitudinal Control + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + Neue Fahrvisualisierung + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + Der experimentelle Modus ist momentan für dieses Auto nicht verfügbar da es den eingebauten adaptiven Tempomaten des Autos benutzt. + + + openpilot longitudinal control may come in a future update. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1208,6 +3130,168 @@ This may take up to a minute. Aktualisierung fehlgeschlagen + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1254,4 +3338,27 @@ This may take up to a minute. Vergessen + + WifiUISP + + Scanning for networks... + Suche nach Netzwerken... + + + CONNECTING... + VERBINDEN... + + + FORGET + VERGESSEN + + + Forget Wi-Fi Network "%1"? + WLAN Netzwerk "%1" vergessen? + + + Forget + Vergessen + + diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index ee262c0953..6527ddfc12 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -87,6 +87,89 @@ para "%1" + + AdvancedNetworkingSP + + Back + + + + Enable Tethering + Activar Tether + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + Contraseña de Tethering + + + EDIT + EDITAR + + + Enter new tethering password + Nueva contraseña de tethering + + + IP Address + Dirección IP + + + Enable Roaming + Activar Roaming + + + APN Setting + Configuración de APN + + + Enter APN + Insertar APN + + + leave blank for automatic configuration + dejar en blanco para configuración automática + + + Cellular Metered + Plano de datos limitado + + + Prevent large data uploads when on a metered connection + Evitar grandes descargas de datos cuando tiene una conexión limitada + + + Hidden Network + Red Oculta + + + CONNECT + + + + Enter SSID + Ingrese SSID + + + Enter password + + + + for "%1" + para "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - VELOCIDAD + VELOCIDAD LIMIT - LIMITE + LIMITE + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + MAX + + + SPEED + VELOCIDAD + + + LIMIT + LIMITE + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ Cancelar + + CustomOffsetsSettings + + Back + + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - Debe aceptar los terminos y condiciones para poder utilizar openpilot. + Debe aceptar los terminos y condiciones para poder utilizar openpilot. Back @@ -135,6 +333,37 @@ Decline, uninstall %1 Rechazar, desinstalar %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + + + + DestinationWidget + + Home + + + + Work + + + + No destination set + + + + home + + + + work + + + + No %1 location set + + DevicePanel @@ -240,7 +469,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot requiere que el dispositivo sea montado entre 4° izquierda o derecha y entre 5° arriba o 9° abajo. openpilot está constantemente en calibración, reiniciar es rara vez necesario. + openpilot requiere que el dispositivo sea montado entre 4° izquierda o derecha y entre 5° arriba o 9° abajo. openpilot está constantemente en calibración, reiniciar es rara vez necesario. Your device is pointed %1° %2 and %3° %4. @@ -278,6 +507,155 @@ Disengage to Power Off Desactivar para apagar + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + VER + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + REINICIAR + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + Reiniciar + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -318,6 +696,177 @@ Instalando... + + LaneChangeSettings + + Back + + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + + + MapETA + + eta + + + + min + + + + hr + + + + + MapSettings + + NAVIGATION + + + + Manage at connect.comma.ai + + + + + MapWindow + + Map Loading + + + + Waiting for GPS + + + + Waiting for route + + + + + MapWindowSP + + Map Loading + + + + Waiting for GPS + + + + Waiting for route + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + + + + hr + + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -348,6 +897,33 @@ Contraseña equivocada + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + Avanzado + + + Enter password + + + + for "%1" + para "%1" + + + Wrong password + Contraseña equivocada + + OffroadAlert @@ -400,6 +976,16 @@ openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. openpilot detectó un cambio en la posición de montaje del dispositivo. Asegúrese de que el dispositivo esté completamente asentado en el soporte y que el soporte esté firmemente asegurado al parabrisas. + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -439,6 +1025,186 @@ Reiniciar Dispositivo + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + VERIFICAR + + + Country + + + + SELECT + SELECCIONAR + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + ACTUALIZAR + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -469,6 +1235,51 @@ Cancelar + + ParamControlSP + + Enable + Activar + + + Cancel + Cancelar + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -497,7 +1308,11 @@ Remote snapshots - Istantánea remota + Istantánea remota + + + Turn-by-turn navigation + @@ -523,7 +1338,7 @@ openpilot - openpilot + openpilot now @@ -550,6 +1365,46 @@ hace %n días + + km + + + + m + + + + mi + + + + ft + + + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -592,6 +1447,131 @@ Esto puede tardar hasta un minuto. No es posible montar una partición de datos. Partición corrompida. Confirme para borrar y reiniciar su dispositivo. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -615,6 +1595,61 @@ Esto puede tardar hasta un minuto. Software + + SettingsWindowSP + + × + × + + + Device + Dispositivo + + + Network + Red + + + sunnylink + + + + Toggles + Ajustes + + + Software + Software + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -808,6 +1843,100 @@ Esto puede tardar hasta un minuto. 5G + + SidebarSP + + TEMP + TEMP + + + HIGH + ALTA + + + GOOD + BUENO + + + OK + OK + + + DISABLED + + + + OFFLINE + SIN LINEA + + + REGIST... + + + + ONLINE + EN LINEA + + + ERROR + ERROR + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -883,6 +2012,233 @@ Esto puede tardar hasta un minuto. actualizado, último chequeo %1 + + SoftwarePanelSP + + Driving Model + + + + SELECT + SELECCIONAR + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + Reiniciar Calibración + + + Warning: You are on a metered connection! + + + + Continue + Continuar + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mph + + SshControl @@ -929,6 +2285,399 @@ Esto puede tardar hasta un minuto. Habilitar SSH + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + EMPAREJAR + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -948,15 +2697,30 @@ Esto puede tardar hasta un minuto. Aceptar + + TermsPageSP + + Terms & Conditions + Terminos & Condiciones + + + Decline + Rechazar + + + Scroll to accept + Desliza para aceptar + + TogglesPanel Enable openpilot - Activar openpilot + Activar openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Utilice el sistema openpilot para control de crucero adaptativo y asistencia al conductor para mantenerse en el carril. Se requiere su atención en todo momento para utilizar esta función. Cambiar esta configuración solo tendrá efecto con el auto apagado. + Utilice el sistema openpilot para control de crucero adaptativo y asistencia al conductor para mantenerse en el carril. Se requiere su atención en todo momento para utilizar esta función. Cambiar esta configuración solo tendrá efecto con el auto apagado. openpilot Longitudinal Control (Alpha) @@ -1070,6 +2834,252 @@ Esto puede tardar hasta un minuto. Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. Activar el control longitudinal experimental para permitir el modo Experimental. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + Control longitudinal de openpilot (Alfa) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + Modo Experimental + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + Desactivar con el Acelerador + + + When enabled, pressing the accelerator pedal will disengage openpilot. + Cuando esté activado, presionar el acelerador deshabilitará el openpilot. + + + Enable Lane Departure Warnings + Activar Aviso de Salida de Carril + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + Recibir alertas para volver dentro del carril cuando su vehículo se sale fuera del carril sin que esté activado la señal de giro mientras esté conduciendo por encima de 50 km/h (31 mph). + + + Always-On Driver Monitoring + Monitoreo del Conductor Siempre Activo + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + Grabar y Subir Cámara del Conductor + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + Subir datos de la cámara del conductor para ayudar a mejorar el algoritmo de monitorización del conductor. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + Usar Sistema Métrico + + + Display speed in km/h instead of mph. + Mostrar velocidad en km/h en vez de mph. + + + Show ETA in 24h Format + + + + Use 24h format instead of am/pm + + + + Show Map on Left Side of UI + + + + Show map on left side when in split screen view. + + + + Aggressive + Agresivo + + + Moderate + + + + Standard + Estándar + + + Relaxed + Relajado + + + Driving Personality + Personalidad de conducción + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + openpilot por defecto conduce en <b>modo chill</b>. El modo Experimental activa <b>recursos de nível-alfa</b> que no están listos para el modo chill. Los recursos del modo expeimental están listados abajo: + + + End-to-End Longitudinal Control + 🌮 Control Longitudinal de Punta a Punta 🌮 + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + Nueva Visualización de la conducción + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + La visualización de la conducción cambiará a la cámara que enfoca la carretera a velocidades bajas para mostrar mejor los giros. El logo del modo experimental se mostrará en la esquina superior derecha. + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + El modo Experimental no está disponible actualmente para este auto, ya que el ACC del auto está siendo usado para el control longitudinal. + + + openpilot longitudinal control may come in a future update. + El control longitudinal de openpilot podrá llegar en futuras actualizaciones. + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1106,6 +3116,168 @@ Esto puede tardar hasta un minuto. Actualización fallida + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1152,4 +3324,27 @@ Esto puede tardar hasta un minuto. Olvidar + + WifiUISP + + Scanning for networks... + Buscando redes... + + + CONNECTING... + CONECTANDO... + + + FORGET + OLVIDAR + + + Forget Wi-Fi Network "%1"? + Olvidar Red Wi-Fi "%1"? + + + Forget + Olvidar + + diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index dde6adadd3..0071a33e47 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -87,6 +87,89 @@ pour "%1" + + AdvancedNetworkingSP + + Back + Retour + + + Enable Tethering + Activer le partage de connexion + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + Mot de passe du partage de connexion + + + EDIT + MODIFIER + + + Enter new tethering password + Entrez le nouveau mot de passe du partage de connexion + + + IP Address + Adresse IP + + + Enable Roaming + Activer l'itinérance + + + APN Setting + Paramètre APN + + + Enter APN + Entrer le nom du point d'accès + + + leave blank for automatic configuration + laisser vide pour une configuration automatique + + + Cellular Metered + Connexion cellulaire limitée + + + Prevent large data uploads when on a metered connection + Éviter les transferts de données importants sur une connexion limitée + + + Hidden Network + Réseau Caché + + + CONNECT + CONNECTER + + + Enter SSID + Entrer le SSID + + + Enter password + Entrer le mot de passe + + + for "%1" + pour "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - VITESSE + VITESSE LIMIT - LIMITE + LIMITE + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mi/h + + + MAX + MAX + + + SPEED + VITESSE + + + LIMIT + LIMITE + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ Annuler + + CustomOffsetsSettings + + Back + Retour + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - Vous devez accepter les conditions générales pour utiliser openpilot. + Vous devez accepter les conditions générales pour utiliser openpilot. Back @@ -135,6 +333,10 @@ Decline, uninstall %1 Refuser, désinstaller %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -255,7 +457,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot nécessite que l'appareil soit monté à 4° à gauche ou à droite et à 5° vers le haut ou 9° vers le bas. openpilot se calibre en continu, la réinitialisation est rarement nécessaire. + openpilot nécessite que l'appareil soit monté à 4° à gauche ou à droite et à 5° vers le haut ou 9° vers le bas. openpilot se calibre en continu, la réinitialisation est rarement nécessaire. Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + VOIR + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + RÉINITIALISER + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + Réinitialiser + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -345,6 +696,79 @@ Installation... + + LaneChangeSettings + + Back + Retour + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -386,6 +810,63 @@ En attente d'un trajet + + MapWindowSP + + Map Loading + Chargement de la carte + + + Waiting for GPS + En attente du GPS + + + Waiting for route + En attente d'un trajet + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + h + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -416,6 +897,33 @@ Mot de passe incorrect + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + Avancé + + + Enter password + Entrer le mot de passe + + + for "%1" + pour "%1" + + + Wrong password + Mot de passe incorrect + + OffroadAlert @@ -468,6 +976,16 @@ openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. openpilot a détecté un changement dans la position de montage de l'appareil. Assurez-vous que l'appareil est totalement inséré dans le support et que le support est fermement fixé au pare-brise. + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -507,6 +1025,186 @@ + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + min + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + VÉRIFIER + + + Country + + + + SELECT + SÉLECTIONNER + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + MISE À JOUR + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -537,6 +1235,51 @@ Annuler + + ParamControlSP + + Enable + Activer + + + Cancel + Annuler + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mi/h + + PrimeAdWidget @@ -591,7 +1334,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -634,6 +1377,30 @@ now + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -676,6 +1443,131 @@ Cela peut prendre jusqu'à une minute. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -699,6 +1591,61 @@ Cela peut prendre jusqu'à une minute. Logiciel + + SettingsWindowSP + + × + × + + + Device + Appareil + + + Network + Réseau + + + sunnylink + + + + Toggles + Options + + + Software + Logiciel + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -892,6 +1839,100 @@ Cela peut prendre jusqu'à une minute. 5G + + SidebarSP + + TEMP + TEMP + + + HIGH + HAUT + + + GOOD + BON + + + OK + OK + + + DISABLED + + + + OFFLINE + HORS LIGNE + + + REGIST... + + + + ONLINE + EN LIGNE + + + ERROR + ERREUR + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -967,6 +2008,233 @@ Cela peut prendre jusqu'à une minute. à jour, dernière vérification %1 + + SoftwarePanelSP + + Driving Model + + + + SELECT + SÉLECTIONNER + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + Réinitialiser la calibration + + + Warning: You are on a metered connection! + + + + Continue + Continuer + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mi/h + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mi/h + + SshControl @@ -1013,6 +2281,399 @@ Cela peut prendre jusqu'à une minute. Activer SSH + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1032,15 +2693,30 @@ Cela peut prendre jusqu'à une minute. Accepter + + TermsPageSP + + Terms & Conditions + Termes & Conditions + + + Decline + Refuser + + + Scroll to accept + Faire défiler pour accepter + + TogglesPanel Enable openpilot - Activer openpilot + Activer openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Utilisez le système openpilot pour le régulateur de vitesse adaptatif et l'assistance au maintien de voie. Votre attention est requise en permanence pour utiliser cette fonctionnalité. La modification de ce paramètre prend effet lorsque la voiture est éteinte. + Utilisez le système openpilot pour le régulateur de vitesse adaptatif et l'assistance au maintien de voie. Votre attention est requise en permanence pour utiliser cette fonctionnalité. La modification de ce paramètre prend effet lorsque la voiture est éteinte. openpilot Longitudinal Control (Alpha) @@ -1092,19 +2768,19 @@ Cela peut prendre jusqu'à une minute. Show ETA in 24h Format - Afficher l'heure d'arrivée en format 24h + Afficher l'heure d'arrivée en format 24h Use 24h format instead of am/pm - Utiliser le format 24h plutôt que am/pm + Utiliser le format 24h plutôt que am/pm Show Map on Left Side of UI - Afficher la carte à gauche de l'interface + Afficher la carte à gauche de l'interface Show map on left side when in split screen view. - Afficher la carte à gauche en mode écran scindé. + Afficher la carte à gauche en mode écran scindé. Aggressive @@ -1170,6 +2846,252 @@ Cela peut prendre jusqu'à une minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + Contrôle longitudinal openpilot (Alpha) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + Mode expérimental + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + Désengager avec la pédale d'accélérateur + + + When enabled, pressing the accelerator pedal will disengage openpilot. + Lorsqu'il est activé, appuyer sur la pédale d'accélérateur désengagera openpilot. + + + Enable Lane Departure Warnings + Activer les avertissements de sortie de voie + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + Recevez des alertes pour revenir dans la voie lorsque votre véhicule dérive au-delà d'une ligne de voie détectée sans clignotant activé en roulant à plus de 31 mph (50 km/h). + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + Enregistrer et télécharger la caméra conducteur + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + Publiez les données de la caméra orientée vers le conducteur et aidez à améliorer l'algorithme de surveillance du conducteur. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + Utiliser le système métrique + + + Display speed in km/h instead of mph. + Afficher la vitesse en km/h au lieu de mph. + + + Show ETA in 24h Format + Afficher l'heure d'arrivée en format 24h + + + Use 24h format instead of am/pm + Utiliser le format 24h plutôt que am/pm + + + Show Map on Left Side of UI + Afficher la carte à gauche de l'interface + + + Show map on left side when in split screen view. + Afficher la carte à gauche en mode écran scindé. + + + Aggressive + Aggressif + + + Moderate + + + + Standard + Standard + + + Relaxed + Détendu + + + Driving Personality + Personnalité de conduite + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + Par défaut, openpilot conduit en <b>mode détente</b>. Le mode expérimental permet d'activer des <b>fonctionnalités alpha</b> qui ne sont pas prêtes pour le mode détente. Les fonctionnalités expérimentales sont listées ci-dessous : + + + End-to-End Longitudinal Control + Contrôle longitudinal de bout en bout + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + Nouvelle visualisation de la conduite + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + La visualisation de la conduite passera sur la caméra grand angle dirigée vers la route à faible vitesse afin de mieux montrer certains virages. Le logo du mode expérimental s'affichera également dans le coin supérieur droit. + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + Le mode expérimental est actuellement indisponible pour cette voiture car le régulateur de vitesse adaptatif d'origine est utilisé pour le contrôle longitudinal. + + + openpilot longitudinal control may come in a future update. + Le contrôle longitudinal openpilot pourrait être disponible dans une future mise à jour. + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1206,6 +3128,168 @@ Cela peut prendre jusqu'à une minute. Échec de la mise à jour + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1252,4 +3336,27 @@ Cela peut prendre jusqu'à une minute. Oublier + + WifiUISP + + Scanning for networks... + Recherche de réseaux... + + + CONNECTING... + CONNEXION... + + + FORGET + OUBLIER + + + Forget Wi-Fi Network "%1"? + Oublier le réseau Wi-Fi "%1" ? + + + Forget + Oublier + + diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index e0fb60620b..5db645c9f4 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -87,6 +87,89 @@ ネットワーク名:%1 + + AdvancedNetworkingSP + + Back + 戻る + + + Enable Tethering + テザリングを有効化 + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + テザリングパスワード + + + EDIT + 編集 + + + Enter new tethering password + 新しいテザリングパスワードを入力 + + + IP Address + IP アドレス + + + Enable Roaming + ローミングを有効化 + + + APN Setting + APN 設定 + + + Enter APN + APN を入力 + + + leave blank for automatic configuration + 自動で設定するには、空白のままにしてください。 + + + Cellular Metered + 従量制通信設定 + + + Prevent large data uploads when on a metered connection + 大量のデータのアップロードを防止します。 + + + Hidden Network + + + + CONNECT + 接続 + + + Enter SSID + SSID を入力 + + + Enter password + パスワードを入力 + + + for "%1" + ネットワーク名:%1 + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - 速度 + 速度 LIMIT - 制限速度 + 制限速度 + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + 最高速度 + + + SPEED + 速度 + + + LIMIT + 制限速度 + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ キャンセル + + CustomOffsetsSettings + + Back + 戻る + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - openpilot をご利用される前に、利用規約に同意する必要があります。 + openpilot をご利用される前に、利用規約に同意する必要があります。 Back @@ -135,6 +333,10 @@ Decline, uninstall %1 拒否して %1 をアンインストール + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilotの本体は、左右4°以内、上5°、下9°以内の角度で取付ける必要があります。継続してキャリブレーションを続けているので、手動でリセットを行う必要はほぼありません。 + openpilotの本体は、左右4°以内、上5°、下9°以内の角度で取付ける必要があります。継続してキャリブレーションを続けているので、手動でリセットを行う必要はほぼありません。 Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + 見る + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + リセット + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + リセット + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -344,6 +695,79 @@ インストールしています... + + LaneChangeSettings + + Back + 戻る + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -385,6 +809,63 @@ + + MapWindowSP + + Map Loading + マップを読み込んでいます + + + Waiting for GPS + GPS信号を探しています + + + Waiting for route + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + メートル + + + hr + 時間 + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -415,6 +896,33 @@ パスワードが間違っています + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + 詳細 + + + Enter password + パスワードを入力 + + + for "%1" + ネットワーク名:%1 + + + Wrong password + パスワードが間違っています + + OffroadAlert @@ -466,6 +974,16 @@ Device temperature too high. System cooling down before starting. Current internal component temperature: %1 + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -505,6 +1023,186 @@ + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 確認 + + + Country + + + + SELECT + 選択 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 更新 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -535,6 +1233,51 @@ を有効化 + + ParamControlSP + + Enable + を有効化 + + + Cancel + キャンセル + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -589,7 +1332,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -629,6 +1372,30 @@ now + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -670,6 +1437,131 @@ This may take up to a minute. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -693,6 +1585,61 @@ This may take up to a minute. ソフトウェア + + SettingsWindowSP + + × + × + + + Device + デバイス + + + Network + ネットワーク + + + sunnylink + + + + Toggles + 機能設定 + + + Software + ソフトウェア + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -886,6 +1833,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + 温度 + + + HIGH + 高温 + + + GOOD + 最適 + + + OK + OK + + + DISABLED + + + + OFFLINE + オフライン + + + REGIST... + + + + ONLINE + オンライン + + + ERROR + エラー + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -961,6 +2002,233 @@ This may take up to a minute. + + SoftwarePanelSP + + Driving Model + + + + SELECT + 選択 + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + キャリブレーションをリセット + + + Warning: You are on a metered connection! + + + + Continue + 続ける + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mph + + SshControl @@ -1007,6 +2275,399 @@ This may take up to a minute. SSH を有効化 + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1026,15 +2687,30 @@ This may take up to a minute. 同意 + + TermsPageSP + + Terms & Conditions + 利用規約 + + + Decline + 拒否 + + + Scroll to accept + スクロールして同意 + + TogglesPanel Enable openpilot - openpilot を有効化 + openpilot を有効化 Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - openpilotによるアダプティブクルーズコントロールとレーンキーピングドライバーアシストを利用します。この機能を利用する際は、常に前方への注意が必要です。この設定を変更すると、車の電源が切れた時に反映されます。 + openpilotによるアダプティブクルーズコントロールとレーンキーピングドライバーアシストを利用します。この機能を利用する際は、常に前方への注意が必要です。この設定を変更すると、車の電源が切れた時に反映されます。 Enable Lane Departure Warnings @@ -1070,19 +2746,19 @@ This may take up to a minute. Show ETA in 24h Format - 24時間表示 + 24時間表示 Use 24h format instead of am/pm - AM/PM の代わりに24時間形式を使用します + AM/PM の代わりに24時間形式を使用します Show Map on Left Side of UI - ディスプレイの左側にマップを表示 + ディスプレイの左側にマップを表示 Show map on left side when in split screen view. - 分割画面表示の場合、ディスプレイの左側にマップを表示します。 + 分割画面表示の場合、ディスプレイの左側にマップを表示します。 Experimental Mode @@ -1164,6 +2840,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + 実験モード + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + アクセルを踏むと openpilot を中断 + + + When enabled, pressing the accelerator pedal will disengage openpilot. + この機能を有効化すると、openpilotを利用中にアクセルを踏むとopenpilotによる運転サポートを中断します。 + + + Enable Lane Departure Warnings + 車線逸脱警報機能を有効化 + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 時速31マイル(50km)を超えるスピードで走行中、ウインカーを作動させずに検出された車線ライン上に車両が触れた場合、手動で車線内に戻るように警告を行います。 + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + 車内カメラの録画とアップロード + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 車内カメラの映像をアップロードし、ドライバー監視システムのアルゴリズムの向上に役立てます。 + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + メートル法を使用 + + + Display speed in km/h instead of mph. + 速度は mph ではなく km/h で表示されます。 + + + Show ETA in 24h Format + 24時間表示 + + + Use 24h format instead of am/pm + AM/PM の代わりに24時間形式を使用します + + + Show Map on Left Side of UI + ディスプレイの左側にマップを表示 + + + Show map on left side when in split screen view. + 分割画面表示の場合、ディスプレイの左側にマップを表示します。 + + + Aggressive + + + + Moderate + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + openpilotは標準ではゆっくりとくつろげる運転を提供します。この実験モードを有効にすると、以下のくつろげる段階ではない開発中の機能を利用する事ができます。 + + + End-to-End Longitudinal Control + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + 新しい運転画面 + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + この車のACCがアクセル制御を行うため実験モードを利用することができません。 + + + openpilot longitudinal control may come in a future update. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1200,6 +3122,168 @@ This may take up to a minute. 更新失敗 + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1246,4 +3330,27 @@ This may take up to a minute. 削除 + + WifiUISP + + Scanning for networks... + ネットワークをスキャン中... + + + CONNECTING... + 接続中... + + + FORGET + 削除 + + + Forget Wi-Fi Network "%1"? + Wi-Fiネットワーク%1を削除してもよろしいですか? + + + Forget + 削除 + + diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 56fc5014ee..1a55d59a3b 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -87,6 +87,89 @@ "%1"에 접속하려면 비밀번호가 필요합니다 + + AdvancedNetworkingSP + + Back + 뒤로 + + + Enable Tethering + 테더링 사용 + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + 테더링 비밀번호 + + + EDIT + 편집 + + + Enter new tethering password + 새 테더링 비밀번호를 입력하세요 + + + IP Address + IP 주소 + + + Enable Roaming + 로밍 사용 + + + APN Setting + APN 설정 + + + Enter APN + APN 입력 + + + leave blank for automatic configuration + 자동 설정하려면 빈 칸으로 두세요 + + + Cellular Metered + 데이터 요금제 + + + Prevent large data uploads when on a metered connection + 데이터 요금제 연결 시 대용량 데이터 업로드를 방지합니다 + + + Hidden Network + 숨겨진 네트워크 + + + CONNECT + + + + Enter SSID + SSID 입력 + + + Enter password + 비밀번호를 입력하세요 + + + for "%1" + "%1"에 접속하려면 비밀번호가 필요합니다 + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - SPEED + SPEED LIMIT - LIMIT + LIMIT + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + MAX + + + SPEED + SPEED + + + LIMIT + LIMIT + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ 취소 + + CustomOffsetsSettings + + Back + 뒤로 + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - openpilot을 사용하려면 이용약관에 동의해야 합니다. + openpilot을 사용하려면 이용약관에 동의해야 합니다. Back @@ -135,6 +333,10 @@ Decline, uninstall %1 거절, %1 제거 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot 장치는 좌우 4°, 위로 5°, 아래로 9° 이내 각도로 장착되어야 합니다. openpilot은 지속적으로 자동 보정되며 재설정은 거의 필요하지 않습니다. + openpilot 장치는 좌우 4°, 위로 5°, 아래로 9° 이내 각도로 장착되어야 합니다. openpilot은 지속적으로 자동 보정되며 재설정은 거의 필요하지 않습니다. Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR 동기화 + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + 보기 + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + 초기화 + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + 초기화 + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -344,6 +695,79 @@ 설치 중... + + LaneChangeSettings + + Back + 뒤로 + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -385,6 +809,63 @@ 경로를 기다리는 중 + + MapWindowSP + + Map Loading + 지도 로딩 중 + + + Waiting for GPS + GPS 수신 중 + + + Waiting for route + 경로를 기다리는 중 + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + 시간 + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -415,6 +896,33 @@ 비밀번호가 틀렸습니다 + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + 고급 설정 + + + Enter password + 비밀번호를 입력하세요 + + + for "%1" + "%1"에 접속하려면 비밀번호가 필요합니다 + + + Wrong password + 비밀번호가 틀렸습니다 + + OffroadAlert @@ -467,6 +975,16 @@ Device temperature too high. System cooling down before starting. Current internal component temperature: %1 장치 온도가 너무 높습니다. 시작하기 전에 온도를 낮춰주세요. 현재 내부 부품 온도: %1 + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -506,6 +1024,186 @@ 장치를 재부팅하세요 + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 확인 + + + Country + + + + SELECT + 선택 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 업데이트 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -536,6 +1234,51 @@ 활성화 + + ParamControlSP + + Enable + 활성화 + + + Cancel + 취소 + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -590,7 +1333,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -630,6 +1373,30 @@ now now + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -672,6 +1439,131 @@ This may take up to a minute. 시스템 재설정이 시작되었습니다. 모든 콘텐츠와 설정을 지우려면 확인을 누르시고 부팅을 재개하려면 취소를 누르세요. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -695,6 +1587,61 @@ This may take up to a minute. 소프트웨어 + + SettingsWindowSP + + × + × + + + Device + 장치 + + + Network + 네트워크 + + + sunnylink + + + + Toggles + 토글 + + + Software + 소프트웨어 + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -888,6 +1835,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + 온도 + + + HIGH + 높음 + + + GOOD + 좋음 + + + OK + OK + + + DISABLED + + + + OFFLINE + 연결 안됨 + + + REGIST... + + + + ONLINE + 온라인 + + + ERROR + 오류 + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -963,6 +2004,233 @@ This may take up to a minute. 업데이트 안함 + + SoftwarePanelSP + + Driving Model + + + + SELECT + 선택 + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + 캘리브레이션 초기화 + + + Warning: You are on a metered connection! + + + + Continue + 계속 + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mph + + SshControl @@ -1009,6 +2277,399 @@ This may take up to a minute. SSH 사용 + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + 동기화 + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1028,15 +2689,30 @@ This may take up to a minute. 동의 + + TermsPageSP + + Terms & Conditions + 이용약관 + + + Decline + 거절 + + + Scroll to accept + 동의하려면 아래로 스크롤하세요 + + TogglesPanel Enable openpilot - openpilot 사용 + openpilot 사용 Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 어댑티브 크루즈 컨트롤 및 차로 유지 보조를 위해 openpilot 시스템을 사용할 수 있습니다. 이 기능을 사용할 때에는 언제나 주의를 기울여야 합니다. 설정을 변경하면 차량 시동이 꺼졌을 때 적용됩니다. + 어댑티브 크루즈 컨트롤 및 차로 유지 보조를 위해 openpilot 시스템을 사용할 수 있습니다. 이 기능을 사용할 때에는 언제나 주의를 기울여야 합니다. 설정을 변경하면 차량 시동이 꺼졌을 때 적용됩니다. Enable Lane Departure Warnings @@ -1072,19 +2748,19 @@ This may take up to a minute. Show ETA in 24h Format - 24시간 형식으로 도착 예정 시간 표시 + 24시간 형식으로 도착 예정 시간 표시 Use 24h format instead of am/pm - 오전/오후 대신 24시간 형식 사용 + 오전/오후 대신 24시간 형식 사용 Show Map on Left Side of UI - UI 왼쪽에 지도 표시 + UI 왼쪽에 지도 표시 Show map on left side when in split screen view. - 분할 화면 보기에서 지도를 왼쪽에 표시합니다. + 분할 화면 보기에서 지도를 왼쪽에 표시합니다. Experimental Mode @@ -1166,6 +2842,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + openpilot 가감속 제어 (알파) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + 실험 모드 + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + 가속페달 조작 시 해제 + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 활성화된 경우 가속 페달을 밟으면 openpilot이 해제됩니다. + + + Enable Lane Departure Warnings + 차선 이탈 경고 활성화 + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 차량이 50km/h(31mph) 이상의 속도로 주행할 때 방향지시등이 켜지지 않은 상태에서 차선을 벗어나면 경고합니다. + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + 운전자 카메라 녹화 및 업로드 + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 운전자 카메라의 영상 데이터를 업로드하여 운전자 모니터링 알고리즘을 개선합니다. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + 미터법 사용 + + + Display speed in km/h instead of mph. + mph 대신 km/h로 속도를 표시합니다. + + + Show ETA in 24h Format + 24시간 형식으로 도착 예정 시간 표시 + + + Use 24h format instead of am/pm + 오전/오후 대신 24시간 형식 사용 + + + Show Map on Left Side of UI + UI 왼쪽에 지도 표시 + + + Show map on left side when in split screen view. + 분할 화면 보기에서 지도를 왼쪽에 표시합니다. + + + Aggressive + 공격적 + + + Moderate + + + + Standard + 표준 + + + Relaxed + 편안한 + + + Driving Personality + 주행 모드 + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + openpilot은 기본적으로 <b>안정 모드</b>로 주행합니다. 실험 모드는 안정화되지 않은 <b>알파 수준의 기능</b>을 활성화합니다. 실험 모드의 기능은 아래와 같습니다: + + + End-to-End Longitudinal Control + E2E 가감속 제어 + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + 새로운 주행 시각화 + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + 차량에 장착된 ACC로 가감속을 제어하기 때문에 현재 이 차량에서는 실험 모드를 사용할 수 없습니다. + + + openpilot longitudinal control may come in a future update. + openpilot 가감속 제어는 향후 업데이트에서 지원될 수 있습니다. + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1202,6 +3124,168 @@ This may take up to a minute. 업데이트 실패 + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1248,4 +3332,27 @@ This may take up to a minute. 삭제 + + WifiUISP + + Scanning for networks... + 네트워크 검색 중... + + + CONNECTING... + 연결 중... + + + FORGET + 삭제 + + + Forget Wi-Fi Network "%1"? + Wi-Fi "%1"에 자동으로 연결하지 않겠습니까? + + + Forget + 삭제 + + diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index feaa6e86a1..856ef26ca8 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -87,6 +87,89 @@ para "%1" + + AdvancedNetworkingSP + + Back + Voltar + + + Enable Tethering + Ativar Tether + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + Senha Tethering + + + EDIT + EDITAR + + + Enter new tethering password + Insira nova senha tethering + + + IP Address + Endereço IP + + + Enable Roaming + Ativar Roaming + + + APN Setting + APN Config + + + Enter APN + Insira APN + + + leave blank for automatic configuration + deixe em branco para configuração automática + + + Cellular Metered + Plano de Dados Limitado + + + Prevent large data uploads when on a metered connection + Evite grandes uploads de dados quando estiver em uma conexão limitada + + + Hidden Network + Rede Oculta + + + CONNECT + + + + Enter SSID + Digite o SSID + + + Enter password + Insira a senha + + + for "%1" + para "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - MAX + MAX LIMIT - VELO + VELO + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + LIMITE + + + SPEED + MAX + + + LIMIT + VELO + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ Cancelar + + CustomOffsetsSettings + + Back + Voltar + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - Você precisa aceitar os Termos e Condições para utilizar openpilot. + Você precisa aceitar os Termos e Condições para utilizar openpilot. Back @@ -135,6 +333,10 @@ Decline, uninstall %1 Rejeitar, desintalar %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - O openpilot requer que o dispositivo seja montado dentro de 4° esquerda ou direita e dentro de 5° para cima ou 9° para baixo. O openpilot está continuamente calibrando, resetar raramente é necessário. + O openpilot requer que o dispositivo seja montado dentro de 4° esquerda ou direita e dentro de 5° para cima ou 9° para baixo. O openpilot está continuamente calibrando, resetar raramente é necessário. Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR PAREAR + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + VER + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + RESET + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + Resetar + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -345,6 +696,79 @@ Instalando... + + LaneChangeSettings + + Back + Voltar + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -386,6 +810,63 @@ Aguardando rota + + MapWindowSP + + Map Loading + Carregando Mapa + + + Waiting for GPS + Aguardando GPS + + + Waiting for route + Aguardando rota + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + hr + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -416,6 +897,33 @@ Senha incorreta + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + Avançado + + + Enter password + Insira a senha + + + for "%1" + para "%1" + + + Wrong password + Senha incorreta + + OffroadAlert @@ -468,6 +976,16 @@ Device temperature too high. System cooling down before starting. Current internal component temperature: %1 Temperatura do dispositivo muito alta. O sistema está sendo resfriado antes de iniciar. A temperatura atual do componente interno é: %1 + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -507,6 +1025,186 @@ Reinicie o Dispositivo + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + min + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + VERIFICAR + + + Country + + + + SELECT + SELECIONE + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + ATUALIZAÇÃO + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -537,6 +1235,51 @@ Ativar + + ParamControlSP + + Enable + Ativar + + + Cancel + Cancelar + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -591,7 +1334,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -634,6 +1377,30 @@ now agora + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -676,6 +1443,131 @@ Isso pode levar até um minuto. Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -699,6 +1591,61 @@ Isso pode levar até um minuto. Software + + SettingsWindowSP + + × + × + + + Device + Dispositivo + + + Network + Rede + + + sunnylink + + + + Toggles + Ajustes + + + Software + Software + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -892,6 +1839,100 @@ Isso pode levar até um minuto. 5G + + SidebarSP + + TEMP + TEMP + + + HIGH + ALTA + + + GOOD + BOA + + + OK + OK + + + DISABLED + + + + OFFLINE + OFFLINE + + + REGIST... + + + + ONLINE + ONLINE + + + ERROR + ERRO + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -967,6 +2008,233 @@ Isso pode levar até um minuto. nunca + + SoftwarePanelSP + + Driving Model + + + + SELECT + SELECIONE + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + Reinicializar Calibragem + + + Warning: You are on a metered connection! + + + + Continue + Continuar + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mph + + SshControl @@ -1013,6 +2281,399 @@ Isso pode levar até um minuto. Habilitar SSH + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + PAREAR + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1032,15 +2693,30 @@ Isso pode levar até um minuto. Concordo + + TermsPageSP + + Terms & Conditions + Termos & Condições + + + Decline + Declinar + + + Scroll to accept + Role a tela para aceitar + + TogglesPanel Enable openpilot - Ativar openpilot + Ativar openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. A alteração desta configuração tem efeito quando o carro é desligado. + Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. A alteração desta configuração tem efeito quando o carro é desligado. Enable Lane Departure Warnings @@ -1076,19 +2752,19 @@ Isso pode levar até um minuto. Show ETA in 24h Format - Mostrar ETA em Formato 24h + Mostrar ETA em Formato 24h Use 24h format instead of am/pm - Use o formato 24h em vez de am/pm + Use o formato 24h em vez de am/pm Show Map on Left Side of UI - Exibir Mapa no Lado Esquerdo + Exibir Mapa no Lado Esquerdo Show map on left side when in split screen view. - Exibir mapa do lado esquerdo quando a tela for dividida. + Exibir mapa do lado esquerdo quando a tela for dividida. Experimental Mode @@ -1170,6 +2846,252 @@ Isso pode levar até um minuto. Enable driver monitoring even when openpilot is not engaged. Habilite o monitoramento do motorista mesmo quando o openpilot não estiver acionado. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + Controle Longitudinal openpilot (Embrionário) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + Modo Experimental + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + Desacionar com Pedal do Acelerador + + + When enabled, pressing the accelerator pedal will disengage openpilot. + Quando ativado, pressionar o pedal do acelerador desacionará o openpilot. + + + Enable Lane Departure Warnings + Ativar Avisos de Saída de Faixa + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + Receba alertas para voltar para a pista se o seu veículo sair da faixa e a seta não tiver sido acionada previamente quando em velocidades superiores a 50 km/h. + + + Always-On Driver Monitoring + Monitoramento do Motorista Sempre Ativo + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + Gravar e Upload Câmera Motorista + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + Upload dados da câmera voltada para o motorista e ajude a melhorar o algoritmo de monitoramentor. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + Usar Sistema Métrico + + + Display speed in km/h instead of mph. + Exibir velocidade em km/h invés de mph. + + + Show ETA in 24h Format + Mostrar ETA em Formato 24h + + + Use 24h format instead of am/pm + Use o formato 24h em vez de am/pm + + + Show Map on Left Side of UI + Exibir Mapa no Lado Esquerdo + + + Show map on left side when in split screen view. + Exibir mapa do lado esquerdo quando a tela for dividida. + + + Aggressive + Disputa + + + Moderate + + + + Standard + Neutro + + + Relaxed + Calmo + + + Driving Personality + Temperamento de Direção + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + openpilot por padrão funciona em <b>modo chill</b>. modo Experimental ativa <b>recursos de nível-embrionário</b> que não estão prontos para o modo chill. Recursos experimentais estão listados abaixo: + + + End-to-End Longitudinal Control + Controle Longitudinal de Ponta a Ponta + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + Nova Visualização de Condução + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + A visualização de condução fará a transição para a câmera grande angular voltada para a estrada em baixas velocidades para mostrar melhor algumas curvas. O logotipo do modo Experimental também será mostrado no canto superior direito. + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + O modo Experimental está atualmente indisponível para este carro já que o ACC original do carro é usado para controle longitudinal. + + + openpilot longitudinal control may come in a future update. + O controle longitudinal openpilot poderá vir em uma atualização futura. + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1206,6 +3128,168 @@ Isso pode levar até um minuto. Falha na atualização + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1252,4 +3336,27 @@ Isso pode levar até um minuto. Esquecer + + WifiUISP + + Scanning for networks... + Procurando redes... + + + CONNECTING... + CONECTANDO... + + + FORGET + ESQUECER + + + Forget Wi-Fi Network "%1"? + Esquecer Rede Wi-Fi "%1"? + + + Forget + Esquecer + + diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index e594a6975f..220b6575c4 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -87,6 +87,89 @@ สำหรับ "%1" + + AdvancedNetworkingSP + + Back + ย้อนกลับ + + + Enable Tethering + ปล่อยฮอตสปอต + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + รหัสผ่านฮอตสปอต + + + EDIT + แก้ไข + + + Enter new tethering password + ป้อนรหัสผ่านฮอตสปอตใหม่ + + + IP Address + หมายเลขไอพี + + + Enable Roaming + เปิดใช้งานโรมมิ่ง + + + APN Setting + ตั้งค่า APN + + + Enter APN + ป้อนค่า APN + + + leave blank for automatic configuration + เว้นว่างเพื่อตั้งค่าอัตโนมัติ + + + Cellular Metered + ลดการส่งข้อมูลผ่านเซลลูล่าร์ + + + Prevent large data uploads when on a metered connection + ปิดการอัพโหลดข้อมูลขนาดใหญ่เมื่อเชื่อมต่อผ่านเซลลูล่าร์ + + + Hidden Network + เครือข่ายที่ซ่อนอยู่ + + + CONNECT + เชื่อมต่อ + + + Enter SSID + ป้อนค่า SSID + + + Enter password + ใส่รหัสผ่าน + + + for "%1" + สำหรับ "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - ความเร็ว + ความเร็ว LIMIT - จำกัด + จำกัด + + + + AnnotatedCameraWidgetSP + + km/h + กม./ชม. + + + mph + ไมล์/ชม. + + + MAX + สูงสุด + + + SPEED + ความเร็ว + + + LIMIT + จำกัด + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ ยกเลิก + + CustomOffsetsSettings + + Back + ย้อนกลับ + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - คุณต้องยอมรับเงื่อนไขและข้อตกลง เพื่อใช้งาน openpilot + คุณต้องยอมรับเงื่อนไขและข้อตกลง เพื่อใช้งาน openpilot Back @@ -135,6 +333,10 @@ Decline, uninstall %1 ปฏิเสธ และถอนการติดตั้ง %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot กำหนดให้ติดตั้งอุปกรณ์ โดยสามารถเอียงด้านซ้ายหรือขวาไม่เกิน 4° และเอียงขึ้นด้านบนไม่เกิน 5° หรือเอียงลงด้านล่างไม่เกิน 9° openpilot ทำการคาลิเบรทอย่างต่อเนื่อง แทบจะไม่จำเป็นต้องทำการรีเซ็ตการคาลิเบรท + openpilot กำหนดให้ติดตั้งอุปกรณ์ โดยสามารถเอียงด้านซ้ายหรือขวาไม่เกิน 4° และเอียงขึ้นด้านบนไม่เกิน 5° หรือเอียงลงด้านล่างไม่เกิน 9° openpilot ทำการคาลิเบรทอย่างต่อเนื่อง แทบจะไม่จำเป็นต้องทำการรีเซ็ตการคาลิเบรท Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR จับคู่ + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + ดู + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + รีเซ็ต + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + รีเซ็ต + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -344,6 +695,79 @@ กำลังติดตั้ง... + + LaneChangeSettings + + Back + ย้อนกลับ + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -385,6 +809,63 @@ กำลังรอเส้นทาง + + MapWindowSP + + Map Loading + กำลังโหลดแผนที่ + + + Waiting for GPS + กำลังรอสัญญาณ GPS + + + Waiting for route + กำลังรอเส้นทาง + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + ม. + + + hr + ชม. + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -415,6 +896,33 @@ รหัสผ่านผิด + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + ขั้นสูง + + + Enter password + ใส่รหัสผ่าน + + + for "%1" + สำหรับ "%1" + + + Wrong password + รหัสผ่านผิด + + OffroadAlert @@ -467,6 +975,16 @@ openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. openpilot ตรวจพบการเปลี่ยนแปลงของตำแหน่งที่ติดตั้ง กรุณาตรวจสอบว่าได้เลื่อนอุปกรณ์เข้ากับจุดติดตั้งจนสุดแล้ว และจุดติดตั้งได้ยึดติดกับกระจกหน้าอย่างแน่นหนา + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -506,6 +1024,186 @@ รีบูตอุปกรณ์ + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + นาที + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + ตรวจสอบ + + + Country + + + + SELECT + เลือก + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + อัปเดต + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -536,6 +1234,51 @@ ยกเลิก + + ParamControlSP + + Enable + เปิดใช้งาน + + + Cancel + ยกเลิก + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + กม./ชม. + + + mph + ไมล์/ชม. + + PrimeAdWidget @@ -590,7 +1333,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -630,6 +1373,30 @@ now ตอนนี้ + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -672,6 +1439,131 @@ This may take up to a minute. ระบบถูกรีเซ็ต กดยืนยันเพื่อลบข้อมูลและการตั้งค่าทั้งหมด กดยกเลิกเพื่อบูตต่อ + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -695,6 +1587,61 @@ This may take up to a minute. ซอฟต์แวร์ + + SettingsWindowSP + + × + × + + + Device + อุปกรณ์ + + + Network + เครือข่าย + + + sunnylink + + + + Toggles + ตัวเลือก + + + Software + ซอฟต์แวร์ + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -888,6 +1835,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + อุณหภูมิ + + + HIGH + สูง + + + GOOD + ดี + + + OK + พอใช้ + + + DISABLED + + + + OFFLINE + ออฟไลน์ + + + REGIST... + + + + ONLINE + ออนไลน์ + + + ERROR + เกิดข้อผิดพลาด + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -963,6 +2004,233 @@ This may take up to a minute. ล่าสุดแล้ว ตรวจสอบครั้งสุดท้ายเมื่อ %1 + + SoftwarePanelSP + + Driving Model + + + + SELECT + เลือก + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + รีเซ็ตการคาลิเบรท + + + Warning: You are on a metered connection! + + + + Continue + ดำเนินการต่อ + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + กม./ชม. + + + mph + ไมล์/ชม. + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + ไม่มี + + + km/h + กม./ชม. + + + mph + ไมล์/ชม. + + SshControl @@ -1009,6 +2277,399 @@ This may take up to a minute. เปิดใช้งาน SSH + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + ไม่มี + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + จับคู่ + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1028,15 +2689,30 @@ This may take up to a minute. ยอมรับ + + TermsPageSP + + Terms & Conditions + ข้อตกลงและเงื่อนไข + + + Decline + ปฏิเสธ + + + Scroll to accept + เลื่อนเพื่อตอบรับข้อตกลง + + TogglesPanel Enable openpilot - เปิดใช้งาน openpilot + เปิดใช้งาน openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - ใช้ระบบ openpilot สำหรับระบบควบคุมความเร็วอัตโนมัติ และระบบช่วยควบคุมรถให้อยู่ในเลน คุณจำเป็นต้องให้ความสนใจตลอดเวลาที่ใช้คุณสมบัตินี้ การเปลี่ยนการตั้งค่านี้จะมีผลเมื่อคุณดับเครื่องยนต์ + ใช้ระบบ openpilot สำหรับระบบควบคุมความเร็วอัตโนมัติ และระบบช่วยควบคุมรถให้อยู่ในเลน คุณจำเป็นต้องให้ความสนใจตลอดเวลาที่ใช้คุณสมบัตินี้ การเปลี่ยนการตั้งค่านี้จะมีผลเมื่อคุณดับเครื่องยนต์ Enable Lane Departure Warnings @@ -1072,19 +2748,19 @@ This may take up to a minute. Show ETA in 24h Format - แสดงเวลา ETA ในรูปแบบ 24 ชั่วโมง + แสดงเวลา ETA ในรูปแบบ 24 ชั่วโมง Use 24h format instead of am/pm - ใช้รูปแบบเวลา 24 ชั่วโมง แทน am/pm + ใช้รูปแบบเวลา 24 ชั่วโมง แทน am/pm Show Map on Left Side of UI - แสดงแผนที่ที่ด้านซ้ายของหน้าจอ + แสดงแผนที่ที่ด้านซ้ายของหน้าจอ Show map on left side when in split screen view. - แสดงแผนที่ด้านซ้ายของหน้าจอเมื่ออยู่ในโหมดแบ่งหน้าจอ + แสดงแผนที่ด้านซ้ายของหน้าจอเมื่ออยู่ในโหมดแบ่งหน้าจอ Experimental Mode @@ -1166,6 +2842,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + ระบบควบคุมการเร่ง/เบรคโดย openpilot (Alpha) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + โหมดทดลอง + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + ยกเลิกระบบช่วยขับเมื่อเหยียบคันเร่ง + + + When enabled, pressing the accelerator pedal will disengage openpilot. + เมื่อเปิดใช้งาน การกดแป้นคันเร่งจะเป็นการยกเลิกระบบช่วยขับโดย openpilot + + + Enable Lane Departure Warnings + เปิดใช้งานการเตือนการออกนอกเลน + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + รับการแจ้งเตือนให้เลี้ยวกลับเข้าเลนเมื่อรถของคุณตรวจพบการข้ามช่องจราจรโดยไม่เปิดสัญญาณไฟเลี้ยวในขณะขับขี่ที่ความเร็วเกิน 31 ไมล์ต่อชั่วโมง (50 กม./ชม) + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + บันทึกและอัปโหลดภาพจากกล้องคนขับ + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + อัปโหลดข้อมูลจากกล้องที่หันหน้าไปทางคนขับ และช่วยปรับปรุงอัลกอริธึมการตรวจสอบผู้ขับขี่ + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + ใช้ระบบเมตริก + + + Display speed in km/h instead of mph. + แสดงความเร็วเป็น กม./ชม. แทน ไมล์/ชั่วโมง + + + Show ETA in 24h Format + แสดงเวลา ETA ในรูปแบบ 24 ชั่วโมง + + + Use 24h format instead of am/pm + ใช้รูปแบบเวลา 24 ชั่วโมง แทน am/pm + + + Show Map on Left Side of UI + แสดงแผนที่ที่ด้านซ้ายของหน้าจอ + + + Show map on left side when in split screen view. + แสดงแผนที่ด้านซ้ายของหน้าจอเมื่ออยู่ในโหมดแบ่งหน้าจอ + + + Aggressive + ดุดัน + + + Moderate + + + + Standard + มาตรฐาน + + + Relaxed + ผ่อนคลาย + + + Driving Personality + บุคลิกการขับขี่ + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + โดยปกติ openpilot จะขับใน<b>โหมดชิล</b> เปิดโหมดทดลองเพื่อใช้<b>ความสามารถในขั้นพัฒนา</b> ซึ่งยังไม่พร้อมสำหรับโหมดชิล ความสามารถในขั้นพัฒนามีดังนี้: + + + End-to-End Longitudinal Control + ควบคุมเร่ง/เบรคแบบ End-to-End + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + การแสดงภาพการขับขี่แบบใหม่ + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + การแสดงภาพการขับขี่จะเปลี่ยนไปใช้กล้องมุมกว้างที่หันหน้าไปทางถนนเมื่ออยู่ในความเร็วต่ำ เพื่อแสดงภาพการเลี้ยวที่ดีขึ้น โลโก้โหมดการทดลองจะแสดงที่มุมบนขวาด้วย + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + ขณะนี้โหมดทดลองไม่สามารถใช้งานได้ในรถคันนี้ เนื่องจากเปิดใช้ระบบควบคุมการเร่ง/เบรคของรถที่ติดตั้งจากโรงงานอยู่ + + + openpilot longitudinal control may come in a future update. + ระบบควบคุมการเร่ง/เบรคโดย openpilot อาจมาในการอัปเดตในอนาคต + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1202,6 +3124,168 @@ This may take up to a minute. การอัปเดตล้มเหลว + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1248,4 +3332,27 @@ This may take up to a minute. เลิกใช้ + + WifiUISP + + Scanning for networks... + กำลังสแกนหาเครือข่าย... + + + CONNECTING... + กำลังเชื่อมต่อ... + + + FORGET + เลิกใช้ + + + Forget Wi-Fi Network "%1"? + เลิกใช้เครือข่าย Wi-Fi "%1"? + + + Forget + เลิกใช้ + + diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 48615f1699..a8c3992fd2 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -87,6 +87,89 @@ için "%1" + + AdvancedNetworkingSP + + Back + + + + Enable Tethering + Kişisel erişim noktasını aç + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + Kişisel erişim noktasının parolası + + + EDIT + DÜZENLE + + + Enter new tethering password + Erişim noktasına yeni bir sonraki başlatılışında çekilir. parola belirleyin. + + + IP Address + IP Adresi + + + Enable Roaming + Hücresel veri aç + + + APN Setting + APN Ayarları + + + Enter APN + APN Gir + + + leave blank for automatic configuration + otomatik yapılandırma için boş bırakın + + + Cellular Metered + + + + Prevent large data uploads when on a metered connection + + + + Hidden Network + + + + CONNECT + BAĞLANTI + + + Enter SSID + APN Gir + + + Enter password + Parolayı girin + + + for "%1" + için "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -101,6 +184,29 @@ MAX MAX + + SPEED + HIZ + + + LIMIT + LİMİT + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + MAX + SPEED HIZ @@ -110,6 +216,91 @@ LİMİT + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + + + ConfirmationDialog @@ -121,11 +312,18 @@ Vazgeç + + CustomOffsetsSettings + + Back + + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - Openpilotu kullanmak için Kullanıcı Koşullarını kabul etmelisiniz. + Openpilotu kullanmak için Kullanıcı Koşullarını kabul etmelisiniz. Back @@ -135,6 +333,10 @@ Decline, uninstall %1 Reddet, Kurulumu kaldır. %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot, cihazın 4° sola veya 5° yukarı yada 9° aşağı bakıcak şekilde monte edilmesi gerekmektedir. openpilot sürekli kendisini kalibre edilmektedir ve nadiren sıfırlama gerebilir. + openpilot, cihazın 4° sola veya 5° yukarı yada 9° aşağı bakıcak şekilde monte edilmesi gerekmektedir. openpilot sürekli kendisini kalibre edilmektedir ve nadiren sıfırlama gerebilir. Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + BAK + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + SIFIRLA + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -344,6 +695,79 @@ Yükleniyor... + + LaneChangeSettings + + Back + + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -385,6 +809,63 @@ + + MapWindowSP + + Map Loading + Harita yükleniyor + + + Waiting for GPS + GPS verisi bekleniyor... + + + Waiting for route + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + saat + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -415,6 +896,33 @@ Yalnış parola + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + Gelişmiş Seçenekler + + + Enter password + Parolayı girin + + + for "%1" + için "%1" + + + Wrong password + Yalnış parola + + OffroadAlert @@ -466,6 +974,16 @@ openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -505,6 +1023,186 @@ + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + dk + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + KONTROL ET + + + Country + + + + SELECT + + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + GÜNCELLE + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -535,6 +1233,51 @@ + + ParamControlSP + + Enable + + + + Cancel + + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -589,7 +1332,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -629,6 +1372,30 @@ now + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -670,6 +1437,131 @@ This may take up to a minute. + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -693,6 +1585,61 @@ This may take up to a minute. Yazılım + + SettingsWindowSP + + × + x + + + Device + Cihaz + + + Network + + + + sunnylink + + + + Toggles + Değiştirme + + + Software + Yazılım + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -886,6 +1833,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + SICAKLIK + + + HIGH + YÜKSEK + + + GOOD + İYİ + + + OK + TAMAM + + + DISABLED + + + + OFFLINE + ÇEVRİMDIŞI + + + REGIST... + + + + ONLINE + ÇEVRİMİÇİ + + + ERROR + HATA + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -961,6 +2002,233 @@ This may take up to a minute. + + SoftwarePanelSP + + Driving Model + + + + SELECT + + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + Kalibrasyonu sıfırla + + + Warning: You are on a metered connection! + + + + Continue + Devam et + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mph + + SshControl @@ -1007,6 +2275,399 @@ This may take up to a minute. SSH aç + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1026,15 +2687,30 @@ This may take up to a minute. Kabul et + + TermsPageSP + + Terms & Conditions + Şartlar ve Koşullar + + + Decline + Reddet + + + Scroll to accept + Kabul etmek için kaydırın + + TogglesPanel Enable openpilot - openpilot'u aktifleştir + openpilot'u aktifleştir Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Ayarlanabilir hız sabitleyici ve şeritte kalma yardımı için openpilot sistemini kullanın. Bu özelliği kullanırken her zaman dikkatli olmanız gerekiyor. Bu ayarın değiştirilmesi için araç kapatılıp açılması gerekiyor. + Ayarlanabilir hız sabitleyici ve şeritte kalma yardımı için openpilot sistemini kullanın. Bu özelliği kullanırken her zaman dikkatli olmanız gerekiyor. Bu ayarın değiştirilmesi için araç kapatılıp açılması gerekiyor. Enable Lane Departure Warnings @@ -1066,19 +2742,19 @@ This may take up to a minute. Show ETA in 24h Format - Tahmini varış süresini 24 saat formatı şeklinde göster + Tahmini varış süresini 24 saat formatı şeklinde göster Use 24h format instead of am/pm - 24 saat formatını kullan + 24 saat formatını kullan Show Map on Left Side of UI - Haritayı arayüzün sol tarafında göster + Haritayı arayüzün sol tarafında göster Show map on left side when in split screen view. - Bölünmüş ekran görünümündeyken haritayı sol tarafta göster. + Bölünmüş ekran görünümündeyken haritayı sol tarafta göster. openpilot Longitudinal Control (Alpha) @@ -1164,6 +2840,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + Aktifleştirilirse eğer gaz pedalına basınca openpilot devre dışı kalır. + + + Enable Lane Departure Warnings + Şerit ihlali uyarı alın + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 50 km/s (31 mph) hızın üzerinde sürüş sırasında aracınız dönüş sinyali vermeden algılanan bir sonraki başlatılışında çekilir. şerit çizgisi ihlalinde şeride geri dönmek için uyarılar alın. + + + Always-On Driver Monitoring + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + Sürücü kamerasını kayıt et. + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + Sürücüye bakan kamera verisini yükleyin ve Cihazın algoritmasını geliştirmemize yardımcı olun. + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + Metrik sistemi kullan + + + Display speed in km/h instead of mph. + Hızı mph yerine km/h şeklinde görüntüleyin. + + + Show ETA in 24h Format + Tahmini varış süresini 24 saat formatı şeklinde göster + + + Use 24h format instead of am/pm + 24 saat formatını kullan + + + Show Map on Left Side of UI + Haritayı arayüzün sol tarafında göster + + + Show map on left side when in split screen view. + Bölünmüş ekran görünümündeyken haritayı sol tarafta göster. + + + Aggressive + + + + Moderate + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + End-to-End Longitudinal Control + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + + + + openpilot longitudinal control may come in a future update. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1200,6 +3122,168 @@ This may take up to a minute. Güncelleme başarız oldu + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1246,4 +3330,27 @@ This may take up to a minute. + + WifiUISP + + Scanning for networks... + Ağ aranıyor... + + + CONNECTING... + BAĞLANILIYOR... + + + FORGET + UNUT + + + Forget Wi-Fi Network "%1"? + Wi-Fi ağını unut "%1"? + + + Forget + + + diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 32119ee10f..fddf413a50 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -87,6 +87,89 @@ 网络名称:"%1" + + AdvancedNetworkingSP + + Back + 返回 + + + Enable Tethering + 启用WiFi热点 + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + WiFi热点密码 + + + EDIT + 编辑 + + + Enter new tethering password + 输入新的WiFi热点密码 + + + IP Address + IP地址 + + + Enable Roaming + 启用数据漫游 + + + APN Setting + APN设置 + + + Enter APN + 输入APN + + + leave blank for automatic configuration + 留空以自动配置 + + + Cellular Metered + 按流量计费的手机移动网络 + + + Prevent large data uploads when on a metered connection + 当使用按流量计费的连接时,避免上传大流量数据 + + + Hidden Network + 隐藏的网络 + + + CONNECT + + + + Enter SSID + 输入 SSID + + + Enter password + 输入密码 + + + for "%1" + 网络名称:"%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - SPEED + SPEED LIMIT - LIMIT + LIMIT + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + 最高定速 + + + SPEED + SPEED + + + LIMIT + LIMIT + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ 取消 + + CustomOffsetsSettings + + Back + 返回 + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - 您必须接受条款和条件以使用openpilot。 + 您必须接受条款和条件以使用openpilot。 Back @@ -135,6 +333,10 @@ Decline, uninstall %1 拒绝并卸载%1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot要求设备安装的偏航角在左4°和右4°之间,俯仰角在上5°和下9°之间。一般来说,openpilot会持续更新校准,很少需要重置。 + openpilot要求设备安装的偏航角在左4°和右4°之间,俯仰角在上5°和下9°之间。一般来说,openpilot会持续更新校准,很少需要重置。 Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR 配对 + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + 查看 + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + 重置 + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + 重置 + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -344,6 +695,79 @@ 正在安装…… + + LaneChangeSettings + + Back + 返回 + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -385,6 +809,63 @@ 等待路线 + + MapWindowSP + + Map Loading + 地图加载中 + + + Waiting for GPS + 等待 GPS + + + Waiting for route + 等待路线 + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + 小时 + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -415,6 +896,33 @@ 密码错误 + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + 高级 + + + Enter password + 输入密码 + + + for "%1" + 网络名称:"%1" + + + Wrong password + 密码错误 + + OffroadAlert @@ -467,6 +975,16 @@ Device temperature too high. System cooling down before starting. Current internal component temperature: %1 设备温度过高。系统正在冷却中,等冷却完毕后才会启动。目前内部组件温度:%1 + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -506,6 +1024,186 @@ 重启设备 + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + 分钟 + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 查看 + + + Country + + + + SELECT + 选择 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 更新 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -536,6 +1234,51 @@ 启用 + + ParamControlSP + + Enable + 启用 + + + Cancel + 取消 + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -590,7 +1333,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -630,6 +1373,30 @@ now 现在 + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -672,6 +1439,131 @@ This may take up to a minute. 系统重置已触发。按下“确认”以清除所有内容和设置,按下“取消”以继续启动。 + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -695,6 +1587,61 @@ This may take up to a minute. 软件 + + SettingsWindowSP + + × + × + + + Device + 设备 + + + Network + 网络 + + + sunnylink + + + + Toggles + 设定 + + + Software + 软件 + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -888,6 +1835,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + 设备温度 + + + HIGH + 过热 + + + GOOD + 良好 + + + OK + 一般 + + + DISABLED + + + + OFFLINE + 离线 + + + REGIST... + + + + ONLINE + 在线 + + + ERROR + 连接出错 + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -963,6 +2004,233 @@ This may take up to a minute. 从未更新 + + SoftwarePanelSP + + Driving Model + + + + SELECT + 选择 + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + 重置设备校准 + + + Warning: You are on a metered connection! + + + + Continue + 继续 + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + N/A + + + km/h + km/h + + + mph + mph + + SshControl @@ -1009,6 +2277,399 @@ This may take up to a minute. 启用SSH + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + N/A + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + 配对 + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1028,15 +2689,30 @@ This may take up to a minute. 同意 + + TermsPageSP + + Terms & Conditions + 条款和条件 + + + Decline + 拒绝 + + + Scroll to accept + 滑动以接受 + + TogglesPanel Enable openpilot - 启用openpilot + 启用openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 使用openpilot进行自适应巡航和车道保持辅助。使用此功能时您必须时刻保持注意力。该设置的更改在熄火时生效。 + 使用openpilot进行自适应巡航和车道保持辅助。使用此功能时您必须时刻保持注意力。该设置的更改在熄火时生效。 Enable Lane Departure Warnings @@ -1072,19 +2748,19 @@ This may take up to a minute. Show ETA in 24h Format - 以24小时格式显示预计到达时间 + 以24小时格式显示预计到达时间 Use 24h format instead of am/pm - 使用24小时制代替am/pm + 使用24小时制代替am/pm Show Map on Left Side of UI - 在介面左侧显示地图 + 在介面左侧显示地图 Show map on left side when in split screen view. - 在分屏模式中,将地图置于屏幕左侧。 + 在分屏模式中,将地图置于屏幕左侧。 Experimental Mode @@ -1166,6 +2842,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 即使在openpilot未激活时也启用驾驶员监控。 + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + openpilot纵向控制(Alpha 版) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + 测试模式 + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + 踩油门时取消控制 + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 启用后,踩下油门踏板将取消openpilot。 + + + Enable Lane Departure Warnings + 启用车道偏离警告 + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 车速超过31mph(50km/h)时,若检测到车辆越过车道线且未打转向灯,系统将发出警告以提醒您返回车道。 + + + Always-On Driver Monitoring + 驾驶员监控常开 + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + 录制并上传驾驶员摄像头 + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 上传驾驶员摄像头的数据,帮助改进驾驶员监控算法。 + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + 使用公制单位 + + + Display speed in km/h instead of mph. + 显示车速时,以km/h代替mph。 + + + Show ETA in 24h Format + 以24小时格式显示预计到达时间 + + + Use 24h format instead of am/pm + 使用24小时制代替am/pm + + + Show Map on Left Side of UI + 在介面左侧显示地图 + + + Show map on left side when in split screen view. + 在分屏模式中,将地图置于屏幕左侧。 + + + Aggressive + 积极 + + + Moderate + + + + Standard + 标准 + + + Relaxed + 舒适 + + + Driving Personality + 驾驶风格 + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + openpilot 默认 <b>轻松模式</b>驾驶车辆。试验模式启用一些轻松模式之外的 <b>试验性功能</b>。试验性功能包括: + + + End-to-End Longitudinal Control + 端到端纵向控制 + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + 新驾驶视角 + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + 在低速时,驾驶可视化将转换为道路朝向的广角摄像头,以更好地展示某些转弯。测试模式标志也将显示在右上角。 + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + 由于此车辆使用自带的ACC纵向控制,当前无法使用试验模式。 + + + openpilot longitudinal control may come in a future update. + openpilot纵向控制可能会在未来的更新中提供。 + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1202,6 +3124,168 @@ This may take up to a minute. 更新失败 + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1248,4 +3332,27 @@ This may take up to a minute. 忽略 + + WifiUISP + + Scanning for networks... + 正在扫描网络…… + + + CONNECTING... + 正在连接…… + + + FORGET + 忽略 + + + Forget Wi-Fi Network "%1"? + 忽略WiFi网络 "%1"? + + + Forget + 忽略 + + diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 9d1c16db9f..684447b113 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -87,6 +87,89 @@ 給 "%1" + + AdvancedNetworkingSP + + Back + 回上頁 + + + Enable Tethering + 啟用網路分享 + + + Retain hotspot/tethering state + + + + Enabling this toggle will retain the hotspot/tethering toggle state across reboots. + + + + Tethering Password + 網路分享密碼 + + + EDIT + 編輯 + + + Enter new tethering password + 輸入新的網路分享密碼 + + + IP Address + IP 地址 + + + Enable Roaming + 啟用漫遊 + + + APN Setting + APN 設置 + + + Enter APN + 輸入 APN + + + leave blank for automatic configuration + 留空白將自動配置 + + + Cellular Metered + 行動網路 + + + Prevent large data uploads when on a metered connection + 防止使用行動網路上傳大量的數據 + + + Hidden Network + 隱藏的網路 + + + CONNECT + + + + Enter SSID + 輸入 SSID + + + Enter password + 輸入密碼 + + + for "%1" + 給 "%1" + + + Ngrok Service + + + AnnotatedCameraWidget @@ -103,11 +186,119 @@ SPEED - 速度 + 速度 LIMIT - 速限 + 速限 + + + + AnnotatedCameraWidgetSP + + km/h + km/h + + + mph + mph + + + MAX + 最高 + + + SPEED + 速度 + + + LIMIT + 速限 + + + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + BackupSettings + + Settings backed up for sunnylink Device ID: + + + + Settings updated successfully, but no additional data was returned by the server. + + + + OOPS! We made a booboo. + + + + Please try again later. + + + + Settings restored. Confirm to restart the interface. + + + + No settings found to restore. + + + + + BrightnessControl + + Brightness + + + + Manually adjusts the global brightness of the screen. + + + + Auto + + + + + CameraOffset + + Camera Offset - Laneful Only + + + + Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm + + + + cm + @@ -121,11 +312,18 @@ 取消 + + CustomOffsetsSettings + + Back + 回上頁 + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - 您必須先接受條款和條件才能使用 openpilot。 + 您必須先接受條款和條件才能使用 openpilot。 Back @@ -135,6 +333,10 @@ Decline, uninstall %1 拒絕並解除安裝 %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DestinationWidget @@ -247,7 +449,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot 需要將裝置固定在左右偏差 4° 以內,朝上偏差 5° 以內或朝下偏差 9° 以內。鏡頭在後台會持續自動校準,很少有需要重置的情況。 + openpilot 需要將裝置固定在左右偏差 4° 以內,朝上偏差 5° 以內或朝下偏差 9° 以內。鏡頭在後台會持續自動校準,很少有需要重置的情況。 Your device is pointed %1° %2 and %3° %4. @@ -305,6 +507,155 @@ PAIR 配對 + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. sunnypilot is continuously calibrating, resetting is rarely required. + + + + + DevicePanelSP + + TOGGLE + + + + Enable or disable PIN requirement for Fleet Manager access. + + + + Are you sure you want to turn off PIN requirement? + + + + Turn Off + + + + Error Troubleshoot + + + + VIEW + 觀看 + + + Display error from the tmux session when an error has occurred from a system process. + + + + Reset Access Tokens for Map Services + + + + RESET + 重設 + + + Reset self-service access tokens for Mapbox, Amap, and Google Maps. + + + + Are you sure you want to reset access tokens for all map services? + + + + Reset + 重設 + + + Reset sunnypilot Settings + + + + Are you sure you want to reset all sunnypilot settings? + + + + Toggle Onroad/Offroad + + + + OFF + + + + Are you sure you want to unforce offroad? + + + + Unforce + + + + Are you sure you want to force offroad? + + + + Force + + + + Disengage to Force Offroad + + + + Unforce Offroad + + + + Force Offroad + + + + Fleet Manager PIN: + + + + + DisplayPanel + + Driving Screen Off: Non-Critical Events + + + + When <b>Driving Screen Off Timer</b> is not set to <b>"Always On"</b>: + + + + Enabled: Wake the brightness of the screen to display all events. + + + + Disabled: Wake the brightness of the screen to display critical events. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -344,6 +695,79 @@ 安裝中… + + LaneChangeSettings + + Back + 回上頁 + + + Pause Lateral Below Speed with Blinker + + + + Enable this toggle to pause lateral actuation with blinker when traveling below the desired speed selected below. + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + Block Lane Change: Road Edge Detection + + + + Enable this toggle to block lane change when road edge is detected on the stalk actuated side. + + + + + MadsSettings + + Enable ACC+MADS with RES+/SET- + + + + Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button. + + + + Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off. + + + + Toggle M.A.D.S. with Cruise Main + + + + Allows M.A.D.S. engagement/disengagement with "Cruise Main" cruise control button from the steering wheel. + + + + Remain Active + + + + Pause Steering + + + + Steering Mode After Braking + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + +Remain Active: ALC will remain active even after the brake pedal is pressed. +Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + + MapETA @@ -385,6 +809,63 @@ 等待路線 + + MapWindowSP + + Map Loading + 地圖載入中 + + + Waiting for GPS + 等待 GPS + + + Waiting for route + 等待路線 + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road). + + + + s + + + + m + m + + + hr + 小時 + + + Always On + + + + Immediate + + + + + MonitoringPanel + + Enable Hands on Wheel Monitoring + + + + Monitor and alert when driver is not keeping the hands on the steering wheel. + + + MultiOptionDialog @@ -415,6 +896,33 @@ 密碼錯誤 + + NetworkingSP + + Scan + + + + Scanning... + + + + Advanced + 進階 + + + Enter password + 輸入密碼 + + + for "%1" + 給 "%1" + + + Wrong password + 密碼錯誤 + + OffroadAlert @@ -467,6 +975,16 @@ Device temperature too high. System cooling down before starting. Current internal component temperature: %1 裝置溫度過高。系統正在冷卻中,等冷卻完畢後才會啟動。目前內部組件溫度:%1 + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + + + sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to "Settings" -> "Device" -> "Unforce Offroad" to exit Force Offroad mode. + + OffroadHome @@ -506,6 +1024,186 @@ 請重新啟裝置 + + OnroadScreenOff + + Driving Screen Off Timer + + + + Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs. + + + + s + + + + min + 分鐘 + + + Always On + + + + + OnroadScreenOffBrightness + + Driving Screen Off Brightness (%) + + + + When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio. + + + + Dark + + + + + OnroadSettings + + ONROAD OPTIONS + + + + <b>ONROAD SETTINGS | SUNNYPILOT</b> + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 檢查 + + + Country + + + + SELECT + 選取 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 更新 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + + PairingPopup @@ -536,6 +1234,51 @@ 啟用 + + ParamControlSP + + Enable + 啟用 + + + Cancel + 取消 + + + + PathOffset + + Path Offset + + + + Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately. + + + + cm + + + + + PauseLateralSpeed + + Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h. + + + + Default + + + + km/h + km/h + + + mph + mph + + PrimeAdWidget @@ -590,7 +1333,7 @@ openpilot - openpilot + openpilot %n minute(s) ago @@ -630,6 +1373,30 @@ now 現在 + + sunnypilot + + + + Update downloaded. Ready to reboot. + + + + Update: Check and Download Update + + + + Reboot: Reboot Device + + + + Update + + + + Updating... + + Reset @@ -672,6 +1439,131 @@ This may take up to a minute. 系統重設已啟動。按下「確認」以清除所有內容和設定,或按下「取消」以繼續開機。 + + SPVehiclesTogglesPanel + + Hyundai/Kia/Genesis + + + + HKG CAN: Smoother Stopping Performance (Beta) + + + + Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control. + + + + Subaru + + + + Manual Parking Brake: Stop and Go (Beta) + + + + Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation! + + + + Toyota/Lexus + + + + Enable Stock Toyota Longitudinal Control + + + + sunnypilot will <b>not</b> take over control of gas and brakes. Stock Toyota longitudinal control will be used. + + + + Allow M.A.D.S. toggling w/ LKAS Button (Beta) + + + + Allows M.A.D.S. engagement/disengagement with "LKAS" button from the steering wheel. + + + + Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Toyota TSS2 Longitudinal: Custom Tuning + + + + Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + + + Enable Enhanced Blind Spot Monitor + + + + Enable Toyota Stop and Go Hack + + + + sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk. + + + + Enable Toyota Door Auto Locking + + + + sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Toyota Door Auto Unlocking + + + + sunnypilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + Volkswagen + + + + Enable CC Only support + + + + sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory. + + + + Start the car to check car compatibility + + + + This platform is already supported, therefore no need to enable this toggle + + + + This platform is not supported + + + + This platform can be supported + + + + sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects. + + + + Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2. + + + SettingsWindow @@ -695,6 +1587,61 @@ This may take up to a minute. 軟體 + + SettingsWindowSP + + × + × + + + Device + 裝置 + + + Network + 網路 + + + sunnylink + + + + Toggles + 設定 + + + Software + 軟體 + + + sunnypilot + + + + OSM + + + + Monitoring + + + + Visuals + + + + Display + + + + Trips + + + + Vehicle + + + Setup @@ -888,6 +1835,100 @@ This may take up to a minute. 5G + + SidebarSP + + TEMP + 溫度 + + + HIGH + 偏高 + + + GOOD + 正常 + + + OK + 一般 + + + DISABLED + + + + OFFLINE + 已離線 + + + REGIST... + + + + ONLINE + 已連線 + + + ERROR + 錯誤 + + + SUNNYLINK + + + + + SlcSettings + + Auto + + + + User Confirm + + + + Engage Mode + + + + Default + + + + Fixed + + + + Percentage + + + + Limit Offset + + + + Set speed limit slightly higher than actual speed limit for a more natural drive. + + + + This platform defaults to <b>Auto</b> mode. <b>User Confirm</b> mode is not supported on this platform. + + + + Select the desired mode to set the cruising speed to the speed limit: + + + + Auto: Automatic speed adjustment on motorways based on speed limit data. + + + + User Confirm: Inform the driver to change set speed of Adaptive Cruise Control to help the driver stay within the speed limit. + + + SoftwarePanel @@ -963,6 +2004,233 @@ This may take up to a minute. 從未更新 + + SoftwarePanelSP + + Driving Model + + + + SELECT + 選取 + + + PENDING + + + + Downloading Driving model + + + + (CACHED) + + + + Driving model + + + + downloaded + + + + Downloading Navigation model + + + + Navigation model + + + + Downloading Metadata model + + + + Metadata model + + + + Downloads have failed, please try swapping the model! + + + + Failed: + + + + Fetching models... + + + + Select a Driving Model + + + + Download has started in the background. + + + + We STRONGLY suggest you to reset calibration. Would you like to do that now? + + + + Reset Calibration + 重設校準 + + + Warning: You are on a metered connection! + + + + Continue + 繼續 + + + on Metered + + + + + SpeedLimitPolicySettings + + Speed Limit Source Policy + + + + Nav + + + + Only + + + + Map + + + + Car + + + + First + + + + Select the precedence order of sources. Utilized by Speed Limit Control and Speed Limit Warning + + + + Nav Only: Data from Mapbox active navigation only. + + + + Map Only: Data from OpenStreetMap only. + + + + Car Only: Data from the car's built-in sources (if available). + + + + Nav First: Nav -> Map -> Car + + + + Map First: Map -> Nav -> Car + + + + Car First: Car -> Nav -> Map + + + + + SpeedLimitValueOffset + + km/h + km/h + + + mph + mph + + + + SpeedLimitWarningSettings + + Off + + + + Display + + + + Chime + + + + Speed Limit Warning + + + + Warning with speed limit flash + + + + When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset. + + + + Default + + + + Fixed + + + + Percentage + + + + Warning Offset + + + + Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit. + + + + Off: When the cruising speed is faster than the speed limit plus the offset, there will be no warning. + + + + Display: The speed on the speed limit sign turns red to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + Chime: The speed on the speed limit sign turns red and chimes to alert the driver when the cruising speed is faster than the speed limit plus the offset. + + + + + SpeedLimitWarningValueOffset + + N/A + 無法使用 + + + km/h + km/h + + + mph + mph + + SshControl @@ -1009,6 +2277,399 @@ This may take up to a minute. 啟用 SSH 服務 + + SunnylinkPanel + + Enable sunnylink + + + + Device ID + + + + N/A + 無法使用 + + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + 配對 + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + Manage Settings + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + THANKS + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + Backing up... + + + + Restoring... + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + + + SunnypilotPanel + + Enable M.A.D.S. + + + + Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement. + + + + Laneless for Curves in "Auto" Mode + + + + While in Auto Lane, switch to Laneless for current/future curves. + + + + Speed Limit Control (SLC) + + + + When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed. + + + + Enable Vision-based Turn Speed Control (V-TSC) + + + + Use vision path predictions to estimate the appropriate speed to drive through turns ahead. + + + + Enable Map Data Turn Speed Control (M-TSC) (Beta) + + + + Use curvature information from map data to define speed limits to take turns ahead. + + + + ACC +/-: Long Press Reverse + + + + Change the ACC +/- buttons behavior with cruise speed change in sunnypilot. + + + + Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric) + + + + Enabled: Short = 5 (imperial) / 10 (metric), Long=1 + + + + Custom Offsets + + + + Neural Network Lateral Control (NNLC) + + + + Enforce Torque Lateral Control + + + + Enable this to enforce sunnypilot to steer with Torque lateral control. + + + + Enable Self-Tune + + + + Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default. + + + + Less Restrict Settings for Self-Tune (Beta) + + + + Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values. + + + + Enable Custom Tuning + + + + Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within "selfdrive/torque_data". The values will also be used live when "Override Self-Tune" toggle is enabled. + + + + Manual Real-Time Tuning + + + + Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values. + + + + Quiet Drive 🤫 + + + + sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on. + + + + Green Traffic Light Chime (Beta) + + + + A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged. + + + + Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly. + + + + Lead Vehicle Departure Alert + + + + Enable this will notify when the leading vehicle drives away. + + + + Customize M.A.D.S. + + + + Customize Lane Change + + + + Customize Offsets + + + + Customize Speed Limit Control + + + + Customize Warning + + + + Customize Source + + + + Laneful + + + + Laneless + + + + Auto + + + + Dynamic Lane Profile + + + + Speed Limit Assist + + + + Real-time and Offline + + + + Offline Only + + + + Dynamic Lane Profile is not available with the current Driving Model + + + + Custom Offsets is not available with the current Driving Model + + + + NNLC is currently not available on this platform. + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Fuzzy + + + + Exact + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: + + + + Match + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: + + + + Add custom offsets to Camera and Path in sunnypilot. + + + + Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions. + + + TermsPage @@ -1028,15 +2689,30 @@ This may take up to a minute. 接受 + + TermsPageSP + + Terms & Conditions + 條款和條件 + + + Decline + 拒絕 + + + Scroll to accept + 滑動至頁尾接受條款 + + TogglesPanel Enable openpilot - 啟用 openpilot + 啟用 openpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 使用 openpilot 的主動式巡航和車道保持功能,開啟後您需要持續集中注意力,設定變更在重新啟動車輛後生效。 + 使用 openpilot 的主動式巡航和車道保持功能,開啟後您需要持續集中注意力,設定變更在重新啟動車輛後生效。 Enable Lane Departure Warnings @@ -1072,19 +2748,19 @@ This may take up to a minute. Show ETA in 24h Format - 預計到達時間單位改用 24 小時制 + 預計到達時間單位改用 24 小時制 Use 24h format instead of am/pm - 使用 24 小時制。(預設值為 12 小時制) + 使用 24 小時制。(預設值為 12 小時制) Show Map on Left Side of UI - 將地圖顯示在畫面的左側 + 將地圖顯示在畫面的左側 Show map on left side when in split screen view. - 進入分割畫面後,地圖將會顯示在畫面的左側。 + 進入分割畫面後,地圖將會顯示在畫面的左側。 Experimental Mode @@ -1166,6 +2842,252 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 即使在openpilot未激活時也啟用駕駛監控。 + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + + TogglesPanelSP + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + + + + openpilot Longitudinal Control (Alpha) + openpilot 縱向控制 (Alpha 版) + + + WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + + + + On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha. + + + + Custom Stock Longitudinal Control + + + + When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses. +This feature must be used along with SLC, and/or V-TSC, and/or M-TSC. + + + + Experimental Mode + 實驗模式 + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + Enable Dynamic Personality + + + + Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your "Driving Personality" setting. Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car. + + + + Disengage on Accelerator Pedal + 油門取消控車 + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 啟用後,踩踏油門將會取消 openpilot 控制。 + + + Enable Lane Departure Warnings + 啟用車道偏離警告 + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 車速在時速 50 公里 (31 英里) 以上且未打方向燈的情況下,如果偵測到車輛駛出目前車道線時,發出車道偏離警告。 + + + Always-On Driver Monitoring + 駕駛監控常開 + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Record and Upload Driver Camera + 記錄並上傳駕駛監控影像 + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 上傳駕駛監控的錄影來協助我們提升駕駛監控的準確率。 + + + Disable Onroad Uploads + + + + Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC). + + + + Use Metric System + 使用公制單位 + + + Display speed in km/h instead of mph. + 啟用後,速度單位顯示將從 mp/h 改為 km/h。 + + + Show ETA in 24h Format + 預計到達時間單位改用 24 小時制 + + + Use 24h format instead of am/pm + 使用 24 小時制。(預設值為 12 小時制) + + + Show Map on Left Side of UI + 將地圖顯示在畫面的左側 + + + Show map on left side when in split screen view. + 進入分割畫面後,地圖將會顯示在畫面的左側。 + + + Aggressive + 積極 + + + Moderate + + + + Standard + 標準 + + + Relaxed + 舒適 + + + Driving Personality + 駕駛風格 + + + Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + Sport + + + + Normal + + + + Eco + + + + Stock + + + + Acceleration Personality + + + + Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these acceleration personality within Onroad Settings on the driving screen. + + + + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + openpilot 預設以 <b>輕鬆模式</b> 駕駛。 實驗模式啟用了尚未準備好進入輕鬆模式的 <b>alpha 級功能</b>。實驗功能如下: + + + End-to-End Longitudinal Control + 端到端縱向控制 + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + New Driving Visualization + 新的駕駛視覺介面 + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + 在低速時,駕駛可視化將切換至道路朝向的廣角攝影機,以更好地顯示某些彎道。在右上角還會顯示「實驗模式」的標誌。 + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + 因車輛使用內建ACC系統,無法在本車輛上啟動實驗模式。 + + + openpilot longitudinal control may come in a future update. + openpilot 縱向控制可能會在未來的更新中提供。 + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TorqueFriction + + FRICTION + + + + Adjust Friction for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + + + + TorqueMaxLatAccel + + LAT_ACCEL_FACTOR + + + + Adjust Max Lateral Acceleration for the Torque Lateral Controller. <b>Live</b>: Override self-tune values; <b>Offline</b>: Override self-tune offline values at car restart. + + + + Real-time and Offline + + + + Offline Only + + Updater @@ -1202,6 +3124,168 @@ This may take up to a minute. 更新失敗 + + VehiclePanel + + Updating this setting takes effect when the car is powered off. + + + + Select your car + + + + + VisualsPanel + + Display Braking Status + + + + Enable this will turn the current speed value to red while the brake is used. + + + + Display Stand Still Timer + + + + Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions). + + + + Display DM Camera in Reverse Gear + + + + Show Driver Monitoring camera while the car is in reverse gear. + + + + OSM: Show debug UI elements + + + + OSM: Show UI elements that aid debugging. + + + + Display Feature Status + + + + Display the statuses of certain features on the driving screen. + + + + Enable Onroad Settings + + + + Display the Onroad Settings button on the driving screen to adjust feature options on the driving screen, without navigating into the settings menu. + + + + Speedometer: Display True Speed + + + + Display the true vehicle current speed from wheel speed sensors. + + + + Speedometer: Hide from Onroad Screen + + + + Display End-to-end Longitudinal Status (Beta) + + + + Enable this will display an icon that appears when the End-to-end model decides to start or stop. + + + + Navigation: Display in Full Screen + + + + Enable this will display the built-in navigation in full screen.<br>To switch back to driving view, <font color='yellow'>tap on the border edge</font>. + + + + Map: Display 3D Buildings + + + + Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation. + + + + Off + + + + 5 Metrics + + + + 10 Metrics + + + + Developer UI + + + + Display real-time parameters and metrics from various sources. + + + + Distance + + + + Speed + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + + + RAM + + + + CPU + + + + GPU + + + + Max + + + + Display Temperature on Sidebar + + + + Time + + + + All + + + WiFiPromptWidget @@ -1248,4 +3332,27 @@ This may take up to a minute. 清除 + + WifiUISP + + Scanning for networks... + 掃描無線網路中... + + + CONNECTING... + 連線中... + + + FORGET + 清除 + + + Forget Wi-Fi Network "%1"? + 清除 Wi-Fi 網路 "%1"? + + + Forget + 清除 + + diff --git a/system/manager/manager.py b/system/manager/manager.py index 032fc0a667..0f34a5e57f 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -99,7 +99,6 @@ def manager_init() -> None: ("TorqueDeadzoneDeg", "0"), ("TorqueFriction", "1"), ("TorqueMaxLatAccel", "250"), - ("ToyotaAutoHold", "0"), ("ToyotaAutoLockBySpeed", "0"), ("ToyotaAutoUnlockByShifter", "0"), ("ToyotaEnhancedBsm", "0"), From 447f76d9cf7fac394b932c2b6446bf5cf1d054ef Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 29 Jul 2024 10:16:46 +0000 Subject: [PATCH 11/12] ui: Split sunnypilot into its own classes --- SConstruct | 53 +- selfdrive/navd/main.cc | 4 + selfdrive/navd/map_renderer.cc | 4 + selfdrive/ui/SConscript | 19 +- selfdrive/ui/main.cc | 7 +- selfdrive/ui/qt/api.cc | 162 +- selfdrive/ui/qt/api.h | 18 +- selfdrive/ui/qt/body.h | 4 + selfdrive/ui/qt/home.cc | 172 +- selfdrive/ui/qt/home.h | 43 +- selfdrive/ui/qt/maps/map.cc | 71 +- selfdrive/ui/qt/maps/map.h | 17 +- selfdrive/ui/qt/maps/map_eta.cc | 7 +- selfdrive/ui/qt/maps/map_helpers.cc | 4 + selfdrive/ui/qt/maps/map_helpers.h | 3 +- selfdrive/ui/qt/maps/map_instructions.cc | 7 +- selfdrive/ui/qt/maps/map_panel.cc | 8 +- selfdrive/ui/qt/maps/map_settings.h | 4 + selfdrive/ui/qt/network/networking.cc | 65 +- selfdrive/ui/qt/network/networking.h | 11 +- .../qt/network/sunnylink/models/user_model.h | 33 - .../sunnylink/services/base_device_service.cc | 48 - .../sunnylink/services/base_device_service.h | 29 - .../sunnylink/services/role_service.cc | 29 - .../network/sunnylink/services/role_service.h | 26 - .../sunnylink/services/user_service.cc | 31 - .../network/sunnylink/services/user_service.h | 26 - .../qt/network/sunnylink/sunnylink_client.cc | 7 - .../qt/network/sunnylink/sunnylink_client.h | 18 - selfdrive/ui/qt/network/wifi_manager.cc | 4 + selfdrive/ui/qt/offroad/experimental_mode.cc | 7 +- selfdrive/ui/qt/offroad/onboarding.cc | 20 +- selfdrive/ui/qt/offroad/onboarding.h | 17 +- selfdrive/ui/qt/offroad/settings.cc | 362 +--- selfdrive/ui/qt/offroad/settings.h | 53 +- selfdrive/ui/qt/offroad/software_settings.cc | 17 +- .../sunnypilot/custom_offsets_settings.h | 45 - .../qt/offroad/sunnypilot/display_settings.h | 69 - .../ui/qt/offroad/sunnypilot/json_fetcher.h | 35 - .../offroad/sunnypilot/lane_change_settings.h | 57 - .../ui/qt/offroad/sunnypilot/mads_settings.h | 25 - .../offroad/sunnypilot/monitoring_settings.cc | 30 - .../offroad/sunnypilot/monitoring_settings.h | 17 - .../sunnypilot/speed_limit_policy_settings.cc | 51 - .../offroad/sunnypilot/sunnypilot_settings.h | 84 - .../qt/offroad/sunnypilot/trips_settings.cc | 29 - .../ui/qt/offroad/sunnypilot/trips_settings.h | 17 - .../qt/offroad/sunnypilot/vehicle_settings.h | 59 - .../qt/offroad/sunnypilot/visuals_settings.h | 19 - selfdrive/ui/qt/offroad/sunnypilot_main.h | 11 - selfdrive/ui/qt/offroad_home.cc | 152 ++ selfdrive/ui/qt/offroad_home.h | 55 + selfdrive/ui/qt/onroad/annotated_camera.cc | 1403 +-------------- selfdrive/ui/qt/onroad/annotated_camera.h | 176 +- selfdrive/ui/qt/onroad/buttons.cc | 60 +- selfdrive/ui/qt/onroad/buttons.h | 37 +- selfdrive/ui/qt/onroad/onroad_home.cc | 111 +- selfdrive/ui/qt/onroad/onroad_home.h | 46 +- selfdrive/ui/qt/onroad_settings_panel.cc | 26 - selfdrive/ui/qt/onroad_settings_panel.h | 23 - selfdrive/ui/qt/request_repeater.cc | 34 +- selfdrive/ui/qt/request_repeater.h | 23 +- selfdrive/ui/qt/sidebar.cc | 89 +- selfdrive/ui/qt/sidebar.h | 16 +- selfdrive/ui/qt/text.cc | 72 - selfdrive/ui/qt/util.cc | 116 +- selfdrive/ui/qt/util.h | 15 +- selfdrive/ui/qt/widgets/cameraview.h | 4 + selfdrive/ui/qt/widgets/controls.cc | 70 +- selfdrive/ui/qt/widgets/controls.h | 337 +--- selfdrive/ui/qt/widgets/scrollview.cc | 8 - selfdrive/ui/qt/widgets/scrollview.h | 7 - selfdrive/ui/qt/widgets/ssh_keys.h | 6 + .../ui/qt/widgets/sunnypilot/drive_stats.h | 25 - selfdrive/ui/qt/widgets/toggle.cc | 4 +- selfdrive/ui/qt/widgets/toggle.h | 3 +- selfdrive/ui/qt/widgets/wifi.h | 4 + selfdrive/ui/qt/window.cc | 13 +- selfdrive/ui/qt/window.h | 13 +- selfdrive/ui/sunnypilot/SConscript | 75 +- selfdrive/ui/sunnypilot/qt/api.cc | 212 +++ selfdrive/ui/sunnypilot/qt/api.h | 55 + .../ui/sunnypilot/qt/common/json_fetcher.h | 60 + selfdrive/ui/sunnypilot/qt/home.cc | 80 + selfdrive/ui/sunnypilot/qt/home.h | 59 + selfdrive/ui/sunnypilot/qt/maps/map.cc | 293 ++++ selfdrive/ui/sunnypilot/qt/maps/map.h | 53 + selfdrive/ui/sunnypilot/qt/maps/map_helpers.h | 38 + .../ui/sunnypilot/qt/network/networking.cc | 448 +++++ .../ui/sunnypilot/qt/network/networking.h | 128 ++ .../qt/network/sunnylink/models/role_model.h | 31 +- .../sunnylink/models/sponsor_role_model.h | 33 +- .../qt/network/sunnylink/models/user_model.h | 56 + .../sunnylink/services/base_device_service.cc | 76 + .../sunnylink/services/base_device_service.h | 50 + .../sunnylink/services/role_service.cc | 55 + .../network/sunnylink/services/role_service.h | 51 + .../sunnylink/services/user_service.cc | 57 + .../network/sunnylink/services/user_service.h | 51 + .../qt/network/sunnylink/sunnylink_client.cc | 33 + .../qt/network/sunnylink/sunnylink_client.h | 41 + .../qt/offroad/settings/device_panel.cc | 189 ++ .../qt/offroad/settings/device_panel.h | 52 + .../qt/offroad/settings}/display_settings.cc | 53 +- .../qt/offroad/settings/display_settings.h | 98 ++ .../offroad/settings/monitoring_settings.cc | 58 + .../qt/offroad/settings/monitoring_settings.h | 46 + .../qt/offroad/settings/onboarding.cc | 123 ++ .../qt/offroad/settings/onboarding.h | 57 + .../offroad/settings/osm}/locations_fetcher.h | 35 +- .../offroad/settings/osm}/models_fetcher.cc | 31 +- .../qt/offroad/settings/osm}/models_fetcher.h | 36 +- .../qt/offroad/settings}/osm_settings.cc | 79 +- .../qt/offroad/settings}/osm_settings.h | 68 +- .../qt/offroad/settings/settings.cc | 446 +++++ .../sunnypilot/qt/offroad/settings/settings.h | 66 + .../qt/offroad/settings/software_settings.cc} | 48 +- .../qt/offroad/settings/software_settings.h} | 39 +- .../offroad/settings}/sunnylink_settings.cc | 97 +- .../qt/offroad/settings}/sunnylink_settings.h | 42 +- .../sunnypilot/custom_offsets_settings.cc | 40 +- .../sunnypilot/custom_offsets_settings.h | 74 + .../sunnypilot/lane_change_settings.cc | 53 +- .../sunnypilot/lane_change_settings.h | 86 + .../settings}/sunnypilot/mads_settings.cc | 53 +- .../settings/sunnypilot/mads_settings.h | 54 + .../speed_limit_control_settings.cc | 56 +- .../sunnypilot/speed_limit_control_settings.h | 44 +- .../sunnypilot/speed_limit_policy_settings.cc | 76 + .../sunnypilot/speed_limit_policy_settings.h | 37 +- .../speed_limit_warning_settings.cc | 61 +- .../sunnypilot/speed_limit_warning_settings.h | 46 +- .../offroad/settings}/sunnypilot_settings.cc | 98 +- .../qt/offroad/settings/sunnypilot_settings.h | 115 ++ .../qt/offroad/settings/trips_settings.cc | 55 + .../qt/offroad/settings/trips_settings.h | 47 + .../qt/offroad/settings}/vehicle_settings.cc | 93 +- .../qt/offroad/settings/vehicle_settings.h | 82 + .../qt/offroad/settings}/visuals_settings.cc | 60 +- .../qt/offroad/settings/visuals_settings.h | 48 + selfdrive/ui/sunnypilot/qt/offroad_home.cc | 58 + selfdrive/ui/sunnypilot/qt/offroad_home.h | 45 + .../sunnypilot/qt/onroad/annotated_camera.cc | 1535 +++++++++++++++++ .../sunnypilot/qt/onroad/annotated_camera.h | 230 +++ selfdrive/ui/sunnypilot/qt/onroad/buttons.cc | 96 ++ selfdrive/ui/sunnypilot/qt/onroad/buttons.h | 68 + .../qt/onroad/developer_ui/developer_ui.cc | 224 +++ .../qt/onroad/developer_ui/developer_ui.h | 46 + .../qt/onroad/developer_ui/ui_elements.h | 39 + .../ui/sunnypilot/qt/onroad/onroad_home.cc | 163 ++ .../ui/sunnypilot/qt/onroad/onroad_home.h | 72 + .../qt/onroad}/onroad_settings.cc | 78 +- .../qt/onroad}/onroad_settings.h | 37 +- .../qt/onroad/onroad_settings_panel.cc | 51 + .../qt/onroad/onroad_settings_panel.h | 49 + .../ui/sunnypilot/qt/request_repeater.cc | 62 + selfdrive/ui/sunnypilot/qt/request_repeater.h | 52 + selfdrive/ui/sunnypilot/qt/sidebar.cc | 132 ++ selfdrive/ui/sunnypilot/qt/sidebar.h | 57 + selfdrive/ui/sunnypilot/qt/text.cc | 162 ++ selfdrive/ui/sunnypilot/qt/ui_scene.h | 115 ++ selfdrive/ui/sunnypilot/qt/util.cc | 104 ++ selfdrive/ui/sunnypilot/qt/util.h | 39 + .../ui/sunnypilot/qt/widgets/controls.cc | 247 +++ selfdrive/ui/sunnypilot/qt/widgets/controls.h | 582 +++++++ .../qt/widgets}/drive_stats.cc | 29 +- .../ui/sunnypilot/qt/widgets/drive_stats.h | 51 + .../ui/sunnypilot/qt/widgets/scrollview.cc | 37 + .../ui/sunnypilot/qt/widgets/scrollview.h | 43 + selfdrive/ui/sunnypilot/qt/widgets/toggle.cc | 49 + selfdrive/ui/sunnypilot/qt/widgets/toggle.h | 39 + selfdrive/ui/sunnypilot/qt/window.cc | 44 + selfdrive/ui/sunnypilot/qt/window.h | 45 + selfdrive/ui/sunnypilot/sunnypilot_main.h | 45 + selfdrive/ui/sunnypilot/ui.cc | 348 ++++ selfdrive/ui/sunnypilot/ui.h | 154 ++ selfdrive/ui/translations/main_ar.ts | 16 + selfdrive/ui/translations/main_de.ts | 16 + selfdrive/ui/translations/main_es.ts | 16 + selfdrive/ui/translations/main_fr.ts | 16 + selfdrive/ui/translations/main_ja.ts | 16 + selfdrive/ui/translations/main_ko.ts | 16 + selfdrive/ui/translations/main_pt-BR.ts | 16 + selfdrive/ui/translations/main_th.ts | 16 + selfdrive/ui/translations/main_tr.ts | 16 + selfdrive/ui/translations/main_zh-CHS.ts | 16 + selfdrive/ui/translations/main_zh-CHT.ts | 16 + selfdrive/ui/ui.cc | 210 +-- selfdrive/ui/ui.h | 196 +-- system/manager/process_config.py | 16 +- tools/cabana/detailwidget.h | 5 + 191 files changed, 10742 insertions(+), 4970 deletions(-) delete mode 100644 selfdrive/ui/qt/network/sunnylink/models/user_model.h delete mode 100644 selfdrive/ui/qt/network/sunnylink/services/base_device_service.cc delete mode 100644 selfdrive/ui/qt/network/sunnylink/services/base_device_service.h delete mode 100644 selfdrive/ui/qt/network/sunnylink/services/role_service.cc delete mode 100644 selfdrive/ui/qt/network/sunnylink/services/role_service.h delete mode 100644 selfdrive/ui/qt/network/sunnylink/services/user_service.cc delete mode 100644 selfdrive/ui/qt/network/sunnylink/services/user_service.h delete mode 100644 selfdrive/ui/qt/network/sunnylink/sunnylink_client.cc delete mode 100644 selfdrive/ui/qt/network/sunnylink/sunnylink_client.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/display_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/json_fetcher.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.cc delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot_main.h create mode 100644 selfdrive/ui/qt/offroad_home.cc create mode 100644 selfdrive/ui/qt/offroad_home.h delete mode 100644 selfdrive/ui/qt/onroad_settings_panel.cc delete mode 100644 selfdrive/ui/qt/onroad_settings_panel.h delete mode 100644 selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h create mode 100644 selfdrive/ui/sunnypilot/qt/api.cc create mode 100644 selfdrive/ui/sunnypilot/qt/api.h create mode 100644 selfdrive/ui/sunnypilot/qt/common/json_fetcher.h create mode 100644 selfdrive/ui/sunnypilot/qt/home.cc create mode 100644 selfdrive/ui/sunnypilot/qt/home.h create mode 100644 selfdrive/ui/sunnypilot/qt/maps/map.cc create mode 100644 selfdrive/ui/sunnypilot/qt/maps/map.h create mode 100644 selfdrive/ui/sunnypilot/qt/maps/map_helpers.h create mode 100644 selfdrive/ui/sunnypilot/qt/network/networking.cc create mode 100644 selfdrive/ui/sunnypilot/qt/network/networking.h rename selfdrive/ui/{ => sunnypilot}/qt/network/sunnylink/models/role_model.h (53%) rename selfdrive/ui/{ => sunnypilot}/qt/network/sunnylink/models/sponsor_role_model.h (67%) create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/display_settings.cc (71%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.h rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings/osm}/locations_fetcher.h (64%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings/osm}/models_fetcher.cc (80%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings/osm}/models_fetcher.h (75%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/osm_settings.cc (76%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/osm_settings.h (77%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h rename selfdrive/ui/{qt/offroad/sunnypilot/software_settings_sp.cc => sunnypilot/qt/offroad/settings/software_settings.cc} (86%) rename selfdrive/ui/{qt/offroad/sunnypilot/software_settings_sp.h => sunnypilot/qt/offroad/settings/software_settings.h} (64%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/sunnylink_settings.cc (85%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/sunnylink_settings.h (58%) rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/custom_offsets_settings.cc (54%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.h rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/lane_change_settings.cc (68%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.h rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/mads_settings.cc (52%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/speed_limit_control_settings.cc (68%) rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/speed_limit_control_settings.h (50%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.cc rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/speed_limit_policy_settings.h (59%) rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/speed_limit_warning_settings.cc (64%) rename selfdrive/ui/{qt/offroad => sunnypilot/qt/offroad/settings}/sunnypilot/speed_limit_warning_settings.h (50%) rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/sunnypilot_settings.cc (85%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.h rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/vehicle_settings.cc (75%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.h rename selfdrive/ui/{qt/offroad/sunnypilot => sunnypilot/qt/offroad/settings}/visuals_settings.cc (68%) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad_home.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad_home.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/buttons.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/buttons.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h rename selfdrive/ui/{qt => sunnypilot/qt/onroad}/onroad_settings.cc (86%) rename selfdrive/ui/{qt => sunnypilot/qt/onroad}/onroad_settings.h (51%) create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.h create mode 100644 selfdrive/ui/sunnypilot/qt/request_repeater.cc create mode 100644 selfdrive/ui/sunnypilot/qt/request_repeater.h create mode 100644 selfdrive/ui/sunnypilot/qt/sidebar.cc create mode 100644 selfdrive/ui/sunnypilot/qt/sidebar.h create mode 100644 selfdrive/ui/sunnypilot/qt/text.cc create mode 100644 selfdrive/ui/sunnypilot/qt/ui_scene.h create mode 100644 selfdrive/ui/sunnypilot/qt/util.cc create mode 100644 selfdrive/ui/sunnypilot/qt/util.h create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/controls.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/controls.h rename selfdrive/ui/{qt/widgets/sunnypilot => sunnypilot/qt/widgets}/drive_stats.cc (73%) create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/drive_stats.h create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/scrollview.h create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/toggle.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/toggle.h create mode 100644 selfdrive/ui/sunnypilot/qt/window.cc create mode 100644 selfdrive/ui/sunnypilot/qt/window.h create mode 100644 selfdrive/ui/sunnypilot/sunnypilot_main.h create mode 100644 selfdrive/ui/sunnypilot/ui.cc create mode 100644 selfdrive/ui/sunnypilot/ui.h diff --git a/SConstruct b/SConstruct index 13da37c7a8..1ccd76e740 100644 --- a/SConstruct +++ b/SConstruct @@ -7,8 +7,6 @@ import numpy as np import SCons.Errors -from openpilot.common.basedir import BASEDIR - SCons.Warnings.warningAsException(True) # pending upstream fix - https://github.com/SCons/scons/issues/4461 @@ -18,45 +16,6 @@ TICI = os.path.isfile('/TICI') AGNOS = TICI UBUNTU_FOCAL = int(subprocess.check_output('[ -f /etc/os-release ] && . /etc/os-release && [ "$ID" = "ubuntu" ] && [ "$VERSION_ID" = "20.04" ] && echo 1 || echo 0', shell=True, encoding='utf-8').rstrip()) Export('UBUNTU_FOCAL') -_DEBUG = False - -def is_internal_developer(debug=False): - def collect_required_gpg_key_ids(keys_dir): - try: - key_ids = [f.split('.')[0] for f in os.listdir(keys_dir) if f.endswith(".gpg")] - if debug: - print(f"SP: Required GPG key IDs: {key_ids}") - return key_ids - except OSError as e: - if debug: - print(f"SP: Failed to read GPG key IDs from {keys_dir}. Error: {e}") - return [] - - def is_key_available(required_gpg_key_ids): - for key_id in required_gpg_key_ids: - try: - result = subprocess.check_output(['gpg', '--list-keys', key_id], stderr=subprocess.STDOUT) - if key_id in result.decode(): - if debug: - print(f"SP: GPG key {key_id} is available.") - return True - except subprocess.CalledProcessError as e: - if debug: - print(f"SP: Failed to list GPG key {key_id}. Error:", e.output.decode().strip()) - return False - - keys_dir = os.path.join(BASEDIR, ".git-crypt/keys/default/0") - required_gpg_key_ids = collect_required_gpg_key_ids(keys_dir) - - sunnypilot = is_key_available(required_gpg_key_ids) - - if sunnypilot: - print("SP: Confirmed sunnypilot internal developer.") - print("SP: Loading sunnypilot elements ...") - elif debug: - print("SP: None of the required GPG keys are available.") - - return sunnypilot Decider('MD5-timestamp') @@ -113,11 +72,11 @@ AddOption('--minimal', default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') -AddOption('--sunnypilot', +AddOption('--stock-ui', action='store_true', - dest='sunnypilot', - default=is_internal_developer(_DEBUG), # check if the current user is a sunnypilot developer - help='build sunnypilot elements and other sunnypilot-specific items that are meant for internal development') + dest='stock_ui', + default=False, + help='Build stock UI instead of sunnypilot UI') ## Architecture name breakdown (arch) ## - larch64: linux tici aarch64 @@ -222,6 +181,10 @@ if arch != "Darwin": cflags += ['-DSWAGLOG="\\"common/swaglog.h\\""'] cxxflags += ['-DSWAGLOG="\\"common/swaglog.h\\""'] +if not GetOption('stock_ui'): + cflags += ["-DSUNNYPILOT"] + cxxflags += ["-DSUNNYPILOT"] + ccflags_option = GetOption('ccflags') if ccflags_option: ccflags += ccflags_option.split(' ') diff --git a/selfdrive/navd/main.cc b/selfdrive/navd/main.cc index 2e7b4d3b60..da188bb26b 100644 --- a/selfdrive/navd/main.cc +++ b/selfdrive/navd/main.cc @@ -6,7 +6,11 @@ #include "common/util.h" #include "selfdrive/ui/qt/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#else #include "selfdrive/ui/qt/maps/map_helpers.h" +#endif #include "selfdrive/navd/map_renderer.h" #include "system/hardware/hw.h" diff --git a/selfdrive/navd/map_renderer.cc b/selfdrive/navd/map_renderer.cc index d52ee162bd..f24e6bb876 100644 --- a/selfdrive/navd/map_renderer.cc +++ b/selfdrive/navd/map_renderer.cc @@ -8,7 +8,11 @@ #include "common/util.h" #include "common/timing.h" #include "common/swaglog.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#else #include "selfdrive/ui/qt/maps/map_helpers.h" +#endif const float DEFAULT_ZOOM = 13.5; // Don't go below 13 or features will start to disappear const int HEIGHT = 256, WIDTH = 256; diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 3d49b3df14..a825a74ab0 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -18,30 +18,32 @@ if arch == "Darwin": qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] sp_widgets_src = [] +sp_maps_widgets_src = [] sp_qt_src = [] -if GetOption('sunnypilot'): +sp_qt_util = [] +if not GetOption('stock_ui'): SConscript(['sunnypilot/SConscript']) - Import('sp_widgets_src', 'sp_qt_src') + Import('sp_widgets_src', 'sp_maps_widgets_src', 'sp_qt_src', "sp_qt_util") -qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) +qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"] + sp_qt_util, LIBS=base_libs) widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src -qt_env['CPPDEFINES'] = ["SUNNYPILOT"] if GetOption('sunnypilot') else [] +qt_env['CPPDEFINES'] = [] if maps: base_libs += ['QMapLibre'] widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc", - "qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"] + "qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"] + sp_maps_widgets_src qt_env['CPPDEFINES'] += ["ENABLE_MAPS"] widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) Export('widgets') qt_libs = [widgets, qt_util] + base_libs -qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc", +qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc", "qt/offroad_home.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", @@ -79,14 +81,15 @@ asset_obj = qt_env.Object("assets", assets) qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) # spinner and text window -qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs) +text_cc_path = "qt/text.cc" if GetOption('stock_ui') else "sunnypilot/qt/text.cc" +qt_env.Program("_text", [text_cc_path], LIBS=qt_libs) qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs) # build main UI qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) if GetOption('extras'): qt_src.remove("main.cc") # replaced by test_runner - #qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) + qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) qt_env.Program('tests/ui_snapshot', [asset_obj, "tests/ui_snapshot.cc"] + qt_src, LIBS=qt_libs) diff --git a/selfdrive/ui/main.cc b/selfdrive/ui/main.cc index 4903a3db3d..baf09c79f1 100644 --- a/selfdrive/ui/main.cc +++ b/selfdrive/ui/main.cc @@ -6,7 +6,13 @@ #include "system/hardware/hw.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/window.h" +#define MainWindow MainWindowSP +#else #include "selfdrive/ui/qt/window.h" +#endif int main(int argc, char *argv[]) { setpriority(PRIO_PROCESS, 0, -20); @@ -22,7 +28,6 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); a.installTranslator(&translator); - MainWindow w; setMainWindow(&w); a.installEventFilter(&w); diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc index f3314221b8..80019f406e 100644 --- a/selfdrive/ui/qt/api.cc +++ b/selfdrive/ui/qt/api.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -10,12 +9,10 @@ #include #include #include -#include #include #include -#include "common/swaglog.h" #include "common/util.h" #include "system/hardware/hw.h" #include "selfdrive/ui/qt/util.h" @@ -48,130 +45,11 @@ QByteArray rsa_sign(const QByteArray &data) { return sig; } -void derive_aes_key_iv_from_rsa(EVP_PKEY *rsa_key, unsigned char *aes_key, unsigned char *aes_iv) { - unsigned char hash[SHA256_DIGEST_LENGTH]; - size_t pub_len; - unsigned char *pub_key; - - // Convert RSA key to public key in DER format for simplicity - pub_len = i2d_PublicKey(rsa_key, NULL); - pub_key = (unsigned char *)malloc(pub_len); - unsigned char *tmp = pub_key; - i2d_PublicKey(rsa_key, &tmp); - - // Hash the public key to derive bytes for AES key and IV - SHA256(pub_key, pub_len, hash); - - // Assuming AES-256-CBC, we need 32 bytes for the key and 16 for the IV - memcpy(aes_key, hash, 32); // First 32 bytes for AES key - memcpy(aes_iv, hash + 32 - 16, 16); // Last 16 bytes for AES IV - - free(pub_key); -} - -EVP_PKEY *load_public_key(const char *file) { - EVP_PKEY *key = NULL; - FILE *fp = fopen(file, "r"); - if (fp) { - key = PEM_read_PUBKEY(fp, NULL, NULL, NULL); - fclose(fp); - } - return key; -} - -EVP_PKEY *load_private_key(const char *file) { - EVP_PKEY *key = NULL; - FILE *fp = fopen(file, "r"); - if (fp) { - key = PEM_read_PrivateKey(fp, NULL, NULL, NULL); - fclose(fp); - } - return key; -} - -QByteArray rsa_encrypt(const QByteArray &data) { - EVP_PKEY *rsa_key = load_public_key(Path::rsa_pub_file().c_str()); // Load your RSA key - unsigned char aes_key[32], aes_iv[16]; - derive_aes_key_iv_from_rsa(rsa_key, aes_key, aes_iv); - - EVP_CIPHER_CTX *ctx; - if (!(ctx = EVP_CIPHER_CTX_new())) { - // Handle error: Allocate memory failed - return {}; - } - - if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, aes_key, aes_iv) != 1) { - qDebug() << "Failed to initialize encryption"; - EVP_CIPHER_CTX_free(ctx); - return {}; - } - - int ciphertext_len; - int len = data.size(); - unsigned char out[len + AES_BLOCK_SIZE]; - auto *in = (unsigned char*) data.constData(); - if (EVP_EncryptUpdate(ctx, out, &ciphertext_len, in, len) != 1) { - qDebug() << "Failed to update encryption"; - EVP_CIPHER_CTX_free(ctx); - return {}; - } - - if (EVP_EncryptFinal_ex(ctx, out + ciphertext_len, &len) != 1) { - // Handle error: Encryption finalize failed - qDebug() << "Failed to finalize encryption"; - EVP_CIPHER_CTX_free(ctx); - return {}; - } - - //qDebug() << "Encrypted data length: %d", ciphertext_len + len; // Print the length of encrypted data - EVP_CIPHER_CTX_free(ctx); - return {reinterpret_cast(out), ciphertext_len + len}; -} - -QByteArray rsa_decrypt(const QByteArray &data) { - EVP_PKEY *rsa_key = load_public_key(Path::rsa_pub_file().c_str()); // Load your RSA key - unsigned char aes_key[32], aes_iv[16]; - derive_aes_key_iv_from_rsa(rsa_key, aes_key, aes_iv); - - EVP_CIPHER_CTX *ctx; - if (!(ctx = EVP_CIPHER_CTX_new())) { - qDebug() << "Failed to allocate memory for EVP_CIPHER_CTX"; - return {}; - } - - if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, aes_key, aes_iv) != 1) { - qDebug() << "Failed to initialize EVP"; - EVP_CIPHER_CTX_free(ctx); - return {}; - } - - int len = data.size(); - unsigned char out[len + AES_BLOCK_SIZE]; - auto *in = (unsigned char*) data.constData(); - if (EVP_DecryptUpdate(ctx, out, &len, in, len) != 1) { - qDebug() << "Failed to update decryption"; - EVP_CIPHER_CTX_free(ctx); - return {}; - } - - int final_len; - if (EVP_DecryptFinal_ex(ctx, out + len, &final_len) != 1) { - qDebug() << "Failed to finalize decryption"; - EVP_CIPHER_CTX_free(ctx); - return {}; - } - EVP_CIPHER_CTX_free(ctx); - - //qDebug() << "Decrypted data length: %d", len + final_len; // Print the length of decrypted data - return {reinterpret_cast(out), len + final_len}; -} - -QString create_jwt(const QJsonObject &payloads, int expiry, bool sunnylink) { +QString create_jwt(const QJsonObject &payloads, int expiry) { QJsonObject header = {{"alg", "RS256"}}; auto t = QDateTime::currentSecsSinceEpoch(); - auto dongle_id = sunnylink ? getSunnylinkDongleId() : getDongleId(); - QJsonObject payload = {{"identity", dongle_id.value_or("")}, {"nbf", t}, {"iat", t}, {"exp", t + expiry}}; + QJsonObject payload = {{"identity", getDongleId().value_or("")}, {"nbf", t}, {"iat", t}, {"exp", t + expiry}}; for (auto it = payloads.begin(); it != payloads.end(); ++it) { payload.insert(it.key(), it.value()); } @@ -186,7 +64,7 @@ QString create_jwt(const QJsonObject &payloads, int expiry, bool sunnylink) { } // namespace CommaApi -HttpRequest::HttpRequest(QObject *parent, bool create_jwt, int timeout, const bool sunnylink) : create_jwt(create_jwt), sunnylink(sunnylink), QObject(parent) { +HttpRequest::HttpRequest(QObject *parent, bool create_jwt, int timeout) : create_jwt(create_jwt), QObject(parent) { networkTimer = new QTimer(this); networkTimer->setSingleShot(true); networkTimer->setInterval(timeout); @@ -201,40 +79,38 @@ bool HttpRequest::timeout() const { return reply && reply->error() == QNetworkReply::OperationCanceledError; } -void HttpRequest::sendRequest(const QString &requestURL, const HttpRequest::Method method, const QByteArray &payload) { - LOGD("Requesting %s", qPrintable(requestURL)); - if (active()) { - qDebug() << "HttpRequest is active"; - return; - } +QNetworkRequest HttpRequest::prepareRequest(const QString &requestURL) +{ + QNetworkRequest request; QString token; if (create_jwt) { - token = CommaApi::create_jwt({}, 3600, sunnylink); + token = GetJwtToken(); } else { QString token_json = QString::fromStdString(util::read_file(util::getenv("HOME") + "/.comma/auth.json")); QJsonDocument json_d = QJsonDocument::fromJson(token_json.toUtf8()); token = json_d["access_token"].toString(); } - QNetworkRequest request; request.setUrl(QUrl(requestURL)); - request.setRawHeader("User-Agent", getUserAgent(sunnylink).toUtf8()); - if (!payload.isEmpty()) { - request.setRawHeader("Content-Type", "application/json"); - } + request.setRawHeader("User-Agent", GetUserAgent().toUtf8()); if (!token.isEmpty()) { request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8()); } + return request; +} - if (method == HttpRequest::Method::GET) { +void HttpRequest::sendRequest(const QString &requestURL, const Method method) { + if (active()) { + qDebug() << "HttpRequest is active"; + return; + } + + QNetworkRequest request = prepareRequest(requestURL); + if (method == Method::GET) { reply = nam()->get(request); - } else if (method == HttpRequest::Method::DELETE) { + } else if (method == Method::DELETE) { reply = nam()->deleteResource(request); - } else if (method == HttpRequest::Method::POST) { - reply = nam()->post(request, payload); - } else if (method == HttpRequest::Method::PUT) { - reply = nam()->put(request, payload); } networkTimer->start(); diff --git a/selfdrive/ui/qt/api.h b/selfdrive/ui/qt/api.h index bbebc502e5..f0e21f56a8 100644 --- a/selfdrive/ui/qt/api.h +++ b/selfdrive/ui/qt/api.h @@ -6,14 +6,13 @@ #include #include "common/util.h" +#include "selfdrive/ui/qt/util.h" namespace CommaApi { const QString BASE_URL = util::getenv("API_HOST", "https://api.commadotai.com").c_str(); QByteArray rsa_sign(const QByteArray &data); -QByteArray rsa_encrypt(const QByteArray &data); -QByteArray rsa_decrypt(const QByteArray &data); -QString create_jwt(const QJsonObject &payloads = {}, int expiry = 3600, bool sunnylink = false); +QString create_jwt(const QJsonObject &payloads = {}, int expiry = 3600); } // namespace CommaApi @@ -27,8 +26,9 @@ class HttpRequest : public QObject { public: enum class Method {GET, DELETE, POST, PUT}; - explicit HttpRequest(QObject* parent, bool create_jwt = true, int timeout = 20000, const bool sunnylink = false); - void sendRequest(const QString &requestURL, const Method method = Method::GET, const QByteArray &payload = {}); + explicit HttpRequest(QObject* parent, bool create_jwt = true, int timeout = 20000); + virtual void sendRequest(const QString &requestURL, Method method); + void sendRequest(const QString &requestURL) { sendRequest(requestURL, Method::GET);} bool active() const; bool timeout() const; @@ -37,14 +37,14 @@ signals: protected: QNetworkReply *reply = nullptr; - -private: static QNetworkAccessManager *nam(); QTimer *networkTimer = nullptr; bool create_jwt; - bool sunnylink; + virtual QNetworkRequest prepareRequest(const QString& requestURL); + virtual QString GetJwtToken() const { return CommaApi::create_jwt(); } + virtual QString GetUserAgent() const { return getUserAgent(); } -private slots: +protected slots: void requestTimeout(); void requestFinished(); }; diff --git a/selfdrive/ui/qt/body.h b/selfdrive/ui/qt/body.h index 567a54d49b..ac3cd2ce1c 100644 --- a/selfdrive/ui/qt/body.h +++ b/selfdrive/ui/qt/body.h @@ -5,7 +5,11 @@ #include #include "common/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif class RecordButton : public QPushButton { Q_OBJECT diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index 48de10a79c..68ab992095 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -9,10 +9,6 @@ #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/prime.h" -#ifdef ENABLE_MAPS -#include "selfdrive/ui/qt/maps/map_settings.h" -#endif - // HomeWindow: the container for the offroad and onroad UIs HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) { @@ -32,8 +28,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) { slayout->addWidget(home); onroad = new OnroadWindow(this); - QObject::connect(onroad, &OnroadWindow::mapPanelRequested, this, [=] { sidebar->hide(); }); - QObject::connect(onroad, &OnroadWindow::onroadSettingsPanelRequested, this, [=] { sidebar->hide(); }); slayout->addWidget(onroad); body = new BodyWindow(this); @@ -54,10 +48,6 @@ void HomeWindow::showSidebar(bool show) { sidebar->setVisible(show); } -void HomeWindow::showMapPanel(bool show) { - onroad->showMapPanel(show); -} - void HomeWindow::updateState(const UIState &s) { const SubMaster &sm = *(s.sm); @@ -66,9 +56,6 @@ void HomeWindow::updateState(const UIState &s) { body->setEnabled(true); slayout->setCurrentWidget(body); } - - uiState()->scene.map_visible = onroad->isMapVisible(); - uiState()->scene.onroad_settings_visible = onroad->isOnroadSettingsVisible(); } void HomeWindow::offroadTransition(bool offroad) { @@ -92,23 +79,9 @@ void HomeWindow::showDriverView(bool show) { } void HomeWindow::mousePressEvent(QMouseEvent* e) { - if (uiState()->scene.started) { - if (uiState()->scene.onroadScreenOff != -2) { - uiState()->scene.touched2 = true; - QTimer::singleShot(500, []() { uiState()->scene.touched2 = false; }); - } - if (uiState()->scene.button_auto_hide) { - uiState()->scene.touch_to_wake = true; - uiState()->scene.sleep_btn_fading_in = true; - QTimer::singleShot(500, []() { uiState()->scene.touch_to_wake = false; }); - } - } - // Handle sidebar collapsing if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) { - if (onroad->wakeScreenTimeout()) { - sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible()); - } + sidebar->setVisible(!sidebar->isVisible()); } } @@ -124,146 +97,3 @@ void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { showSidebar(false); } } - -// OffroadHome: the offroad home page - -OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(40, 40, 40, 40); - - // top header - QHBoxLayout* header_layout = new QHBoxLayout(); - header_layout->setContentsMargins(0, 0, 0, 0); - header_layout->setSpacing(16); - - update_notif = new QPushButton(tr("UPDATE")); - update_notif->setVisible(false); - update_notif->setStyleSheet("background-color: #364DEF;"); - QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); }); - header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); - - alert_notif = new QPushButton(); - alert_notif->setVisible(false); - alert_notif->setStyleSheet("background-color: #E22C2C;"); - QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); }); - header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); - - version = new ElidedLabel(); - header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); - - main_layout->addLayout(header_layout); - - // main content - main_layout->addSpacing(25); - center_layout = new QStackedLayout(); - - QWidget *home_widget = new QWidget(this); - { - QHBoxLayout *home_layout = new QHBoxLayout(home_widget); - home_layout->setContentsMargins(0, 0, 0, 0); - home_layout->setSpacing(30); - - // left: MapSettings/PrimeAdWidget - QStackedWidget *left_widget = new QStackedWidget(this); -#ifdef ENABLE_MAPS - left_widget->addWidget(new MapSettings); -#else - left_widget->addWidget(new QWidget); -#endif - custom_mapbox = QString::fromStdString(params.get("CustomMapboxTokenSk")) != ""; - if (!custom_mapbox) { - left_widget->addWidget(new PrimeAdWidget); - } - left_widget->setStyleSheet("border-radius: 10px;"); - - left_widget->setCurrentIndex((uiState()->hasPrime() || custom_mapbox) ? 0 : 1); - connect(uiState(), &UIState::primeChanged, [=](bool prime) { - left_widget->setCurrentIndex((prime || custom_mapbox) ? 0 : 1); - }); - - home_layout->addWidget(left_widget, 1); - - // right: ExperimentalModeButton, SetupWidget - QWidget* right_widget = new QWidget(this); - QVBoxLayout* right_column = new QVBoxLayout(right_widget); - right_column->setContentsMargins(0, 0, 0, 0); - right_widget->setFixedWidth(750); - right_column->setSpacing(30); - - ExperimentalModeButton *experimental_mode = new ExperimentalModeButton(this); - QObject::connect(experimental_mode, &ExperimentalModeButton::openSettings, this, &OffroadHome::openSettings); - right_column->addWidget(experimental_mode, 1); - - SetupWidget *setup_widget = new SetupWidget; - QObject::connect(setup_widget, &SetupWidget::openSettings, this, &OffroadHome::openSettings); - right_column->addWidget(setup_widget, 1); - - home_layout->addWidget(right_widget, 1); - } - center_layout->addWidget(home_widget); - - // add update & alerts widgets - update_widget = new UpdateAlert(); - QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); - center_layout->addWidget(update_widget); - alerts_widget = new OffroadAlert(); - QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); - center_layout->addWidget(alerts_widget); - - main_layout->addLayout(center_layout, 1); - - // set up refresh timer - timer = new QTimer(this); - timer->callOnTimeout(this, &OffroadHome::refresh); - - setStyleSheet(R"( - * { - color: white; - } - OffroadHome { - background-color: black; - } - OffroadHome > QPushButton { - padding: 15px 30px; - border-radius: 5px; - font-size: 40px; - font-weight: 500; - } - OffroadHome > QLabel { - font-size: 55px; - } - )"); -} - -void OffroadHome::showEvent(QShowEvent *event) { - refresh(); - timer->start(10 * 1000); -} - -void OffroadHome::hideEvent(QHideEvent *event) { - timer->stop(); -} - -void OffroadHome::refresh() { - version->setText(getBrand() + " " + QString::fromStdString(params.get("UpdaterCurrentDescription"))); - - bool updateAvailable = update_widget->refresh(); - int alerts = alerts_widget->refresh(); - - // pop-up new notification - int idx = center_layout->currentIndex(); - if (!updateAvailable && !alerts) { - idx = 0; - } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) { - idx = 1; - } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) { - idx = 2; - } - center_layout->setCurrentIndex(idx); - - update_notif->setVisible(updateAvailable); - alert_notif->setVisible(alerts); - if (alerts) { - alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT"))); - } -} diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h index 7e0267a8a9..e903dad47d 100644 --- a/selfdrive/ui/qt/home.h +++ b/selfdrive/ui/qt/home.h @@ -12,35 +12,20 @@ #include "selfdrive/ui/qt/body.h" #include "selfdrive/ui/qt/onroad/onroad_home.h" #include "selfdrive/ui/qt/sidebar.h" -#include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/offroad_alerts.h" #include "selfdrive/ui/ui.h" -class OffroadHome : public QFrame { - Q_OBJECT - -public: - explicit OffroadHome(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); - -private: - void showEvent(QShowEvent *event) override; - void hideEvent(QHideEvent *event) override; - void refresh(); - - Params params; - - QTimer* timer; - ElidedLabel* version; - QStackedLayout* center_layout; - UpdateAlert *update_widget; - OffroadAlert* alerts_widget; - QPushButton* alert_notif; - QPushButton* update_notif; - bool custom_mapbox; -}; +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/offroad_home.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h" +#define OnroadWindow OnroadWindowSP +#define OffroadHome OffroadHomeSP +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/onroad/onroad_home.h" +#include "selfdrive/ui/qt/offroad_home.h" +#endif class HomeWindow : public QWidget { Q_OBJECT @@ -56,13 +41,11 @@ public slots: void offroadTransition(bool offroad); void showDriverView(bool show); void showSidebar(bool show); - void showMapPanel(bool show); protected: void mousePressEvent(QMouseEvent* e) override; void mouseDoubleClickEvent(QMouseEvent* e) override; -private: Sidebar *sidebar; OffroadHome *home; OnroadWindow *onroad; @@ -70,6 +53,6 @@ private: DriverViewWindow *driver_view; QStackedLayout *slayout; -private slots: - void updateState(const UIState &s); +protected slots: + virtual void updateState(const UIState &s); }; diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index b5b731ef66..80cd82277e 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -6,9 +6,14 @@ #include #include "common/swaglog.h" -#include "selfdrive/ui/qt/maps/map_helpers.h" #include "selfdrive/ui/qt/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#else +#include "selfdrive/ui/qt/maps/map_helpers.h" #include "selfdrive/ui/ui.h" +#endif const int INTERACTION_TIMEOUT = 100; @@ -54,6 +59,7 @@ MapWindow::~MapWindow() { } void MapWindow::initLayers() { + RETURN_IF_SUNNYPILOT // This doesn't work from initializeGL if (!m_map->layerExists("modelPathLayer")) { qDebug() << "Initializing modelPathLayer"; @@ -75,7 +81,7 @@ void MapWindow::initLayers() { QVariantMap transition; transition["duration"] = 400; // ms - m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(uiState()->scene.navigate_on_openpilot_deprecated)); + m_map->setPaintProperty("navLayer", "line-color", QColor("#31a1ee")); m_map->setPaintProperty("navLayer", "line-color-transition", transition); m_map->setPaintProperty("navLayer", "line-width", 7.5); m_map->setLayoutProperty("navLayer", "line-cap", "round"); @@ -110,52 +116,10 @@ void MapWindow::initLayers() { // TODO: remove, symbol-sort-key does not seem to matter outside of each layer m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0); } - if ((!m_map->layerExists("buildingsLayer")) && uiState()->scene.map_3d_buildings) { // Could put this behind the cellular metered toggle in case it increases data usage - qDebug() << "Initializing buildingsLayer"; - QVariantMap buildings; - buildings["id"] = "buildingsLayer"; - buildings["source"] = "composite"; - buildings["source-layer"] = "building"; - buildings["type"] = "fill-extrusion"; - buildings["minzoom"] = 15; - m_map->addLayer("buildingsLayer", buildings); - m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"})); - - QVariantList fillExtrusionHeight = { // scale buildings as you zoom in - "interpolate", - QVariantList{"linear"}, - QVariantList{"zoom"}, - 15, 0, - 15.05, QVariantList{"get", "height"} - }; - - QVariantList fillExtrusionBase = { - "interpolate", - QVariantList{"linear"}, - QVariantList{"zoom"}, - 15, 0, - 15.05, QVariantList{"get", "min_height"} - }; - - QVariantList fillExtrusionOpacity = { - "interpolate", - QVariantList{"linear"}, - QVariantList{"zoom"}, - 15, 0, // transparent at zoom level 15 - 15.5, .6, // fade in - 17, .6, // begin fading out - 20, 0 // fade out when zoomed in - }; - - m_map->setPaintProperty("buildingsLayer", "fill-extrusion-color", QColor("grey")); - m_map->setPaintProperty("buildingsLayer", "fill-extrusion-opacity", fillExtrusionOpacity); - m_map->setPaintProperty("buildingsLayer", "fill-extrusion-height", fillExtrusionHeight); - m_map->setPaintProperty("buildingsLayer", "fill-extrusion-base", fillExtrusionBase); - m_map->setLayoutProperty("buildingsLayer", "visibility", "visible"); - } } void MapWindow::updateState(const UIState &s) { + RETURN_IF_SUNNYPILOT if (!uiState()->scene.started) { return; } @@ -170,22 +134,6 @@ void MapWindow::updateState(const UIState &s) { } prev_time_valid = sm.valid("clocks"); - if (sm.updated("modelV2")) { - // set path color on change, and show map on rising edge of navigate on openpilot - auto car_control = sm["carControl"].getCarControl(); - bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabledDEPRECATED() && - (sm["controlsState"].getControlsState().getEnabled() || car_control.getLatActive() || car_control.getLongActive()); - if (nav_enabled != uiState()->scene.navigate_on_openpilot_deprecated) { - if (loaded_once) { - m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled)); - } - if (nav_enabled) { - emit requestVisible(true); - } - } - uiState()->scene.navigate_on_openpilot_deprecated = nav_enabled; - } - if (sm.updated("liveLocationKalman")) { auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman(); auto locationd_pos = locationd_location.getPositionGeodetic(); @@ -425,7 +373,6 @@ void MapWindow::pinchTriggered(QPinchGesture *gesture) { void MapWindow::offroadTransition(bool offroad) { if (offroad) { clearRoute(); - uiState()->scene.navigate_on_openpilot_deprecated = false; routing_problem = false; } else { auto dest = coordinate_from_param("NavDestination"); diff --git a/selfdrive/ui/qt/maps/map.h b/selfdrive/ui/qt/maps/map.h index 62871e79d2..00e8b6f78f 100644 --- a/selfdrive/ui/qt/maps/map.h +++ b/selfdrive/ui/qt/maps/map.h @@ -20,7 +20,11 @@ #include "cereal/messaging/messaging.h" #include "common/params.h" #include "common/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif #include "selfdrive/ui/qt/maps/map_eta.h" #include "selfdrive/ui/qt/maps/map_instructions.h" @@ -32,15 +36,17 @@ public: ~MapWindow(); private: - void initializeGL() final; void paintGL() final; void resizeGL(int w, int h) override; +protected: + void initializeGL() final; QMapLibre::Settings m_settings; QScopedPointer m_map; void initLayers(); +protected: void mousePressEvent(QMouseEvent *ev) final; void mouseDoubleClickEvent(QMouseEvent *ev) final; void mouseMoveEvent(QMouseEvent *ev) final; @@ -50,9 +56,11 @@ private: void pinchTriggered(QPinchGesture *gesture); void setError(const QString &err_str); +protected: bool loaded_once = false; bool prev_time_valid = true; +protected: // Panning QPointF m_lastPos; int interaction_counter = 0; @@ -70,16 +78,11 @@ private: MapInstructions* map_instructions; MapETA* map_eta; - // Blue with normal nav, green when nav is input into the model - QColor getNavPathColor(bool nav_enabled) { - return nav_enabled ? QColor("#31ee73") : QColor("#31a1ee"); - } - void clearRoute(); void updateDestinationMarker(); uint64_t route_rcv_frame = 0; -private slots: +public slots: void updateState(const UIState &s); public slots: diff --git a/selfdrive/ui/qt/maps/map_eta.cc b/selfdrive/ui/qt/maps/map_eta.cc index 0eb77e36ce..e153884576 100644 --- a/selfdrive/ui/qt/maps/map_eta.cc +++ b/selfdrive/ui/qt/maps/map_eta.cc @@ -3,8 +3,13 @@ #include #include -#include "selfdrive/ui/qt/maps/map_helpers.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#else #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/maps/map_helpers.h" +#endif const float MANEUVER_TRANSITION_THRESHOLD = 10; diff --git a/selfdrive/ui/qt/maps/map_helpers.cc b/selfdrive/ui/qt/maps/map_helpers.cc index 50e1401164..6b7b05e786 100644 --- a/selfdrive/ui/qt/maps/map_helpers.cc +++ b/selfdrive/ui/qt/maps/map_helpers.cc @@ -1,4 +1,8 @@ +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#else #include "selfdrive/ui/qt/maps/map_helpers.h" +#endif #include #include diff --git a/selfdrive/ui/qt/maps/map_helpers.h b/selfdrive/ui/qt/maps/map_helpers.h index b9cb1f9469..0f4be674f0 100644 --- a/selfdrive/ui/qt/maps/map_helpers.h +++ b/selfdrive/ui/qt/maps/map_helpers.h @@ -12,9 +12,8 @@ #include "common/transformations/coordinates.hpp" #include "common/transformations/orientation.hpp" #include "cereal/messaging/messaging.h" -#include "common/params.h" -const QString MAPBOX_TOKEN = QString::fromStdString(Params().get("CustomMapboxTokenSk")) != "" ? QString::fromStdString(Params().get("CustomMapboxTokenSk")) : util::getenv("MAPBOX_TOKEN").c_str(); +const QString MAPBOX_TOKEN = util::getenv("MAPBOX_TOKEN").c_str(); const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str(); const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db"; diff --git a/selfdrive/ui/qt/maps/map_instructions.cc b/selfdrive/ui/qt/maps/map_instructions.cc index ba8cb356bd..b631548fe7 100644 --- a/selfdrive/ui/qt/maps/map_instructions.cc +++ b/selfdrive/ui/qt/maps/map_instructions.cc @@ -3,8 +3,13 @@ #include #include -#include "selfdrive/ui/qt/maps/map_helpers.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#else #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/maps/map_helpers.h" +#endif const QString ICON_SUFFIX = ".png"; diff --git a/selfdrive/ui/qt/maps/map_panel.cc b/selfdrive/ui/qt/maps/map_panel.cc index c4cc20e21d..cd448483ff 100644 --- a/selfdrive/ui/qt/maps/map_panel.cc +++ b/selfdrive/ui/qt/maps/map_panel.cc @@ -3,10 +3,16 @@ #include #include -#include "selfdrive/ui/qt/maps/map.h" #include "selfdrive/ui/qt/maps/map_settings.h" #include "selfdrive/ui/qt/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/maps/map.h" +#define MapWindow MapWindowSP +#else #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/maps/map.h" +#endif MapPanel::MapPanel(const QMapLibre::Settings &mapboxSettings, QWidget *parent) : QFrame(parent) { content_stack = new QStackedLayout(this); diff --git a/selfdrive/ui/qt/maps/map_settings.h b/selfdrive/ui/qt/maps/map_settings.h index 0e151df4ad..40b0d35a6a 100644 --- a/selfdrive/ui/qt/maps/map_settings.h +++ b/selfdrive/ui/qt/maps/map_settings.h @@ -13,7 +13,11 @@ #include "common/params.h" #include "selfdrive/ui/qt/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif const QString NAV_TYPE_FAVORITE = "favorite"; const QString NAV_TYPE_RECENT = "recent"; diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 484536acf2..4f3d333ffd 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -6,11 +6,16 @@ #include #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/home.h" static const int ICON_WIDTH = 49; @@ -26,29 +31,17 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { wifiScreen = new QWidget(this); QVBoxLayout* vlayout = new QVBoxLayout(wifiScreen); vlayout->setContentsMargins(20, 20, 20, 20); - QHBoxLayout* hlayout = new QHBoxLayout(); - QPushButton* scanButton = new QPushButton(tr("Scan")); - scanButton->setObjectName("scan_btn"); - scanButton->setFixedSize(400, 100); - connect(wifi, &WifiManager::refreshSignal, this, [=]() { scanButton->setText(tr("Scan")); scanButton->setEnabled(true); }); - connect(scanButton, &QPushButton::clicked, [=]() { scanButton->setText(tr("Scanning...")); scanButton->setEnabled(false); wifi->requestScan(); }); - - hlayout->addWidget(scanButton); - hlayout->addStretch(1); // Pushes the button all the way to the left - if (show_advanced) { - hlayout->setSpacing(10); - QPushButton* advancedSettings = new QPushButton(tr("Advanced")); advancedSettings->setObjectName("advanced_btn"); + advancedSettings->setStyleSheet("margin-right: 30px;"); advancedSettings->setFixedSize(400, 100); connect(advancedSettings, &QPushButton::clicked, [=]() { main_layout->setCurrentWidget(an); }); - hlayout->addWidget(advancedSettings); + vlayout->addSpacing(10); + vlayout->addWidget(advancedSettings, 0, Qt::AlignRight); + vlayout->addSpacing(10); } - vlayout->addLayout(hlayout); - vlayout->addSpacing(10); - wifiWidget = new WifiUI(this, wifi); wifiWidget->setObjectName("wifiWidget"); connect(wifiWidget, &WifiUI::connectToNetwork, this, &Networking::connectToNetwork); @@ -69,7 +62,7 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { setPalette(pal); setStyleSheet(R"( - #wifiWidget > QPushButton, #back_btn, #advanced_btn, #scan_btn{ + #wifiWidget > QPushButton, #back_btn, #advanced_btn { font-size: 50px; margin: 0px; padding: 15px; @@ -78,7 +71,7 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { color: #dddddd; background-color: #393939; } - #back_btn:pressed, #advanced_btn:pressed, #scan_btn:pressed { + #back_btn:pressed, #advanced_btn:pressed { background-color: #4a4a4a; } )"); @@ -139,23 +132,10 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid ListWidget *list = new ListWidget(this); // Enable tethering layout - const bool set_hotspot_on_boot = params.getBool("HotspotOnBoot") && params.getBool("HotspotOnBootConfirmed"); - tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled() || set_hotspot_on_boot); + tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled()); list->addItem(tetheringToggle); QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering); - hotspotOnBootToggle = new ToggleControl( - tr("Retain hotspot/tethering state"), - tr("Enabling this toggle will retain the hotspot/tethering toggle state across reboots."), - "", - params.getBool("HotspotOnBoot") - ); - hotspotOnBootToggle->setEnabled(wifi->isTetheringEnabled() || set_hotspot_on_boot); - QObject::connect(hotspotOnBootToggle, &ToggleControl::toggleFlipped, [=](bool state) { - params.putBool("HotspotOnBoot", state); - }); - list->addItem(hotspotOnBootToggle); - // Change tethering password ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT")); connect(editPasswordButton, &ButtonControl::clicked, [=]() { @@ -226,19 +206,6 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid }); list->addItem(hiddenNetworkButton); - // Ngrok - QProcess process; - process.start("sudo service ngrok status | grep running"); - process.waitForFinished(); - QString output = QString(process.readAllStandardOutput()); - bool ngrokRunning = !output.isEmpty(); - ToggleControl *ngrokToggle = new ToggleControl(tr("Ngrok Service"), "", "", ngrokRunning); - connect(ngrokToggle, &ToggleControl::toggleFlipped, [=](bool state) { - if (state) std::system("sudo ngrok service start"); - else std::system("sudo ngrok service stop"); - }); - list->addItem(ngrokToggle); - // Set initial config wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered); @@ -260,14 +227,8 @@ void AdvancedNetworking::refresh() { } void AdvancedNetworking::toggleTethering(bool enabled) { - params.putBool("HotspotOnBootConfirmed", enabled); wifi->setTetheringEnabled(enabled); tetheringToggle->setEnabled(false); - - hotspotOnBootToggle->setEnabled(enabled); - if (!enabled) { - params.remove("HotspotOnBoot"); - } } // WifiUI functions @@ -419,4 +380,4 @@ void WifiItem::setItem(const Network &n, const QPixmap &status_icon, bool show_f iconLabel->setPixmap(status_icon); strengthLabel->setPixmap(strength_icon); -} +} \ No newline at end of file diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h index 7444e9c28d..5831d66dc9 100644 --- a/selfdrive/ui/qt/network/networking.h +++ b/selfdrive/ui/qt/network/networking.h @@ -6,6 +6,13 @@ #include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/ssh_keys.h" #include "selfdrive/ui/qt/widgets/toggle.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define LabelControl LabelControlSP +#define ElidedLabel ElidedLabelSP +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#endif class WifiItem : public QWidget { Q_OBJECT @@ -67,8 +74,6 @@ private: WifiManager* wifi = nullptr; Params params; - ToggleControl* hotspotOnBootToggle; - signals: void backPress(); void requestWifiScreen(); @@ -100,4 +105,4 @@ public slots: private slots: void connectToNetwork(const Network n); void wrongPassword(const QString &ssid); -}; +}; \ No newline at end of file diff --git a/selfdrive/ui/qt/network/sunnylink/models/user_model.h b/selfdrive/ui/qt/network/sunnylink/models/user_model.h deleted file mode 100644 index 72baa3983d..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/models/user_model.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef USER_MODEL_H -#define USER_MODEL_H - -#include - -class UserModel { -public: - QString device_id; - QString user_id; - qint64 created_at; - qint64 updated_at; - QString token_hash; - - explicit UserModel(const QJsonObject &json) { - device_id = json["device_id"].toString(); - user_id = json["user_id"].toString(); - created_at = json["created_at"].toInt(); - updated_at = json["updated_at"].toInt(); - token_hash = json["token_hash"].toString(); - } - - [[nodiscard]] QJsonObject toJson() const { - QJsonObject json; - json["device_id"] = device_id; - json["user_id"] = user_id; - json["created_at"] = created_at; - json["updated_at"] = updated_at; - json["token_hash"] = token_hash; - return json; - } -}; - -#endif diff --git a/selfdrive/ui/qt/network/sunnylink/services/base_device_service.cc b/selfdrive/ui/qt/network/sunnylink/services/base_device_service.cc deleted file mode 100644 index 2f2561a49a..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/services/base_device_service.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include "base_device_service.h" - -#include "common/swaglog.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.h" - -BaseDeviceService::BaseDeviceService(QObject* parent) : QObject(parent), initial_request(nullptr), repeater(nullptr) { - param_watcher = new ParamWatcher(this); - connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { - paramsRefresh(); - }); - param_watcher->addParam("SunnylinkEnabled"); -} - -void BaseDeviceService::paramsRefresh() { -} - -void BaseDeviceService::loadDeviceData(const QString &url, bool poll) { - if (!is_sunnylink_enabled()) { - LOGW("Sunnylink is not enabled, refusing to load data."); - return; - } - - auto sl_dongle_id = getSunnylinkDongleId(); - if (!sl_dongle_id.has_value()) - return; - - QString fullUrl = SUNNYLINK_BASE_URL + "/device/" + *sl_dongle_id + url; - if (poll && !isCurrentyPolling()) { - LOGD("Polling %s", qPrintable(fullUrl)); - LOGD("Cache key: SunnylinkCache_%s", qPrintable(QString(getCacheKey()))); - repeater = new RequestRepeater(this, fullUrl, "SunnylinkCache_" + getCacheKey(), 60, false, true); - connect(repeater, &RequestRepeater::requestDone, this, &BaseDeviceService::handleResponse); - } else if(isCurrentyPolling()){ - repeater->ForceUpdate(); - } else { - LOGD("Sending one-time %s", qPrintable(fullUrl)); - initial_request = new HttpRequest(this, true, 10000, true); - connect(initial_request, &HttpRequest::requestDone, this, &BaseDeviceService::handleResponse); - } -} - -void BaseDeviceService::stopPolling() { - if (repeater != nullptr) { - repeater->deleteLater(); - repeater = nullptr; - } -} diff --git a/selfdrive/ui/qt/network/sunnylink/services/base_device_service.h b/selfdrive/ui/qt/network/sunnylink/services/base_device_service.h deleted file mode 100644 index cd7aa9d9b9..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/services/base_device_service.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef BASESERVICE_H -#define BASESERVICE_H - - -#include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/qt/request_repeater.h" -#include "selfdrive/ui/qt/util.h" - -class BaseDeviceService : public QObject { - Q_OBJECT - -protected: - void paramsRefresh(); - void loadDeviceData(const QString &url, bool poll = false); - virtual void handleResponse(const QString &response, bool success) = 0; - - static bool is_sunnylink_enabled() { return Params().getBool("SunnylinkEnabled");}; - ParamWatcher* param_watcher; - HttpRequest* initial_request = nullptr; - RequestRepeater* repeater = nullptr; - -public: - explicit BaseDeviceService(QObject* parent = nullptr); - virtual QString getCacheKey() const = 0; - bool isCurrentyPolling() {return repeater != nullptr;} - void stopPolling(); -}; - -#endif diff --git a/selfdrive/ui/qt/network/sunnylink/services/role_service.cc b/selfdrive/ui/qt/network/sunnylink/services/role_service.cc deleted file mode 100644 index 8bd0904421..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/services/role_service.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "selfdrive/ui/qt/network/sunnylink/services/role_service.h" - -#include -#include - -RoleService::RoleService(QObject* parent) : BaseDeviceService(parent) {} - -void RoleService::load() { - loadDeviceData(url); -} - -void RoleService::startPolling() { - loadDeviceData(url, true); -} - -void RoleService::handleResponse(const QString &response, bool success) { - if (!success) return; - - QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); - QJsonArray jsonArray = doc.array(); - - std::vector roles; - for (const auto &value : jsonArray) { - roles.emplace_back(value.toObject()); - } - - emit rolesReady(roles); - uiState()->setSunnylinkRoles(roles); -} diff --git a/selfdrive/ui/qt/network/sunnylink/services/role_service.h b/selfdrive/ui/qt/network/sunnylink/services/role_service.h deleted file mode 100644 index bc40342982..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/services/role_service.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef ROLESERVICE_H -#define ROLESERVICE_H - -#include "selfdrive/ui/qt/network/sunnylink/services/base_device_service.h" -#include "selfdrive/ui/qt/network/sunnylink/models/role_model.h" - -class RoleService : public BaseDeviceService { - Q_OBJECT - -public: - explicit RoleService(QObject* parent = nullptr); - void load(); - void startPolling(); - [[nodiscard]] QString getCacheKey() const final { return "Roles"; }; - -signals: - void rolesReady(const std::vector &roles); - -protected: - void handleResponse(const QString&response, bool success) override; - -private: - QString url = "/roles"; -}; - -#endif diff --git a/selfdrive/ui/qt/network/sunnylink/services/user_service.cc b/selfdrive/ui/qt/network/sunnylink/services/user_service.cc deleted file mode 100644 index 2d52421d69..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/services/user_service.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "selfdrive/ui/qt/network/sunnylink/services/user_service.h" - -#include -#include - -UserService::UserService(QObject* parent) : BaseDeviceService(parent) { - url = "/users"; -} - -void UserService::load() { - loadDeviceData(url); -} - -void UserService::startPolling() { - loadDeviceData(url, true); -} - -void UserService::handleResponse(const QString &response, bool success) { - if (!success) return; - - QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); - QJsonArray jsonArray = doc.array(); - - std::vector users; - for (const auto &value : jsonArray) { - users.emplace_back(value.toObject()); - } - - emit usersReady(users); - uiState()->setSunnylinkDeviceUsers(users); -} diff --git a/selfdrive/ui/qt/network/sunnylink/services/user_service.h b/selfdrive/ui/qt/network/sunnylink/services/user_service.h deleted file mode 100644 index e4af93fa29..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/services/user_service.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef USERSERVICE_H -#define USERSERVICE_H - -#include "selfdrive/ui/qt/network/sunnylink/services/base_device_service.h" -#include "selfdrive/ui/qt/network/sunnylink/models/user_model.h" - -class UserService : public BaseDeviceService { - Q_OBJECT - -public: - explicit UserService(QObject* parent = nullptr); - void load(); - void startPolling(); - [[nodiscard]] QString getCacheKey() const final { return "Users"; }; - -signals: - void usersReady(const std::vector&users); - -protected: - void handleResponse(const QString&response, bool success) override; - -private: - QString url = "/users"; -}; - -#endif diff --git a/selfdrive/ui/qt/network/sunnylink/sunnylink_client.cc b/selfdrive/ui/qt/network/sunnylink/sunnylink_client.cc deleted file mode 100644 index 2d1613c0eb..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/sunnylink_client.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include "selfdrive/ui/qt/network/sunnylink/sunnylink_client.h" -#include "selfdrive/ui/qt/network/sunnylink/services/user_service.h" - -SunnylinkClient::SunnylinkClient(QObject* parent) : QObject(parent) { - role_service = new RoleService(parent); - user_service = new UserService(parent); -} diff --git a/selfdrive/ui/qt/network/sunnylink/sunnylink_client.h b/selfdrive/ui/qt/network/sunnylink/sunnylink_client.h deleted file mode 100644 index 872d80ccf7..0000000000 --- a/selfdrive/ui/qt/network/sunnylink/sunnylink_client.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SUNNYLINK_CLIENT_H -#define SUNNYLINK_CLIENT_H - -#include - -#include "selfdrive/ui/qt/network/sunnylink/services/role_service.h" -#include "selfdrive/ui/qt/network/sunnylink/services/user_service.h" - -class SunnylinkClient : public QObject { - Q_OBJECT - -public: - explicit SunnylinkClient(QObject* parent); - RoleService* role_service; - UserService* user_service; -}; - -#endif diff --git a/selfdrive/ui/qt/network/wifi_manager.cc b/selfdrive/ui/qt/network/wifi_manager.cc index 717da47096..b12207fab5 100644 --- a/selfdrive/ui/qt/network/wifi_manager.cc +++ b/selfdrive/ui/qt/network/wifi_manager.cc @@ -2,7 +2,11 @@ #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif #include "selfdrive/ui/qt/widgets/prime.h" #include "common/params.h" diff --git a/selfdrive/ui/qt/offroad/experimental_mode.cc b/selfdrive/ui/qt/offroad/experimental_mode.cc index b2c6f3323a..88fadd439b 100644 --- a/selfdrive/ui/qt/offroad/experimental_mode.cc +++ b/selfdrive/ui/qt/offroad/experimental_mode.cc @@ -7,13 +7,18 @@ #include #include "selfdrive/ui/ui.h" +#ifdef SUNNYPILOT +#define TOGGLES_PANEL_INDEX 3 +#else +#define TOGGLES_PANEL_INDEX 2 +#endif ExperimentalModeButton::ExperimentalModeButton(QWidget *parent) : QPushButton(parent) { chill_pixmap = QPixmap("../assets/img_couch.svg").scaledToWidth(img_width, Qt::SmoothTransformation); experimental_pixmap = QPixmap("../assets/img_experimental_grey.svg").scaledToWidth(img_width, Qt::SmoothTransformation); // go to toggles and expand experimental mode description - connect(this, &QPushButton::clicked, [=]() { emit openSettings(3, "ExperimentalMode"); }); + connect(this, &QPushButton::clicked, [=]() { emit openSettings(TOGGLES_PANEL_INDEX, "ExperimentalMode"); }); setFixedHeight(125); QHBoxLayout *main_layout = new QHBoxLayout; diff --git a/selfdrive/ui/qt/offroad/onboarding.cc b/selfdrive/ui/qt/offroad/onboarding.cc index 014e9a6f05..8bf92aa06b 100644 --- a/selfdrive/ui/qt/offroad/onboarding.cc +++ b/selfdrive/ui/qt/offroad/onboarding.cc @@ -106,8 +106,7 @@ void TermsPage::showEvent(QShowEvent *event) { text->setAttribute(Qt::WA_AlwaysStackOnTop); text->setClearColor(QColor("#1B1B1B")); - std::string tc_text = sunnypilot_tc ? "../assets/offroad/sp_tc.html" : "../assets/offroad/tc.html"; - QString text_view = util::read_file(tc_text).c_str(); + QString text_view = util::read_file("../assets/offroad/tc.html").c_str(); text->rootContext()->setContextProperty("text_view", text_view); text->setSource(QUrl::fromLocalFile("qt/offroad/text_view.qml")); @@ -186,8 +185,6 @@ void OnboardingWindow::updateActiveScreen() { setCurrentIndex(0); } else if (!training_done) { setCurrentIndex(1); - } else if (!accepted_terms_sp) { - setCurrentIndex(3); } else { emit onboardingDone(); } @@ -195,13 +192,11 @@ void OnboardingWindow::updateActiveScreen() { OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) { std::string current_terms_version = params.get("TermsVersion"); - std::string current_terms_version_sp = params.get("TermsVersionSunnypilot"); std::string current_training_version = params.get("TrainingVersion"); accepted_terms = params.get("HasAcceptedTerms") == current_terms_version; - accepted_terms_sp = params.get("HasAcceptedTermsSP") == current_terms_version_sp; training_done = params.get("CompletedTrainingVersion") == current_training_version; - TermsPage* terms = new TermsPage(false, this); + TermsPage* terms = new TermsPage(this); addWidget(terms); connect(terms, &TermsPage::acceptedTerms, [=]() { params.put("HasAcceptedTerms", current_terms_version); @@ -222,15 +217,6 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) { addWidget(declinePage); connect(declinePage, &DeclinePage::getBack, [=]() { updateActiveScreen(); }); - TermsPage* terms_sp = new TermsPage(true, this); - addWidget(terms_sp); // index = 3 - connect(terms_sp, &TermsPage::acceptedTerms, [=]() { - params.put("HasAcceptedTermsSP", current_terms_version_sp); - accepted_terms_sp = true; - updateActiveScreen(); - }); - connect(terms_sp, &TermsPage::declinedTerms, [=]() { setCurrentIndex(2); }); - setStyleSheet(R"( * { color: white; @@ -245,4 +231,4 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) { } )"); updateActiveScreen(); -} +} \ No newline at end of file diff --git a/selfdrive/ui/qt/offroad/onboarding.h b/selfdrive/ui/qt/offroad/onboarding.h index 008d86032c..098b0823a7 100644 --- a/selfdrive/ui/qt/offroad/onboarding.h +++ b/selfdrive/ui/qt/offroad/onboarding.h @@ -63,16 +63,17 @@ class TermsPage : public QFrame { Q_OBJECT public: - explicit TermsPage(bool sunnypilot = false, QWidget *parent = 0) : QFrame(parent), sunnypilot_tc(sunnypilot) {} + explicit TermsPage(QWidget *parent = 0) : QFrame(parent) {} public slots: void enableAccept(); +protected: + QPushButton *accept_btn; + private: void showEvent(QShowEvent *event) override; - QPushButton *accept_btn; - bool sunnypilot_tc = false; signals: void acceptedTerms(); @@ -98,14 +99,14 @@ class OnboardingWindow : public QStackedWidget { public: explicit OnboardingWindow(QWidget *parent = 0); inline void showTrainingGuide() { setCurrentIndex(1); } - inline bool completed() const { return accepted_terms && accepted_terms_sp && training_done; } + virtual inline bool completed() const { return accepted_terms && training_done; } -private: - void updateActiveScreen(); +protected: + virtual void updateActiveScreen(); Params params; - bool accepted_terms = false, accepted_terms_sp = false, training_done = false; + bool accepted_terms = false, training_done = false; signals: void onboardingDone(); -}; +}; \ No newline at end of file diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index f9f852a3b4..040b074784 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -5,28 +5,29 @@ #include #include -#include -#include -#include #include "common/watchdog.h" #include "common/util.h" #include "selfdrive/ui/qt/network/networking.h" #include "selfdrive/ui/qt/offroad/settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot_main.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/widgets/prime.h" #include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/widgets/ssh_keys.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/sunnypilot_main.h" +#endif TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { + RETURN_IF_SUNNYPILOT + // param, title, desc, icon std::vector> toggle_defs{ { "OpenpilotEnabledToggle", tr("Enable sunnypilot"), tr("Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_openpilot.png", }, { "ExperimentalLongitudinalEnabled", @@ -35,105 +36,54 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) .arg(tr("On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.")), - "../assets/offroad/icon_blank.png", - }, - { - "CustomStockLong", - tr("Custom Stock Longitudinal Control"), - tr("When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses.\nThis feature must be used along with SLC, and/or V-TSC, and/or M-TSC."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_speed_limit.png", }, { "ExperimentalMode", tr("Experimental Mode"), "", - "../assets/offroad/icon_blank.png", - }, - { - "DynamicExperimentalControl", - tr("Enable Dynamic Experimental Control"), - tr("Enable toggle to allow the model to determine when to use openpilot ACC or openpilot End to End Longitudinal."), - "../assets/offroad/icon_blank.png", - }, - { - "DynamicPersonality", - tr("Enable Dynamic Personality"), - tr("Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your \"Driving Personality\" setting. " - "Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car."), - "../assets/offroad/icon_blank.png", + "../assets/img_experimental_white.svg", }, { "DisengageOnAccelerator", tr("Disengage on Accelerator Pedal"), tr("When enabled, pressing the accelerator pedal will disengage openpilot."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_disengage_on_accelerator.svg", }, { "IsLdwEnabled", tr("Enable Lane Departure Warnings"), tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_warning.png", }, { "AlwaysOnDM", tr("Always-On Driver Monitoring"), tr("Enable driver monitoring even when openpilot is not engaged."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_monitoring.png", }, { "RecordFront", tr("Record and Upload Driver Camera"), tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), - "../assets/offroad/icon_blank.png", - }, - { - "DisableOnroadUploads", - tr("Disable Onroad Uploads"), - tr("Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC)."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_monitoring.png", }, { "IsMetric", tr("Use Metric System"), tr("Display speed in km/h instead of mph."), - "../assets/offroad/icon_blank.png", + "../assets/offroad/icon_metric.png", }, -#ifdef ENABLE_MAPS - { - "NavSettingTime24h", - tr("Show ETA in 24h Format"), - tr("Use 24h format instead of am/pm"), - "../assets/offroad/icon_blank.png", - }, - { - "NavSettingLeftSide", - tr("Show Map on Left Side of UI"), - tr("Show map on left side when in split screen view."), - "../assets/offroad/icon_blank.png", - }, -#endif }; - std::vector longi_button_texts{tr("Aggressive"), tr("Moderate"), tr("Standard"), tr("Relaxed")}; + std::vector longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")}; long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"), - tr("Standard is recommended. In moderate/aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " + tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " "your steering wheel distance button."), - "../assets/offroad/icon_blank.png", - longi_button_texts, - 380); - long_personality_setting->showDescription(); - - // accel controller - std::vector accel_personality_texts{tr("Sport"), tr("Normal"), tr("Eco"), tr("Stock")}; - accel_personality_setting = new ButtonParamControl("AccelPersonality", tr("Acceleration Personality"), - tr("Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. " - "In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these " - "acceleration personality within Onroad Settings on the driving screen."), - "../assets/offroad/icon_blank.png", - accel_personality_texts); - accel_personality_setting->showDescription(); + "../assets/offroad/icon_speed_limit.png", + longi_button_texts); // set up uiState update for personality setting QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState); @@ -150,28 +100,17 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { // insert longitudinal personality after NDOG toggle if (param == "DisengageOnAccelerator") { addItem(long_personality_setting); - addItem(accel_personality_setting); } } // Toggles with confirmation dialogs - //toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); + toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); toggles["ExperimentalMode"]->setConfirmation(true, true); toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false); - toggles["CustomStockLong"]->setConfirmation(true, false); connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() { updateToggles(); }); - connect(toggles["CustomStockLong"], &ToggleControl::toggleFlipped, [=]() { - updateToggles(); - }); - - param_watcher = new ParamWatcher(this); - - QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { - updateToggles(); - }); } void TogglesPanel::updateState(const UIState &s) { @@ -184,14 +123,6 @@ void TogglesPanel::updateState(const UIState &s) { } uiState()->scene.personality = personality; } - - if (sm.updated("controlsStateSP")) { - auto accel_personality = sm["controlsStateSP"].getControlsStateSP().getAccelPersonality(); - if (accel_personality != s.scene.accel_personality && s.scene.started && isVisible()) { - accel_personality_setting->setCheckedButton(static_cast(accel_personality)); - } - uiState()->scene.accel_personality = accel_personality; - } } void TogglesPanel::expandToggleDescription(const QString ¶m) { @@ -203,15 +134,8 @@ void TogglesPanel::showEvent(QShowEvent *event) { } void TogglesPanel::updateToggles() { - param_watcher->addParam("LongitudinalPersonality"); - - if (!isVisible()) return; - auto experimental_mode_toggle = toggles["ExperimentalMode"]; auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"]; - auto custom_stock_long_toggle = toggles["CustomStockLong"]; - auto dec_toggle = toggles["DynamicExperimentalControl"]; - auto dynamic_personality_toggle = toggles["DynamicPersonality"]; const QString e2e_description = QString("%1
" "

%2


" "%3
" @@ -236,35 +160,15 @@ void TogglesPanel::updateToggles() { params.remove("ExperimentalLongitudinalEnabled"); } op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); - - if (!CP.getCustomStockLongAvailable()) { - params.remove("CustomStockLong"); - } - custom_stock_long_toggle->setVisible(CP.getCustomStockLongAvailable()); - if (hasLongitudinalControl(CP)) { // normal description and toggle experimental_mode_toggle->setEnabled(true); experimental_mode_toggle->setDescription(e2e_description); long_personality_setting->setEnabled(true); - accel_personality_setting->setEnabled(true); - op_long_toggle->setEnabled(true); - custom_stock_long_toggle->setEnabled(false); - params.remove("CustomStockLong"); - dec_toggle->setEnabled(true); - dynamic_personality_toggle->setEnabled(true); - } else if (custom_stock_long_toggle->isToggled()) { - op_long_toggle->setEnabled(false); - experimental_mode_toggle->setEnabled(false); - long_personality_setting->setEnabled(false); - accel_personality_setting->setEnabled(false); - params.remove("ExperimentalLongitudinalEnabled"); - params.remove("ExperimentalMode"); } else { // no long for now experimental_mode_toggle->setEnabled(false); long_personality_setting->setEnabled(false); - accel_personality_setting->setEnabled(false); params.remove("ExperimentalMode"); const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control."); @@ -279,26 +183,12 @@ void TogglesPanel::updateToggles() { } } experimental_mode_toggle->setDescription("" + long_desc + "

" + e2e_description); - - op_long_toggle->setEnabled(CP.getExperimentalLongitudinalAvailable() && !is_release); - custom_stock_long_toggle->setEnabled(CP.getCustomStockLongAvailable()); - dec_toggle->setEnabled(false); - dynamic_personality_toggle->setEnabled(false); - params.remove("DynamicExperimentalControl"); - params.remove("DynamicPersonality"); } experimental_mode_toggle->refresh(); - op_long_toggle->refresh(); - custom_stock_long_toggle->refresh(); - dec_toggle->refresh(); - dynamic_personality_toggle->refresh(); } else { experimental_mode_toggle->setDescription(e2e_description); op_long_toggle->setVisible(false); - custom_stock_long_toggle->setVisible(false); - dec_toggle->setVisible(false); - dynamic_personality_toggle->setVisible(false); } } @@ -307,44 +197,6 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { addItem(new LabelControl(tr("Dongle ID"), getDongleId().value_or(tr("N/A")))); addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str())); - fleetManagerPin = new ButtonControl( - pin_title + pin, tr("TOGGLE"), - tr("Enable or disable PIN requirement for Fleet Manager access.")); - connect(fleetManagerPin, &ButtonControl::clicked, [=]() { - if (params.getBool("FleetManagerPin")) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to turn off PIN requirement?"), tr("Turn Off"), this)) { - params.remove("FleetManagerPin"); - refreshPin(); - } - } else { - params.putBool("FleetManagerPin", true); - refreshPin(); - } - }); - addItem(fleetManagerPin); - - fs_watch = new QFileSystemWatcher(this); - connect(fs_watch, &QFileSystemWatcher::fileChanged, this, &DevicePanel::onPinFileChanged); - - QString pin_path = "/data/otp/otp.conf"; - QString pin_require = "/data/params/d/FleetManagerPin"; - fs_watch->addPath(pin_path); - fs_watch->addPath(pin_require); - refreshPin(); - - // Error Troubleshoot - auto errorBtn = new ButtonControl( - tr("Error Troubleshoot"), tr("VIEW"), - tr("Display error from the tmux session when an error has occurred from a system process.")); - QFileInfo file("/data/community/crashes/error.txt"); - QDateTime modifiedTime = file.lastModified(); - QString modified_time = modifiedTime.toString("yyyy-MM-dd hh:mm:ss "); - connect(errorBtn, &ButtonControl::clicked, [=]() { - const std::string txt = util::read_file("/data/community/crashes/error.txt"); - ConfirmationDialog::rich(modified_time + QString::fromStdString(txt), this); - }); - addItem(errorBtn); - pair_device = new ButtonControl(tr("Pair Device"), tr("PAIR"), tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.")); connect(pair_device, &ButtonControl::clicked, [=]() { @@ -370,33 +222,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { }); addItem(resetCalibBtn); - auto resetMapboxTokenBtn = new ButtonControl(tr("Reset Access Tokens for Map Services"), tr("RESET"), tr("Reset self-service access tokens for Mapbox, Amap, and Google Maps.")); - connect(resetMapboxTokenBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reset access tokens for all map services?"), tr("Reset"), this)) { - std::vector tokens = { - "CustomMapboxTokenPk", - "CustomMapboxTokenSk", - "AmapKey1", - "AmapKey2", - "GmapKey" - }; - for (const auto& token : tokens) { - params.remove(token); - } - } - }); - addItem(resetMapboxTokenBtn); - - auto resetParamsBtn = new ButtonControl(tr("Reset sunnypilot Settings"), tr("RESET"), ""); - connect(resetParamsBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reset all sunnypilot settings?"), tr("Reset"), this)) { - std::system("sudo rm -rf /data/params/d/*"); - Hardware::reboot(); - } - }); - addItem(resetParamsBtn); - - auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of sunnypilot")); + auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot")); connect(retrainingBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) { emit reviewTrainingGuide(); @@ -429,16 +255,19 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { QObject::connect(uiState(), &UIState::primeTypeChanged, [this] (PrimeType type) { pair_device->setVisible(type == PrimeType::UNPAIRED); }); + +#ifndef SUNNYPILOT QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { for (auto btn : findChildren()) { - if ((btn != pair_device) && (btn != errorBtn)) { + if (btn != pair_device) { btn->setEnabled(offroad); } } }); +#endif // power buttons - QHBoxLayout *power_layout = new QHBoxLayout(); + power_layout = new QHBoxLayout(); power_layout->setSpacing(30); QPushButton *reboot_btn = new QPushButton(tr("Reboot")); @@ -455,46 +284,14 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { connect(uiState(), &UIState::offroadTransition, poweroff_btn, &QPushButton::setVisible); } - offroad_btn = new QPushButton(tr("Toggle Onroad/Offroad")); - offroad_btn->setObjectName("offroad_btn"); - QObject::connect(offroad_btn, &QPushButton::clicked, this, &DevicePanel::forceoffroad); - - QVBoxLayout *buttons_layout = new QVBoxLayout(); - buttons_layout->setSpacing(24); - buttons_layout->addLayout(power_layout); - buttons_layout->addWidget(offroad_btn); - setStyleSheet(R"( #reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; } #reboot_btn:pressed { background-color: #4a4a4a; } #poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; } #poweroff_btn:pressed { background-color: #FF2424; } )"); - addItem(buttons_layout); - - updateLabels(); -} - -void DevicePanel::onPinFileChanged(const QString &file_path) { - if (file_path == "/data/params/d/FleetManagerPin") { - refreshPin(); - } else if (file_path == "/data/otp/otp.conf") { - refreshPin(); - } -} - -void DevicePanel::refreshPin() { - QFile f("/data/otp/otp.conf"); - QFile require("/data/params/d/FleetManagerPin"); - if (!require.exists()) { - setSpacing(50); - fleetManagerPin->setTitle(pin_title + tr("OFF")); - } else if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { - pin = f.readAll(); - f.close(); - setSpacing(50); - fleetManagerPin->setTitle(pin_title + pin); - } + RETURN_IF_SUNNYPILOT + addItem(power_layout); } void DevicePanel::updateCalibDescription() { @@ -547,51 +344,9 @@ void DevicePanel::poweroff() { } } -void DevicePanel::forceoffroad() { - if (!uiState()->engaged()) { - if (params.getBool("ForceOffroad")) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to unforce offroad?"), tr("Unforce"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.remove("ForceOffroad"); - } - } - } else { - if (ConfirmationDialog::confirm(tr("Are you sure you want to force offroad?"), tr("Force"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.putBool("ForceOffroad", true); - } - } - } - } else { - ConfirmationDialog::alert(tr("Disengage to Force Offroad"), this); - } - - updateLabels(); -} - void DevicePanel::showEvent(QShowEvent *event) { pair_device->setVisible(uiState()->primeType() == PrimeType::UNPAIRED); ListWidget::showEvent(event); - updateLabels(); -} - -void DevicePanel::updateLabels() { - if (!isVisible()) { - return; - } - - bool force_offroad_param = params.getBool("ForceOffroad"); - QString offroad_btn_style = force_offroad_param ? "#393939" : "#E22C2C"; - QString offroad_btn_pressed_style = force_offroad_param ? "#4a4a4a" : "#FF2424"; - QString btn_common_style = QString("QPushButton { height: 120px; border-radius: 15px; background-color: %1; }" - "QPushButton:pressed { background-color: %2; }") - .arg(offroad_btn_style, - offroad_btn_pressed_style); - - offroad_btn->setText(force_offroad_param ? tr("Unforce Offroad") : tr("Force Offroad")); - offroad_btn->setStyleSheet(btn_common_style + offroad_btn_style + offroad_btn_pressed_style); } void SettingsWindow::showEvent(QShowEvent *event) { @@ -607,23 +362,20 @@ void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { } SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { + RETURN_IF_SUNNYPILOT // setup two main layouts sidebar_widget = new QWidget; QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); panel_widget = new QStackedWidget(); - // setup layout for close button - QVBoxLayout *close_btn_layout = new QVBoxLayout; - close_btn_layout->setContentsMargins(0, 0, 0, 20); - // close button QPushButton *close_btn = new QPushButton(tr("×")); close_btn->setStyleSheet(R"( QPushButton { font-size: 140px; padding-bottom: 20px; - border-radius: 76px; + border-radius: 100px; background-color: #292929; font-weight: 400; } @@ -631,16 +383,11 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { background-color: #3B3B3B; } )"); - close_btn->setFixedSize(152, 152); - close_btn_layout->addWidget(close_btn, 0, Qt::AlignLeft); + close_btn->setFixedSize(200, 200); + sidebar_layout->addSpacing(45); + sidebar_layout->addWidget(close_btn, 0, Qt::AlignCenter); QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings); - // setup buttons widget - QWidget *buttons_widget = new QWidget; - QVBoxLayout *buttons_layout = new QVBoxLayout(buttons_widget); - buttons_layout->setMargin(0); - buttons_layout->addSpacing(10); - // setup panels DevicePanel *device = new DevicePanel(this); QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide); @@ -649,43 +396,27 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { TogglesPanel *toggles = new TogglesPanel(this); QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); - QList panels = { - PanelInfo(" " + tr("Device"), device, "../assets/navigation/icon_home.svg"), - PanelInfo(" " + tr("Network"), new Networking(this), "../assets/offroad/icon_network.png"), - PanelInfo(" " + tr("sunnylink"), new SunnylinkPanel(this), "../assets/offroad/icon_wifi_strength_full.svg"), - PanelInfo(" " + tr("Toggles"), toggles, "../assets/offroad/icon_toggle.png"), - PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../assets/offroad/icon_software.png"), - PanelInfo(" " + tr("sunnypilot"), new SunnypilotPanel(this), "../assets/offroad/icon_openpilot.png"), - PanelInfo(" " + tr("OSM"), new OsmPanel(this), "../assets/offroad/icon_map.png"), - PanelInfo(" " + tr("Monitoring"), new MonitoringPanel(this), "../assets/offroad/icon_monitoring.png"), - PanelInfo(" " + tr("Visuals"), new VisualsPanel(this), "../assets/offroad/icon_visuals.png"), - PanelInfo(" " + tr("Display"), new DisplayPanel(this), "../assets/offroad/icon_display.png"), - PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../assets/offroad/icon_trips.png"), - PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../assets/offroad/icon_vehicle.png"), + QList> panels = { + {tr("Device"), device}, + {tr("Network"), new Networking(this)}, + {tr("Toggles"), toggles}, + {tr("Software"), new SoftwarePanel(this)}, }; nav_btns = new QButtonGroup(this); - for (auto &[name, panel, icon] : panels) { + for (auto &[name, panel] : panels) { QPushButton *btn = new QPushButton(name); btn->setCheckable(true); btn->setChecked(nav_btns->buttons().size() == 0); - btn->setIcon(QIcon(QPixmap(icon))); - btn->setIconSize(QSize(70, 70)); btn->setStyleSheet(R"( QPushButton { - border-radius: 20px; - width: 400px; - height: 98px; - color: #bdbdbd; + color: grey; border: none; background: none; - font-size: 50px; + font-size: 65px; font-weight: 500; - text-align: left; - padding-left: 22px; } QPushButton:checked { - background-color: #696868; color: white; } QPushButton:pressed { @@ -694,9 +425,9 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { )"); btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); nav_btns->addButton(btn); - buttons_layout->addWidget(btn, 0, Qt::AlignLeft | Qt::AlignBottom); + sidebar_layout->addWidget(btn, 0, Qt::AlignRight); - const int lr_margin = (name != (" " + tr("Network")) || (name != (" " + tr("sunnypilot")))) ? 50 : 0; // Network and sunnypilot panel handles its own margins + const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins panel->setContentsMargins(lr_margin, 25, lr_margin, 25); ScrollView *panel_frame = new ScrollView(panel, this); @@ -707,18 +438,11 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { panel_widget->setCurrentWidget(w); }); } - sidebar_layout->setContentsMargins(50, 50, 25, 50); + sidebar_layout->setContentsMargins(50, 50, 100, 50); // main settings layout, sidebar + main panel QHBoxLayout *main_layout = new QHBoxLayout(this); - // add layout for close button - sidebar_layout->addLayout(close_btn_layout); - - // add layout for buttons scrolling - ScrollView *buttons_scrollview = new ScrollView(buttons_widget, this); - sidebar_layout->addWidget(buttons_scrollview); - sidebar_widget->setFixedWidth(500); main_layout->addWidget(sidebar_widget); main_layout->addWidget(panel_widget); @@ -732,7 +456,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { background-color: black; } QStackedWidget, ScrollView { - background-color: black; + background-color: #292929; border-radius: 30px; } )"); diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 06fbe8bfb9..dcc07aa2f2 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -8,12 +8,23 @@ #include #include #include -#include #include -#include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/util.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define ListWidget ListWidgetSP +#define ParamControl ParamControlSP +#define ButtonControl ButtonControlSP +#define ButtonParamControl ButtonParamControlSP +#define ToggleControl ToggleControlSP +#define LabelControl LabelControlSP +#else +#include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/widgets/controls.h" +#endif // ********** settings window + top-level panels ********** class SettingsWindow : public QFrame { @@ -32,19 +43,11 @@ signals: void showDriverView(); void expandToggleDescription(const QString ¶m); -private: +protected: QPushButton *sidebar_alert_widget; QWidget *sidebar_widget; QButtonGroup *nav_btns; QStackedWidget *panel_widget; - - struct PanelInfo { - QString name; - QWidget *widget; - QString icon; - - PanelInfo(const QString &name, QWidget *widget, const QString &icon) : name(name), widget(widget), icon(icon) {} - }; }; class DevicePanel : public ListWidget { @@ -57,26 +60,15 @@ signals: void reviewTrainingGuide(); void showDriverView(); -private slots: +protected slots: void poweroff(); void reboot(); void updateCalibDescription(); - void onPinFileChanged(const QString &file_path); - void refreshPin(); - void forceoffroad(); - void updateLabels(); - -private: +protected: Params params; ButtonControl *pair_device; - - ButtonControl *fleetManagerPin; - QString pin_title = tr("Fleet Manager PIN:") + " "; - QString pin = "OFF"; - QFileSystemWatcher *fs_watch; - - QPushButton *offroad_btn; + QHBoxLayout *power_layout; }; class TogglesPanel : public ListWidget { @@ -87,18 +79,16 @@ public: public slots: void expandToggleDescription(const QString ¶m); - void updateToggles(); -private slots: - void updateState(const UIState &s); +protected slots: + virtual void updateState(const UIState &s); -private: +protected: Params params; std::map toggles; ButtonParamControl *long_personality_setting; - ButtonParamControl *accel_personality_setting; - ParamWatcher *param_watcher; + virtual void updateToggles(); }; class SoftwarePanel : public ListWidget { @@ -114,7 +104,6 @@ protected: bool is_onroad = false; QLabel *onroadLbl; - LabelControl *currentModelLbl; LabelControl *versionLbl; ButtonControl *installBtn; ButtonControl *downloadBtn; diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc index 685ca3555d..47b4a22682 100644 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ b/selfdrive/ui/qt/offroad/software_settings.cc @@ -9,22 +9,27 @@ #include "common/params.h" #include "common/util.h" -#include "common/model.h" #include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif #include "selfdrive/ui/qt/widgets/input.h" #include "system/hardware/hw.h" +#ifdef SUNNYPILOT +#define ListWidget ListWidgetSP +#define ButtonControl ButtonControlSP +#endif + void SoftwarePanel::checkForUpdates() { std::system("pkill -SIGUSR1 -f system.updated.updated"); } SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { - currentModelLbl = new LabelControl(tr("Driving Model"), CURRENT_MODEL); - addItem(currentModelLbl); - onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off.")); onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;"); addItem(onroadLbl); @@ -74,7 +79,9 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { checkForUpdates(); } }); - addItem(targetBranchBtn); + if (!params.getBool("IsTestedBranch")) { + addItem(targetBranchBtn); + } // uninstall button auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL")); diff --git a/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h deleted file mode 100644 index 63a5a817be..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class CameraOffset : public SPOptionControl { - Q_OBJECT - -public: - CameraOffset(); - - void refresh(); - -private: - Params params; -}; - -class PathOffset : public SPOptionControl { - Q_OBJECT - -public: - PathOffset(); - - void refresh(); - -private: - Params params; -}; - -class CustomOffsetsSettings : public QWidget { - Q_OBJECT - -public: - explicit CustomOffsetsSettings(QWidget* parent = nullptr); - -signals: - void backPress(); - -private: - Params params; - std::map toggles; - - CameraOffset *camera_offset; - PathOffset *path_offset; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/display_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/display_settings.h deleted file mode 100644 index b8083b53fc..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/display_settings.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/widgets/controls.h" - -class OnroadScreenOff : public SPOptionControl { - Q_OBJECT - -public: - OnroadScreenOff(); - - void refresh(); - -private: - Params params; -}; - -class OnroadScreenOffBrightness : public SPOptionControl { - Q_OBJECT - -public: - OnroadScreenOffBrightness(); - - void refresh(); - -private: - Params params; -}; - -class MaxTimeOffroad : public SPOptionControl { - Q_OBJECT - -public: - MaxTimeOffroad(); - - void refresh(); - -private: - Params params; -}; - -class BrightnessControl : public SPOptionControl { - Q_OBJECT - -public: - BrightnessControl(); - - void refresh(); - -private: - Params params; -}; - -class DisplayPanel : public ListWidget { - Q_OBJECT - -public: - explicit DisplayPanel(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - -public slots: - void updateToggles(); - -private: - Params params; - std::map toggles; - - OnroadScreenOff *onroad_screen_off; - OnroadScreenOffBrightness *onroad_screen_off_brightness; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/json_fetcher.h b/selfdrive/ui/qt/offroad/sunnypilot/json_fetcher.h deleted file mode 100644 index f82041e36e..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/json_fetcher.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include -#include -#include -#include - -class JsonFetcher -{ -public: - static QJsonObject getJsonFromURL(const QString &url) - { - const auto qurl = QUrl(url); - QNetworkAccessManager manager; - const QNetworkRequest request(qurl); - QNetworkReply *reply = manager.get(request); - QEventLoop loop; - - // Send GET request - - QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); - loop.exec(); - - if (reply->error() != QNetworkReply::NoError) { - qWarning() << "Failed to fetch data from URL: " << reply->errorString(); - return QJsonObject(); - } - - const QByteArray responseData = reply->readAll(); - const QJsonDocument doc = QJsonDocument::fromJson(responseData); - QJsonObject json = doc.object(); - - reply->deleteLater(); - return json; - } -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h deleted file mode 100644 index dd8e1748bd..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class AutoLaneChangeTimer : public SPOptionControl { - Q_OBJECT - -public: - AutoLaneChangeTimer(); - - void refresh(); - -signals: - void toggleUpdated(); - -private: - Params params; -}; - -class PauseLateralSpeed : public SPOptionControl { - Q_OBJECT - -public: - PauseLateralSpeed(); - - void refresh(); - - signals: - void ToggleUpdated(); - -private: - Params params; -}; - - -class LaneChangeSettings : public QWidget { - Q_OBJECT - -public: - explicit LaneChangeSettings(QWidget* parent = nullptr); - void showEvent(QShowEvent *event) override; - -signals: - void backPress(); - -public slots: - void updateToggles(); - -private: - Params params; - std::map toggles; - - AutoLaneChangeTimer *auto_lane_change_timer; - PauseLateralSpeed *pause_lateral_speed; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h deleted file mode 100644 index 2a949c97a4..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class MadsSettings : public QWidget { - Q_OBJECT - -public: - explicit MadsSettings(QWidget* parent = nullptr); - void showEvent(QShowEvent *event) override; - -signals: - void backPress(); - -public slots: - void updateToggles(); - -private: - Params params; - std::map toggles; - - ButtonParamControl *dlob_settings; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc deleted file mode 100644 index e2a410f9ff..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc +++ /dev/null @@ -1,30 +0,0 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h" - -MonitoringPanel::MonitoringPanel(QWidget *parent) : QFrame(parent) { - main_layout = new QStackedLayout(this); - - ListWidget *list = new ListWidget(this, false); - // param, title, desc, icon - std::vector> toggle_defs{ - { - "HandsOnWheelMonitoring", - tr("Enable Hands on Wheel Monitoring"), - tr("Monitor and alert when driver is not keeping the hands on the steering wheel."), - "../assets/offroad/icon_blank.png", - } - }; - - for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); - - list->addItem(toggle); - toggles[param.toStdString()] = toggle; - } - - monitoringScreen = new QWidget(this); - QVBoxLayout* vlayout = new QVBoxLayout(monitoringScreen); - vlayout->setContentsMargins(50, 20, 50, 20); - - vlayout->addWidget(new ScrollView(list, this), 1); - main_layout->addWidget(monitoringScreen); -} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h deleted file mode 100644 index 214d2f8ac0..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class MonitoringPanel : public QFrame { - Q_OBJECT - -public: - explicit MonitoringPanel(QWidget *parent = nullptr); - -private: - QStackedLayout* main_layout = nullptr; - QWidget* monitoringScreen = nullptr; - Params params; - std::map toggles; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.cc deleted file mode 100644 index 2de70e9b30..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.cc +++ /dev/null @@ -1,51 +0,0 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.h" - -SpeedLimitPolicySettings::SpeedLimitPolicySettings(QWidget* parent) : QWidget(parent) { - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(50, 20, 50, 20); - main_layout->setSpacing(20); - - // Back button - PanelBackButton* back = new PanelBackButton(); - connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); - main_layout->addWidget(back, 0, Qt::AlignLeft); - - ListWidget *list = new ListWidget(this, false); - - speed_limit_policy = new ButtonParamControl( - "SpeedLimitControlPolicy", - tr("Speed Limit Source Policy"), - "", - "../assets/offroad/icon_blank.png", - speed_limit_policy_texts, - 250 - ); - speed_limit_policy->showDescription(); - connect(speed_limit_policy, &ButtonParamControl::buttonToggled, this, &SpeedLimitPolicySettings::updateToggles); - list->addItem(speed_limit_policy); - - param_watcher = new ParamWatcher(this); - - QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { - updateToggles(); - }); - - main_layout->addWidget(new ScrollView(list, this)); -} - -void SpeedLimitPolicySettings::showEvent(QShowEvent *event) { - updateToggles(); -} - -void SpeedLimitPolicySettings::updateToggles() { - param_watcher->addParam("SpeedLimitControlPolicy"); - - if (!isVisible()) { - return; - } - - // TODO: SP: use upstream's setCheckedButton - speed_limit_policy->setButton("SpeedLimitControlPolicy"); - - speed_limit_policy->setDescription(speedLimitPolicyDescriptionBuilder("SpeedLimitControlPolicy", speed_limit_policy_descriptions)); -} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h deleted file mode 100644 index 8026cf7f1b..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include "common/model.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class TorqueFriction : public SPOptionControl { - Q_OBJECT - -public: - TorqueFriction(); - - void refresh(); - -private: - Params params; -}; - -class TorqueMaxLatAccel : public SPOptionControl { - Q_OBJECT - -public: - TorqueMaxLatAccel(); - - void refresh(); - -private: - Params params; -}; - -class SunnypilotPanel : public QFrame { - Q_OBJECT - -public: - explicit SunnypilotPanel(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - void hideEvent(QHideEvent* event) override; - -public slots: - void updateToggles(); - -private: - QStackedLayout* main_layout = nullptr; - QWidget* sunnypilotScreen = nullptr; - MadsSettings* mads_settings = nullptr; - SubPanelButton *slcSettings = nullptr; - SlcSettings* slc_settings = nullptr; - SubPanelButton *slwSettings = nullptr; - SpeedLimitWarningSettings* slw_settings = nullptr; - SubPanelButton *slpSettings = nullptr; - SpeedLimitPolicySettings* slp_settings = nullptr; - LaneChangeSettings* lane_change_settings = nullptr; - CustomOffsetsSettings* custom_offsets_settings = nullptr; - Params params; - std::map toggles; - ParamWatcher *param_watcher; - - TorqueFriction *friction; - TorqueMaxLatAccel *lat_accel_factor; - ButtonParamControl *dlp_settings; - - ScrollView *scrollView = nullptr; - - const QString nnff_description = QString("%1

" - "%2") - .arg(tr("Formerly known as \"NNFF\", this replaces the lateral \"torque\" controller with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy.")) - .arg(tr("Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, or to provide log data for your car if your car is currently unsupported: ") + "#tuning-nnlc"); - - QString nnffDescriptionBuilder(const QString &custom_description) { - QString description = "" + custom_description + "

" + nnff_description; - return description; - } - - const QString custom_offsets_description = QString(tr("Add custom offsets to Camera and Path in sunnypilot.")); - const QString dlp_description = QString(tr("Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions.")); -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc deleted file mode 100644 index e21dc675ae..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h" - -TripsPanel::TripsPanel(QWidget* parent) : QFrame(parent) { - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setMargin(0); - - // main content - main_layout->addSpacing(25); - center_layout = new QStackedLayout(); - - driveStatsWidget = new DriveStats; - driveStatsWidget->setStyleSheet(R"( - QLabel[type="title"] { font-size: 51px; font-weight: 500; } - QLabel[type="number"] { font-size: 78px; font-weight: 500; } - QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; } - )"); - center_layout->addWidget(driveStatsWidget); - - main_layout->addLayout(center_layout, 1); - - setStyleSheet(R"( - * { - color: white; - } - TripsPanel > QLabel { - font-size: 55px; - } - )"); -} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h deleted file mode 100644 index dc77acac75..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h" - -class TripsPanel : public QFrame { - Q_OBJECT - -public: - explicit TripsPanel(QWidget* parent = 0); - -private: - Params params; - - QStackedLayout* center_layout; - DriveStats *driveStatsWidget; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h deleted file mode 100644 index b210225f49..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include - -#include "common/watchdog.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class VehiclePanel : public QWidget { - Q_OBJECT - -public: - explicit VehiclePanel(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - -public slots: - void updateToggles(); - -private: - Params params; - - QStackedLayout* main_layout = nullptr; - QWidget* home = nullptr; - - QPushButton* setCarBtn; - QString set; - - QWidget* home_widget; - QString prompt_select = tr("Select your car"); -}; - -class SPVehiclesTogglesPanel : public ListWidget { - Q_OBJECT -public: - explicit SPVehiclesTogglesPanel(VehiclePanel *parent); - void showEvent(QShowEvent *event) override; - -public slots: - void updateToggles(); - -private: - Params params; - bool is_onroad = false; - - ParamControl *stockLongToyota; - ParamControl *toyotaEnhancedBsm; - - const QString toyotaEnhancedBsmDescription = QString("%1

" - "%2") - .arg(tr("sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects.")) - .arg(tr("Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2.")); - - QString toyotaEnhancedBsmDesciptionBuilder(const QString &custom_description) { - QString description = "" + custom_description + "

" + toyotaEnhancedBsmDescription; - return description; - } -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h deleted file mode 100644 index 3ea5825f59..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/widgets/controls.h" - -class VisualsPanel : public ListWidget { - Q_OBJECT - -public: - explicit VisualsPanel(QWidget *parent = nullptr); - -private: - Params params; - std::map toggles; - - ButtonParamControl *dev_ui_settings; - ButtonParamControl *chevron_info_settings; - ButtonParamControl *sidebar_temp_setting; -}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot_main.h b/selfdrive/ui/qt/offroad/sunnypilot_main.h deleted file mode 100644 index fe4526db08..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot_main.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/offroad/sunnypilot/display_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.h" diff --git a/selfdrive/ui/qt/offroad_home.cc b/selfdrive/ui/qt/offroad_home.cc new file mode 100644 index 0000000000..e9d54a4efb --- /dev/null +++ b/selfdrive/ui/qt/offroad_home.cc @@ -0,0 +1,152 @@ +#include "selfdrive/ui/qt/offroad_home.h" + +#include +#include + +#include "selfdrive/ui/qt/offroad/experimental_mode.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/prime.h" + +// OffroadHome: the offroad home page + +OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(40, 40, 40, 40); + + // top header + QHBoxLayout* header_layout = new QHBoxLayout(); + header_layout->setContentsMargins(0, 0, 0, 0); + header_layout->setSpacing(16); + + update_notif = new QPushButton(tr("UPDATE")); + update_notif->setVisible(false); + update_notif->setStyleSheet("background-color: #364DEF;"); + QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); }); + header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); + + alert_notif = new QPushButton(); + alert_notif->setVisible(false); + alert_notif->setStyleSheet("background-color: #E22C2C;"); + QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); }); + header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); + + version = new ElidedLabel(); + header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); + + main_layout->addLayout(header_layout); + + // main content + main_layout->addSpacing(25); + center_layout = new QStackedLayout(); + + home_widget = new QWidget(this); + { + home_layout = new QHBoxLayout(home_widget); + home_layout->setContentsMargins(0, 0, 0, 0); + home_layout->setSpacing(30); + + // left: PrimeAdWidget + left_widget = new QStackedWidget(this); + QVBoxLayout *left_prime_layout = new QVBoxLayout(); + QWidget *prime_user = new PrimeUserWidget(); + prime_user->setStyleSheet(R"( + border-radius: 10px; + background-color: #333333; + )"); + left_prime_layout->addWidget(prime_user); + left_prime_layout->addStretch(); + left_widget->addWidget(new LayoutWidget(left_prime_layout)); + left_widget->addWidget(new PrimeAdWidget); + left_widget->setStyleSheet("border-radius: 10px;"); + + left_widget->setCurrentIndex(uiState()->hasPrime() ? 0 : 1); + connect(uiState(), &UIState::primeChanged, [=](bool prime) { + left_widget->setCurrentIndex(prime ? 0 : 1); + }); + + home_layout->addWidget(left_widget, 1); + + // right: ExperimentalModeButton, SetupWidget + QWidget* right_widget = new QWidget(this); + QVBoxLayout* right_column = new QVBoxLayout(right_widget); + right_column->setContentsMargins(0, 0, 0, 0); + right_widget->setFixedWidth(750); + right_column->setSpacing(30); + + ExperimentalModeButton *experimental_mode = new ExperimentalModeButton(this); + QObject::connect(experimental_mode, &ExperimentalModeButton::openSettings, this, &OffroadHome::openSettings); + right_column->addWidget(experimental_mode, 1); + + SetupWidget *setup_widget = new SetupWidget; + QObject::connect(setup_widget, &SetupWidget::openSettings, this, &OffroadHome::openSettings); + right_column->addWidget(setup_widget, 1); + + home_layout->addWidget(right_widget, 1); + } + center_layout->addWidget(home_widget); + + // add update & alerts widgets + update_widget = new UpdateAlert(); + QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); + center_layout->addWidget(update_widget); + alerts_widget = new OffroadAlert(); + QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); + center_layout->addWidget(alerts_widget); + + main_layout->addLayout(center_layout, 1); + + // set up refresh timer + timer = new QTimer(this); + timer->callOnTimeout(this, &OffroadHome::refresh); + + setStyleSheet(R"( + * { + color: white; + } + OffroadHome { + background-color: black; + } + OffroadHome > QPushButton { + padding: 15px 30px; + border-radius: 5px; + font-size: 40px; + font-weight: 500; + } + OffroadHome > QLabel { + font-size: 55px; + } + )"); +} + +void OffroadHome::showEvent(QShowEvent *event) { + refresh(); + timer->start(10 * 1000); +} + +void OffroadHome::hideEvent(QHideEvent *event) { + timer->stop(); +} + +void OffroadHome::refresh() { + version->setText(getBrand() + " " + QString::fromStdString(params.get("UpdaterCurrentDescription"))); + + bool updateAvailable = update_widget->refresh(); + int alerts = alerts_widget->refresh(); + + // pop-up new notification + int idx = center_layout->currentIndex(); + if (!updateAvailable && !alerts) { + idx = 0; + } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) { + idx = 1; + } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) { + idx = 2; + } + center_layout->setCurrentIndex(idx); + + update_notif->setVisible(updateAvailable); + alert_notif->setVisible(alerts); + if (alerts) { + alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT"))); + } +} diff --git a/selfdrive/ui/qt/offroad_home.h b/selfdrive/ui/qt/offroad_home.h new file mode 100644 index 0000000000..5796c60f52 --- /dev/null +++ b/selfdrive/ui/qt/offroad_home.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/body.h" +#include "selfdrive/ui/qt/onroad/onroad_home.h" +#include "selfdrive/ui/qt/widgets/offroad_alerts.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h" +#include "selfdrive/ui/sunnypilot/qt/sidebar.h" +#define OnroadWindow OnroadWindowSP +#define OffroadHomeImp OffroadHomeSP +#define LayoutWidget LayoutWidgetSP +#define Sidebar SidebarSP +#define ElidedLabel ElidedLabelSP +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/onroad/onroad_home.h" +#include "selfdrive/ui/qt/sidebar.h" +#endif + +class OffroadHome : public QFrame { + Q_OBJECT + +public: + void do_work(QWidget*& home_widget); + explicit OffroadHome(QWidget* parent = 0); + +signals: + void openSettings(int index = 0, const QString ¶m = ""); + +protected: + QStackedLayout* center_layout; + QWidget* home_widget; + QHBoxLayout *home_layout; + QStackedWidget *left_widget; + +private: + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + void refresh(); + + Params params; + + QTimer* timer; + ElidedLabel* version; + UpdateAlert *update_widget; + OffroadAlert* alerts_widget; + QPushButton* alert_notif; + QPushButton* update_notif; +}; diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index bb19798e92..b08420a713 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -1,36 +1,17 @@ + #include "selfdrive/ui/qt/onroad/annotated_camera.h" +#include #include #include -#include -#include #include "common/swaglog.h" #include "selfdrive/ui/qt/onroad/buttons.h" #include "selfdrive/ui/qt/util.h" -static std::pair getFeatureStatus(int value, QStringList text_list, QStringList color_list, - bool condition, QString off_text) { - - QString text("Error"); - QColor color("#ffffff"); - - for (int i = 0; i < text_list.size() && i < color_list.size(); ++i) { - if (value == i) { - text = condition ? text_list[i] : off_text; - color = condition ? QColor(color_list[i]) : QColor("#ffffff"); - break; // Exit the loop once a match is found - } - } - - return {text, color}; -} - - // Window that shows camera view and variety of info drawn on top AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) { pm = std::make_unique>({"uiDebug"}); - e2e_state = std::make_unique>({"e2eLongStateSP"}); main_layout = new QVBoxLayout(this); main_layout->setMargin(UI_BORDER_SIZE); @@ -39,68 +20,7 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par experimental_btn = new ExperimentalButton(this); main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight); - onroad_settings_btn = new OnroadSettingsButton(this); - - map_settings_btn = new MapSettingsButton(this); - dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5}); - map_img = loadPixmap("../assets/img_world_icon.png", {subsign_img_size, subsign_img_size}); - left_img = loadPixmap("../assets/img_turn_left_icon.png", {subsign_img_size, subsign_img_size}); - right_img = loadPixmap("../assets/img_turn_right_icon.png", {subsign_img_size, subsign_img_size}); - - buttons_layout = new QHBoxLayout(); - buttons_layout->setContentsMargins(0, 0, 10, 20); - main_layout->addLayout(buttons_layout); - updateButtonsLayout(false); -} - -void AnnotatedCameraWidget::mousePressEvent(QMouseEvent* e) { - bool propagate_event = true; - - UIState *s = uiState(); - UIScene &scene = s->scene; - const SubMaster &sm = *(s->sm); - const auto longitudinal_plan_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP(); - - if (longitudinal_plan_sp.getSpeedLimit() > 0.0 && sl_sign_rect.contains(e->x(), e->y())) { - // If touching the speed limit sign area when visible - scene.last_speed_limit_sign_tap = seconds_since_boot(); - params.putBool("LastSpeedLimitSignTap", true); - scene.speed_limit_control_enabled = !scene.speed_limit_control_enabled; - params.putBool("EnableSlc", scene.speed_limit_control_enabled); - propagate_event = false; - } - - if (propagate_event) { - QWidget::mousePressEvent(e); - } -} - -void AnnotatedCameraWidget::updateButtonsLayout(bool is_rhd) { - QLayoutItem *item; - while ((item = buttons_layout->takeAt(0)) != nullptr) { - delete item; - } - - buttons_layout->setContentsMargins(0, 0, 10, rn_offset != 0 ? rn_offset + 10 : 20); - - if (is_rhd) { - buttons_layout->addSpacing(map_settings_btn->isVisible() ? 30 : 0); - buttons_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignLeft); - - buttons_layout->addStretch(1); - - buttons_layout->addWidget(onroad_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight); - buttons_layout->addSpacing(onroad_settings_btn->isVisible() ? 216 : 0); - } else { - buttons_layout->addSpacing(onroad_settings_btn->isVisible() ? 216 : 0); - buttons_layout->addWidget(onroad_settings_btn, 0, Qt::AlignBottom | Qt::AlignLeft); - - buttons_layout->addStretch(1); - - buttons_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight); - buttons_layout->addSpacing(map_settings_btn->isVisible() ? 30 : 0); // Add spacing to the right - } } void AnnotatedCameraWidget::updateState(const UIState &s) { @@ -108,18 +28,8 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { const SubMaster &sm = *(s.sm); const bool cs_alive = sm.alive("controlsState"); - const bool nav_alive = sm.alive("navInstruction") && sm["navInstruction"].getValid(); const auto cs = sm["controlsState"].getControlsState(); - const auto cs_sp = sm["controlsStateSP"].getControlsStateSP(); const auto car_state = sm["carState"].getCarState(); - const auto nav_instruction = sm["navInstruction"].getNavInstruction(); - const auto car_control = sm["carControl"].getCarControl(); - const auto radar_state = sm["radarState"].getRadarState(); - const auto is_gps_location_external = sm.rcv_frame("gpsLocationExternal") > 1; - const auto gpsLocation = is_gps_location_external ? sm["gpsLocationExternal"].getGpsLocationExternal() : sm["gpsLocation"].getGpsLocation(); - const auto ltp = sm["liveTorqueParameters"].getLiveTorqueParameters(); - const auto lateral_plan_sp = sm["lateralPlanSPDEPRECATED"].getLateralPlanSPDEPRECATED(); - car_params = sm["carParams"].getCarParams(); // Handle older routes where vCruiseCluster is not set float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster(); @@ -132,250 +42,23 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { // Handle older routes where vEgoCluster is not set v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0; float v_ego = v_ego_cluster_seen ? car_state.getVEgoCluster() : car_state.getVEgo(); - v_ego = s.scene.true_vego_ui ? car_state.getVEgo() : v_ego; speed = cs_alive ? std::max(0.0, v_ego) : 0.0; speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH; - auto speed_limit_sign = nav_instruction.getSpeedLimitSign(); - speedLimit = nav_alive ? nav_instruction.getSpeedLimit() : 0.0; - speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); - - has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD); - has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA); is_metric = s.scene.is_metric; speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph"); hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE); status = s.status; - // TODO: Add minimum speed? - left_blindspot = cs_alive && car_state.getLeftBlindspot(); - right_blindspot = cs_alive && car_state.getRightBlindspot(); - - steerOverride = car_state.getSteeringPressed(); - gasOverride = car_state.getGasPressed(); - latActive = car_control.getLatActive(); - madsEnabled = car_state.getMadsEnabled(); - - brakeLights = car_state.getBrakeLightsDEPRECATED() && s.scene.visual_brake_lights; - - standStillTimer = s.scene.stand_still_timer; - standStill = car_state.getStandstill(); - standstillElapsedTime = lateral_plan_sp.getStandstillElapsed(); - - hideVEgoUi = s.scene.hide_vego_ui; - - splitPanelVisible = s.scene.map_visible || s.scene.onroad_settings_visible; - - // ############################## DEV UI START ############################## - lead_d_rel = radar_state.getLeadOne().getDRel(); - lead_v_rel = radar_state.getLeadOne().getVRel(); - lead_status = radar_state.getLeadOne().getStatus(); - lateralState = QString::fromStdString(cs_sp.getLateralState()); - angleSteers = car_state.getSteeringAngleDeg(); - steerAngleDesired = cs.getLateralControlState().getPidState().getSteeringAngleDesiredDeg(); - curvature = cs.getCurvature(); - roll = sm["liveParameters"].getLiveParameters().getRoll(); - memoryUsagePercent = sm["deviceState"].getDeviceState().getMemoryUsagePercent(); - devUiInfo = s.scene.dev_ui_info; - gpsAccuracy = is_gps_location_external ? gpsLocation.getHorizontalAccuracy() : 1.0; //External reports accuracy, internal does not. - altitude = gpsLocation.getAltitude(); - vEgo = car_state.getVEgo(); - aEgo = car_state.getAEgo(); - steeringTorqueEps = car_state.getSteeringTorqueEps(); - bearingAccuracyDeg = gpsLocation.getBearingAccuracyDeg(); - bearingDeg = gpsLocation.getBearingDeg(); - torquedUseParams = (ltp.getUseParams() || s.scene.live_torque_toggle) && !s.scene.torqued_override; - latAccelFactorFiltered = ltp.getLatAccelFactorFiltered(); - frictionCoefficientFiltered = ltp.getFrictionCoefficientFiltered(); - liveValid = ltp.getLiveValid(); - // ############################## DEV UI END ############################## - - btnPerc = s.scene.sleep_btn_opacity * 0.05; - - left_blinker = car_state.getLeftBlinker(); - right_blinker = car_state.getRightBlinker(); - lane_change_edge_block = lateral_plan_sp.getLaneChangeEdgeBlockDEPRECATED(); - // update engageability/experimental mode button experimental_btn->updateState(s); - // update onroad settings button state - onroad_settings_btn->updateState(s); - // update DM icon auto dm_state = sm["driverMonitoringState"].getDriverMonitoringState(); dmActive = dm_state.getIsActiveMode(); rightHandDM = dm_state.getIsRHD(); // DM icon transition dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0); - - // update buttons layout - updateButtonsLayout(rightHandDM); - - // hide map settings button for alerts and flip for right hand DM - if (map_settings_btn->isEnabled()) { - map_settings_btn->setVisible(!hideBottomIcons); - buttons_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); - } - - // hide onroad settings button for alerts and flip for right hand DM - if (onroad_settings_btn->isEnabled()) { - onroad_settings_btn->setVisible(!hideBottomIcons); - buttons_layout->setAlignment(onroad_settings_btn, (rightHandDM ? Qt::AlignRight : Qt::AlignLeft) | Qt::AlignBottom); - } - - const auto lp_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP(); - slcState = lp_sp.getSpeedLimitControlState(); - - speedLimitControlToggle = s.scene.speed_limit_control_enabled; - - const auto vtcState = lp_sp.getVisionTurnControllerState(); - const float vtc_speed = lp_sp.getVisionTurnSpeed() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); - const auto lpSoruce = lp_sp.getLongitudinalPlanSource(); - QColor vtc_color = tcs_colors[int(vtcState)]; - vtc_color.setAlpha(lpSoruce == cereal::LongitudinalPlanSP::LongitudinalPlanSource::TURN ? 255 : 100); - - showVTC = vtcState > cereal::LongitudinalPlanSP::VisionTurnControllerState::DISABLED; - vtcSpeed = QString::number(std::nearbyint(vtc_speed)); - vtcColor = vtc_color; - showDebugUI = s.scene.show_debug_ui; - - const auto lmd_sp = sm["liveMapDataSP"].getLiveMapDataSP(); - - const auto data_type = int(lmd_sp.getDataType()); - const QString data_type_draw(data_type == 2 ? "🌐 " : ""); - roadName = QString::fromStdString(lmd_sp.getCurrentRoadName()); - roadName = !roadName.isEmpty() ? data_type_draw + roadName : ""; - - float speed_limit_slc = lp_sp.getSpeedLimit() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); - const float speed_limit_offset = lp_sp.getSpeedLimitOffset() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); - const bool sl_force_active = speedLimitControlToggle && - seconds_since_boot() < s.scene.last_speed_limit_sign_tap + 2.0; - const bool sl_inactive = !sl_force_active && (!speedLimitControlToggle || - slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::INACTIVE); - const bool sl_temp_inactive = !sl_force_active && (speedLimitControlToggle && - slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::TEMP_INACTIVE); - const bool sl_pre_active = !sl_force_active && (speedLimitControlToggle && - slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::PRE_ACTIVE); - const int sl_distance = int(lp_sp.getDistToSpeedLimit() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH) / 10.0) * 10; - const QString sl_distance_str(QString::number(sl_distance) + (s.scene.is_metric ? "m" : "f")); - const QString sl_offset_str(speed_limit_offset > 0.0 ? speed_limit_offset < 0.0 ? - "-" + QString::number(std::nearbyint(std::abs(speed_limit_offset))) : - "+" + QString::number(std::nearbyint(speed_limit_offset)) : ""); - const QString sl_inactive_str(sl_temp_inactive && s.scene.speed_limit_control_engage_type == 0 ? "TEMP" : ""); - const QString sl_substring(sl_inactive || sl_temp_inactive || sl_pre_active ? sl_inactive_str : - sl_distance > 0 ? sl_distance_str : sl_offset_str); - - showSpeedLimit = speed_limit_slc > 0.0; - speedLimitSLC = speed_limit_slc; - speedLimitSLCOffset = speed_limit_offset; - slcSubText = sl_substring; - slcSubTextSize = sl_inactive || sl_temp_inactive || sl_distance > 0 ? 25.0 : 27.0; - mapSourcedSpeedLimit = lp_sp.getIsMapSpeedLimit(); - slcActive = !sl_inactive && !sl_temp_inactive; - overSpeedLimit = showSpeedLimit && s.scene.speed_limit_warning_type != 0 && - (std::nearbyint(speed_limit_slc + s.scene.speed_limit_warning_value_offset) < std::nearbyint(speed)); - plus_arrow_up_img = loadPixmap("../assets/img_plus_arrow_up", {105, 105}); - minus_arrow_down_img = loadPixmap("../assets/img_minus_arrow_down", {105, 105}); - - const float tsc_speed = lp_sp.getTurnSpeed() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); - const auto tscState = lp_sp.getTurnSpeedControlState(); - const int t_distance = int(lp_sp.getDistToTurn() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH) / 10.0) * 10; - const QString t_distance_str(QString::number(t_distance) + (s.scene.is_metric ? "m" : "f")); - - showTurnSpeedLimit = tsc_speed > 0.0 && std::round(tsc_speed) < 224 && (tsc_speed < speed || s.scene.show_debug_ui); - turnSpeedLimit = QString::number(std::nearbyint(tsc_speed)); - tscSubText = t_distance > 0 ? t_distance_str : QString(""); - tscActive = tscState > cereal::LongitudinalPlanSP::SpeedLimitControlState::TEMP_INACTIVE; - curveSign = lp_sp.getTurnSign(); - - // TODO: Add toggle variables to cereal, and parse from cereal - longitudinalPersonality = s.scene.longitudinal_personality; - dynamicLaneProfile = s.scene.dynamic_lane_profile; - mpcMode = QString::fromStdString(lp_sp.getE2eBlended()); - mpcMode = (mpcMode == "blended") ? mpcMode.replace(0, 1, mpcMode[0].toUpper()) : mpcMode.toUpper(); - - static int reverse_delay = 0; - bool reverse_allowed = false; - if (int(car_state.getGearShifter()) != 4) { - reverse_delay = 0; - reverse_allowed = false; - } else { - reverse_delay += 50; - if (reverse_delay >= 1000) { - reverse_allowed = true; - } - } - - reversing = reverse_allowed; - - cruiseStateEnabled = car_state.getCruiseState().getEnabled(); - - int e2eLStatus = 0; - static bool chime_sent = false; - static int chime_count = 0; - int chime_prompt = 0; - static float last_lead_distance = -1; - const float lead_distance = radar_state.getLeadOne().getDRel(); - - if (s.scene.e2eX[12] > 30 && car_state.getVEgo() < 1.0) { - e2eLStatus = 2; - } else if ((s.scene.e2eX[12] > 0 && s.scene.e2eX[12] < 80) || s.scene.e2eX[12] < 0) { - e2eLStatus = 1; - } else { - e2eLStatus = 0; - } - - if (!car_state.getStandstill()) { - chime_prompt = 0; - chime_sent = false; - chime_count = 0; - - if (last_lead_distance != -1) { - last_lead_distance = -1; - } - } - - if ((cruiseStateEnabled || car_state.getBrakeLightsDEPRECATED()) && !car_state.getGasPressed() && car_state.getStandstill()) { - if (e2eLStatus == 2 && !radar_state.getLeadOne().getStatus()) { - if (chime_sent) { - chime_count = 0; - } else { - chime_count += 1; - } - if (s.scene.e2e_long_alert_light && chime_count >= 2 && !chime_sent) { - chime_prompt = 1; - chime_sent = true; - } else { - chime_prompt = 0; - } - } else if (radar_state.getLeadOne().getStatus()) { - if ((last_lead_distance == -1) || (lead_distance < last_lead_distance)) { - last_lead_distance = lead_distance; - } - if (s.scene.e2e_long_alert_lead && (lead_distance - last_lead_distance > 1.0) && !chime_sent) { - chime_prompt = 2; - chime_sent = true; - } else { - chime_prompt = 0; - } - } else { - chime_prompt = 0; - } - } else { - } - - e2eStatus = chime_prompt; - e2eState = e2eLStatus; - e2eLongAlertUi = s.scene.e2e_long_alert_ui; - dynamicExperimentalControlToggle = s.scene.dynamic_experimental_control; - speedLimitWarningFlash = s.scene.speed_limit_warning_flash; - experimentalMode = cs.getExperimentalMode(); - - featureStatusToggle = s.scene.feature_status_toggle; - - experimental_btn->setVisible(!(showDebugUI && showVTC)); - drivingModelGen = s.scene.driving_model_generation; } void AnnotatedCameraWidget::drawHud(QPainter &p) { @@ -387,33 +70,18 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { bg.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0)); p.fillRect(0, 0, width(), UI_HEADER_HEIGHT, bg); - QString speedLimitStr = (speedLimit > 1) ? QString::number(std::nearbyint(speedLimit)) : "–"; - QString speedLimitStrSlc = showSpeedLimit ? QString::number(std::nearbyint(speedLimitSLC)) : "–"; QString speedStr = QString::number(std::nearbyint(speed)); QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : "–"; - const bool isNavSpeedLimit = has_us_speed_limit || has_eu_speed_limit; - - // Draw outer box + border to contain set speed and speed limit - const int sign_margin = 12; - const int us_sign_height = slcSubText == "" ? 186 : 216; - const int eu_sign_size = 176; + // Draw outer box + border to contain set speed const QSize default_size = {172, 204}; QSize set_speed_size = default_size; - if (is_metric || has_eu_speed_limit) set_speed_size.rwidth() = 200; - if ((mapSourcedSpeedLimit && !is_metric && speedLimitStrSlc.size() >= 3) || - (has_us_speed_limit && speedLimitStr.size() >= 3)) set_speed_size.rwidth() = 223; - - if ((mapSourcedSpeedLimit && !is_metric) || has_us_speed_limit) set_speed_size.rheight() += us_sign_height + sign_margin; - else if ((mapSourcedSpeedLimit && is_metric) || has_eu_speed_limit) set_speed_size.rheight() += eu_sign_size + sign_margin; - - int top_radius = 32; - int bottom_radius = ((mapSourcedSpeedLimit && is_metric) || has_eu_speed_limit) ? 100 : 32; + if (is_metric) set_speed_size.rwidth() = 200; QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size); p.setPen(QPen(whiteColor(75), 6)); p.setBrush(blackColor(166)); - drawRoundedRect(p, set_speed_rect, top_radius, top_radius, bottom_radius, bottom_radius); + p.drawRoundedRect(set_speed_rect, 32, 32); // Draw MAX QColor max_color = QColor(0x80, 0xd8, 0xa6, 0xff); @@ -421,20 +89,8 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { if (is_cruise_set) { if (status == STATUS_DISENGAGED) { max_color = whiteColor(); - } else if (status == STATUS_OVERRIDE && gasOverride) { + } else if (status == STATUS_OVERRIDE) { max_color = QColor(0x91, 0x9b, 0x95, 0xff); - } else if (speedLimitSLC > 0) { - auto interp_color = [=](QColor c1, QColor c2, QColor c3) { - return speedLimitSLC > 0 ? interpColor(setSpeed, {speedLimitSLC + 5, speedLimitSLC + 15, speedLimitSLC + 25}, {c1, c2, c3}) : c1; - }; - max_color = interp_color(max_color, QColor(0xff, 0xe4, 0xbf), QColor(0xff, 0xbf, 0xbf)); - set_speed_color = interp_color(set_speed_color, QColor(0xff, 0x95, 0x00), QColor(0xff, 0x00, 0x00)); - } else if (speedLimit > 0) { - auto interp_color = [=](QColor c1, QColor c2, QColor c3) { - return speedLimit > 0 ? interpColor(setSpeed, {speedLimit + 5, speedLimit + 15, speedLimit + 25}, {c1, c2, c3}) : c1; - }; - max_color = interp_color(max_color, QColor(0xff, 0xe4, 0xbf), QColor(0xff, 0xbf, 0xbf)); - set_speed_color = interp_color(set_speed_color, QColor(0xff, 0x95, 0x00), QColor(0xff, 0x00, 0x00)); } } else { max_color = QColor(0xa6, 0xa6, 0xa6, 0xff); @@ -447,116 +103,11 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { p.setPen(set_speed_color); p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr); - const QRect sign_rect = set_speed_rect.adjusted(sign_margin, default_size.height(), -sign_margin, -sign_margin); - sl_sign_rect = sign_rect; - - speedLimitWarning(p, sign_rect, sign_margin); - - // US/Canada (MUTCD style) sign - if (((mapSourcedSpeedLimit && !is_metric && !isNavSpeedLimit) || has_us_speed_limit) && slcShowSign) { - p.setPen(Qt::NoPen); - p.setBrush(whiteColor()); - p.drawRoundedRect(sign_rect, 24, 24); - p.setPen(QPen(blackColor(), 6)); - p.drawRoundedRect(sign_rect.adjusted(9, 9, -9, -9), 16, 16); - - p.setFont(InterFont(28, QFont::DemiBold)); - p.drawText(sign_rect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("SPEED")); - p.drawText(sign_rect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT")); - p.setFont(InterFont(70, QFont::Bold)); - if (overSpeedLimit) p.setPen(QColor(255, 0, 0, 255)); - else p.setPen(blackColor()); - p.drawText(sign_rect.adjusted(0, 85, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitStrSlc); - - // Speed limit offset value - p.setFont(InterFont(32, QFont::Bold)); - p.setPen(blackColor()); - p.drawText(sign_rect.adjusted(0, 85 + 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, slcSubText); - } - - // EU (Vienna style) sign - if (((mapSourcedSpeedLimit && is_metric && !isNavSpeedLimit) || has_eu_speed_limit) && slcShowSign) { - p.setPen(Qt::NoPen); - p.setBrush(whiteColor()); - p.drawEllipse(sign_rect); - p.setPen(QPen(Qt::red, 20)); - p.drawEllipse(sign_rect.adjusted(16, 16, -16, -16)); - - p.setFont(InterFont((speedLimitStrSlc.size() >= 3) ? 60 : 70, QFont::Bold)); - if (overSpeedLimit) p.setPen(QColor(255, 0, 0, 255)); - else p.setPen(blackColor()); - p.drawText(sign_rect, Qt::AlignCenter, speedLimitStrSlc); - - // Speed limit offset value - p.setFont(InterFont(slcSubTextSize, QFont::Bold)); - p.setPen(blackColor()); - p.drawText(sign_rect.adjusted(0, 27, 0, 0), Qt::AlignTop | Qt::AlignHCenter, slcSubText); - } - // current speed - if (!hideVEgoUi) { - p.setFont(InterFont(176, QFont::Bold)); - drawColoredText(p, rect().center().x(), 210, speedStr, brakeLights ? QColor(0xff, 0, 0, 255) : QColor(0xff, 0xff, 0xff, 255)); - p.setFont(InterFont(66)); - drawText(p, rect().center().x(), 290, speedUnit, 200); - } - - if (!reversing) { - // ####### 1 ROW ####### - QRect bar_rect1(rect().left(), rect().bottom() - 60, rect().width(), 61); - if (!splitPanelVisible && devUiInfo == 2) { - p.setPen(Qt::NoPen); - p.setBrush(QColor(0, 0, 0, 100)); - p.drawRect(bar_rect1); - drawNewDevUi2(p, bar_rect1.left(), bar_rect1.center().y()); - } - - // ####### 1 COLUMN ######## - QRect rc2(rect().right() - (UI_BORDER_SIZE * 2), UI_BORDER_SIZE * 1.5, 184, 152); - if (devUiInfo != 0) { - drawRightDevUi(p, rect().right() - 184 - UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2 + rc2.height()); - } - - int rn_btn = 0; - rn_btn = !splitPanelVisible && devUiInfo == 2 ? 35 : 0; - rn_offset = rn_btn; - - // Stand Still Timer - if (standStillTimer && standStill && !splitPanelVisible) { - drawStandstillTimer(p, rect().right() - 650, 30 + 160 + 250); - } - - // V-TSC - if (showDebugUI && showVTC) { - drawVisionTurnControllerUI(p, rect().right() - 184 - (UI_BORDER_SIZE * 1.5), int(UI_BORDER_SIZE * 1.5), 184, vtcColor, vtcSpeed, 100); - } - - // Bottom bar road name - if (showDebugUI && !roadName.isEmpty()) { - int font_size = splitPanelVisible ? 38 : 50; - int h = splitPanelVisible ? 18 : 26; - p.setFont(InterFont(font_size, QFont::Bold)); - drawRoadNameText(p, rect().center().x(), h, roadName, QColor(255, 255, 255, 255)); - } - - // Turn Speed Sign - if (showTurnSpeedLimit) { - QRect rc = sign_rect; - rc.moveTop(sign_rect.bottom() + UI_BORDER_SIZE); - drawTrunSpeedSign(p, rc, turnSpeedLimit, tscSubText, curveSign, tscActive); - } - } - - // E2E Status - if (e2eLongAlertUi && e2eState != 0) { - drawE2eStatus(p, UI_BORDER_SIZE * 2 + 190, 45, 150, 150, e2eState); - } - - if (!hideBottomIcons && featureStatusToggle) { - int x = UI_BORDER_SIZE * 2 + (rightHandDM ? 600 : 370); - int feature_status_text_x = rightHandDM ? rect().right() - x : x; - drawFeatureStatusText(p, feature_status_text_x, rect().bottom() - 160 - rn_offset); - } + p.setFont(InterFont(176, QFont::Bold)); + drawText(p, rect().center().x(), 210, speedStr); + p.setFont(InterFont(66)); + drawText(p, rect().center().x(), 290, speedUnit, 200); p.restore(); } @@ -569,711 +120,6 @@ void AnnotatedCameraWidget::drawText(QPainter &p, int x, int y, const QString &t p.drawText(real_rect.x(), real_rect.bottom(), text); } -void AnnotatedCameraWidget::drawColoredText(QPainter &p, int x, int y, const QString &text, QColor color) { - QRect real_rect = p.fontMetrics().boundingRect(text); - real_rect.moveCenter({x, y - real_rect.height() / 2}); - - p.setPen(color); - p.drawText(real_rect.x(), real_rect.bottom(), text); -} - -void AnnotatedCameraWidget::drawCenteredText(QPainter &p, int x, int y, const QString &text, QColor color) { - QRect real_rect = p.fontMetrics().boundingRect(text); - real_rect.moveCenter({x, y}); - - p.setPen(color); - p.drawText(real_rect, Qt::AlignCenter, text); -} - -void AnnotatedCameraWidget::drawRoadNameText(QPainter &p, int x, int y, const QString &text, QColor color) { - QRect real_rect = p.fontMetrics().boundingRect(text); - real_rect.moveCenter({x, y}); - - QRect real_rect_adjusted(real_rect); - real_rect_adjusted.adjust(-UI_ROAD_NAME_MARGIN_X, 5, UI_ROAD_NAME_MARGIN_X, 0); - QPainterPath path; - path.addRoundedRect(real_rect_adjusted, 10, 10); - p.setPen(Qt::NoPen); - p.setBrush(QColor(0, 0, 0, 100)); - p.drawPath(path); - - p.setPen(color); - p.drawText(real_rect, Qt::AlignCenter, text); -} - -void AnnotatedCameraWidget::drawVisionTurnControllerUI(QPainter &p, int x, int y, int size, const QColor &color, - const QString &vision_speed, int alpha) { - QRect rvtc(x, y, size, size); - p.setPen(QPen(color, 10)); - p.setBrush(QColor(0, 0, 0, alpha)); - p.drawRoundedRect(rvtc, 20, 20); - p.setPen(Qt::NoPen); - - p.setFont(InterFont(56, QFont::DemiBold)); - drawCenteredText(p, rvtc.center().x(), rvtc.center().y(), vision_speed, color); -} - -void AnnotatedCameraWidget::drawStandstillTimer(QPainter &p, int x, int y) { - char lab_str[16]; - char val_str[16]; - int minute = (int)(standstillElapsedTime / 60); - int second = (int)((standstillElapsedTime) - (minute * 60)); - - if (standStill) { - snprintf(lab_str, sizeof(lab_str), "STOP"); - snprintf(val_str, sizeof(val_str), "%01d:%02d", minute, second); - } - - p.setFont(InterFont(125, QFont::DemiBold)); - drawColoredText(p, x, y, QString(lab_str), QColor(255, 175, 3, 240)); - p.setFont(InterFont(150, QFont::DemiBold)); - drawColoredText(p, x, y + 150, QString(val_str), QColor(255, 255, 255, 240)); -} - -void AnnotatedCameraWidget::drawCircle(QPainter &p, int x, int y, int r, QBrush bg) { - p.setPen(Qt::NoPen); - p.setBrush(bg); - p.drawEllipse(x - r, y - r, 2 * r, 2 * r); -} - -void AnnotatedCameraWidget::drawSpeedSign(QPainter &p, QRect rc, const QString &speed_limit, const QString &sub_text, - int subtext_size, bool is_map_sourced, bool is_active) { - const QColor ring_color = is_active ? QColor(255, 0, 0, 255) : QColor(0, 0, 0, 50); - const QColor inner_color = QColor(255, 255, 255, is_active ? 255 : 85); - const QColor text_color = QColor(0, 0, 0, is_active ? 255 : 85); - - const int x = rc.center().x(); - const int y = rc.center().y(); - const int r = rc.width() / 2.0f; - - drawCircle(p, x, y, r, ring_color); - drawCircle(p, x, y, int(r * 0.8f), inner_color); - - p.setFont(InterFont(89, QFont::Bold)); - drawCenteredText(p, x, y, speed_limit, text_color); - p.setFont(InterFont(subtext_size, QFont::Bold)); - drawCenteredText(p, x, y + 55, sub_text, text_color); - - if (is_map_sourced) { - p.setPen(Qt::NoPen); - p.setOpacity(is_active ? 1.0 : 0.3); - p.drawPixmap(x - subsign_img_size / 2, y - 55 - subsign_img_size / 2, map_img); - p.setOpacity(1.0); - } -} - -void AnnotatedCameraWidget::drawTrunSpeedSign(QPainter &p, QRect rc, const QString &turn_speed, const QString &sub_text, - int curv_sign, bool is_active) { - const QColor border_color = is_active ? QColor(255, 0, 0, 255) : QColor(0, 0, 0, 50); - const QColor inner_color = QColor(255, 255, 255, is_active ? 255 : 85); - const QColor text_color = QColor(0, 0, 0, is_active ? 255 : 85); - - const int x = rc.center().x(); - const int y = 184 * 2 + UI_BORDER_SIZE + 202; - const int width = 184; - - const float stroke_w = 15.0; - const float cS = stroke_w / 2.0 + 4.5; // half width of the stroke on the corners of the triangle - const float R = width / 2.0 - stroke_w / 2.0; - const float A = 0.73205; - const float h2 = 2.0 * R / (1.0 + A); - const float h1 = A * h2; - const float L = 4.0 * R / sqrt(3.0); - - // Draw the internal triangle, compensate for stroke width. Needed to improve rendering when in inactive - // state due to stroke transparency being different from inner transparency. - QPainterPath path; - path.moveTo(x, y - R + cS); - path.lineTo(x - L / 2.0 + cS, y + h1 + h2 - R - stroke_w / 2.0); - path.lineTo(x + L / 2.0 - cS, y + h1 + h2 - R - stroke_w / 2.0); - path.lineTo(x, y - R + cS); - p.setPen(Qt::NoPen); - p.setBrush(inner_color); - p.drawPath(path); - - // Draw the stroke - QPainterPath stroke_path; - stroke_path.moveTo(x, y - R); - stroke_path.lineTo(x - L / 2.0, y + h1 + h2 - R); - stroke_path.lineTo(x + L / 2.0, y + h1 + h2 - R); - stroke_path.lineTo(x, y - R); - p.setPen(QPen(border_color, stroke_w, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - p.setBrush(Qt::NoBrush); - p.drawPath(stroke_path); - - // Draw the turn sign - if (curv_sign != 0) { - p.setPen(Qt::NoPen); - p.setOpacity(is_active ? 1.0 : 0.3); - p.drawPixmap(int(x - (subsign_img_size / 2)), int(y - R + stroke_w + 30), curv_sign > 0 ? left_img : right_img); - p.setOpacity(1.0); - } - - // Draw the texts. - p.setFont(InterFont(67, QFont::Bold)); - drawCenteredText(p, x, y + 25, turn_speed, text_color); - p.setFont(InterFont(22, QFont::Bold)); - drawCenteredText(p, x, y + 65, sub_text, text_color); -} - -// ############################## DEV UI START ############################## -void AnnotatedCameraWidget::drawCenteredLeftText(QPainter &p, int x, int y, const QString &text1, QColor color1, const QString &text2, const QString &text3, QColor color2) { - QFontMetrics fm(p.font()); - QRect init_rect = fm.boundingRect(text1 + " "); - QRect real_rect = fm.boundingRect(init_rect, 0, text1 + " "); - real_rect.moveCenter({x, y}); - - QRect init_rect3 = fm.boundingRect(text3); - QRect real_rect3 = fm.boundingRect(init_rect3, 0, text3); - real_rect3.moveTop(real_rect.top()); - real_rect3.moveLeft(real_rect.right() + 135); - - QRect init_rect2 = fm.boundingRect(text2); - QRect real_rect2 = fm.boundingRect(init_rect2, 0, text2); - real_rect2.moveTop(real_rect.top()); - real_rect2.moveRight(real_rect.right() + 125); - - p.setPen(color1); - p.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, text1); - - p.setPen(color2); - p.drawText(real_rect2, Qt::AlignRight | Qt::AlignVCenter, text2); - p.drawText(real_rect3, Qt::AlignLeft | Qt::AlignVCenter, text3); -} - -int AnnotatedCameraWidget::drawDevUiElementRight(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) { - p.setFont(InterFont(30 * 2, QFont::Bold)); - drawColoredText(p, x + 92, y + 80, value, color); - - p.setFont(InterFont(28, QFont::Bold)); - drawText(p, x + 92, y + 80 + 42, label, 255); - - if (units.length() > 0) { - p.save(); - p.translate(x + 54 + 30 - 3 + 92 + 30, y + 37 + 25); - p.rotate(-90); - drawText(p, 0, 0, units, 255); - p.restore(); - } - - return 110; -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getDRel() { - QString value = lead_status ? QString::number(lead_d_rel, 'f', 0) : "-"; - QColor color = QColor(255, 255, 255, 255); - - if (lead_status) { - // Orange if close, Red if very close - if (lead_d_rel < 5) { - color = QColor(255, 0, 0, 255); - } else if (lead_d_rel < 15) { - color = QColor(255, 188, 0, 255); - } - } - - return UiElement(value, "REL DIST", "m", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getVRel() { - QString value = lead_status ? QString::number(lead_v_rel * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-"; - QColor color = QColor(255, 255, 255, 255); - - if (lead_status) { - // Red if approaching faster than 10mph - // Orange if approaching (negative) - if (lead_v_rel < -4.4704) { - color = QColor(255, 0, 0, 255); - } else if (lead_v_rel < 0) { - color = QColor(255, 188, 0, 255); - } - } - - return UiElement(value, "REL SPEED", speedUnit, color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getSteeringAngleDeg() { - QString value = QString("%1%2%3").arg(QString::number(angleSteers, 'f', 1)).arg("°").arg(""); - QColor color = (madsEnabled && latActive) ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); - - // Red if large steering angle - // Orange if moderate steering angle - if (std::fabs(angleSteers) > 180) { - color = QColor(255, 0, 0, 255); - } else if (std::fabs(angleSteers) > 90) { - color = QColor(255, 188, 0, 255); - } - - return UiElement(value, "REAL STEER", "", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getActualLateralAccel() { - float actualLateralAccel = (curvature * pow(vEgo, 2)) - (roll * 9.81); - - QString value = QString::number(actualLateralAccel, 'f', 2); - QColor color = (madsEnabled && latActive) ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); - - return UiElement(value, "ACTUAL LAT", "m/s²", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getSteeringAngleDesiredDeg() { - QString value = (madsEnabled && latActive) ? QString("%1%2%3").arg(QString::number(steerAngleDesired, 'f', 1)).arg("°").arg("") : "-"; - QColor color = QColor(255, 255, 255, 255); - - if (madsEnabled && latActive) { - // Red if large steering angle - // Orange if moderate steering angle - if (std::fabs(angleSteers) > 180) { - color = QColor(255, 0, 0, 255); - } else if (std::fabs(angleSteers) > 90) { - color = QColor(255, 188, 0, 255); - } else { - color = QColor(0, 255, 0, 255); - } - } - - return UiElement(value, "DESIRED STEER", "", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getMemoryUsagePercent() { - QString value = QString("%1%2").arg(QString::number(memoryUsagePercent, 'd', 0)).arg("%"); - QColor color = (memoryUsagePercent > 85) ? QColor(255, 188, 0, 255) : QColor(255, 255, 255, 255); - - return UiElement(value, "RAM", "", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getAEgo() { - QString value = QString::number(aEgo, 'f', 1); - QColor color = QColor(255, 255, 255, 255); - - return UiElement(value, "ACC.", "m/s²", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getVEgoLead() { - QString value = lead_status ? QString::number((lead_v_rel + vEgo) * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-"; - QColor color = QColor(255, 255, 255, 255); - - if (lead_status) { - // Red if approaching faster than 10mph - // Orange if approaching (negative) - if (lead_v_rel < -4.4704) { - color = QColor(255, 0, 0, 255); - } else if (lead_v_rel < 0) { - color = QColor(255, 188, 0, 255); - } - } - - return UiElement(value, "L.S.", speedUnit, color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getFrictionCoefficientFiltered() { - QString value = QString::number(frictionCoefficientFiltered, 'f', 3); - QColor color = liveValid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); - - return UiElement(value, "FRIC.", "", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getLatAccelFactorFiltered() { - QString value = QString::number(latAccelFactorFiltered, 'f', 3); - QColor color = liveValid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); - - return UiElement(value, "L.A.", "m/s²", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getSteeringTorqueEps() { - QString value = QString::number(std::fabs(steeringTorqueEps), 'f', 1); - QColor color = QColor(255, 255, 255, 255); - - return UiElement(value, "E.T.", "N·dm", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getBearingDeg() { - QString value = (bearingAccuracyDeg != 180.00) ? QString("%1%2%3").arg(QString::number(bearingDeg, 'd', 0)).arg("°").arg("") : "-"; - QColor color = QColor(255, 255, 255, 255); - QString dir_value; - - if (bearingAccuracyDeg != 180.00) { - if (((bearingDeg >= 337.5) && (bearingDeg <= 360)) || ((bearingDeg >= 0) && (bearingDeg <= 22.5))) { - dir_value = "N"; - } else if ((bearingDeg > 22.5) && (bearingDeg < 67.5)) { - dir_value = "NE"; - } else if ((bearingDeg >= 67.5) && (bearingDeg <= 112.5)) { - dir_value = "E"; - } else if ((bearingDeg > 112.5) && (bearingDeg < 157.5)) { - dir_value = "SE"; - } else if ((bearingDeg >= 157.5) && (bearingDeg <= 202.5)) { - dir_value = "S"; - } else if ((bearingDeg > 202.5) && (bearingDeg < 247.5)) { - dir_value = "SW"; - } else if ((bearingDeg >= 247.5) && (bearingDeg <= 292.5)) { - dir_value = "W"; - } else if ((bearingDeg > 292.5) && (bearingDeg < 337.5)) { - dir_value = "NW"; - } - } else { - dir_value = "OFF"; - } - - return UiElement(QString("%1 | %2").arg(dir_value).arg(value), "B.D.", "", color); -} - -AnnotatedCameraWidget::UiElement AnnotatedCameraWidget::getAltitude() { - QString value = (gpsAccuracy != 0.00) ? QString::number(altitude, 'f', 1) : "-"; - QColor color = QColor(255, 255, 255, 255); - - return UiElement(value, "ALT.", "m", color); -} - -void AnnotatedCameraWidget::drawRightDevUi(QPainter &p, int x, int y) { - int rh = 5; - int ry = y; - - // Add Relative Distance to Primary Lead Car - // Unit: Meters - UiElement dRelElement = getDRel(); - rh += drawDevUiElementRight(p, x, ry, dRelElement.value, dRelElement.label, dRelElement.units, dRelElement.color); - ry = y + rh; - - // Add Relative Velocity vs Primary Lead Car - // Unit: kph if metric, else mph - UiElement vRelElement = getVRel(); - rh += drawDevUiElementRight(p, x, ry, vRelElement.value, vRelElement.label, vRelElement.units, vRelElement.color); - ry = y + rh; - - // Add Real Steering Angle - // Unit: Degrees - UiElement steeringAngleDegElement = getSteeringAngleDeg(); - rh += drawDevUiElementRight(p, x, ry, steeringAngleDegElement.value, steeringAngleDegElement.label, steeringAngleDegElement.units, steeringAngleDegElement.color); - ry = y + rh; - - if (lateralState == "torque") { - // Add Actual Lateral Acceleration (roll compensated) when using Torque - // Unit: m/s² - UiElement actualLateralAccelElement = getActualLateralAccel(); - rh += drawDevUiElementRight(p, x, ry, actualLateralAccelElement.value, actualLateralAccelElement.label, actualLateralAccelElement.units, actualLateralAccelElement.color); - } else { - // Add Desired Steering Angle when using PID - // Unit: Degrees - UiElement steeringAngleDesiredDegElement = getSteeringAngleDesiredDeg(); - rh += drawDevUiElementRight(p, x, ry, steeringAngleDesiredDegElement.value, steeringAngleDesiredDegElement.label, steeringAngleDesiredDegElement.units, steeringAngleDesiredDegElement.color); - } - ry = y + rh; - - // Add Device Memory (RAM) Usage - // Unit: Percent - UiElement memoryUsagePercentElement = getMemoryUsagePercent(); - rh += drawDevUiElementRight(p, x, ry, memoryUsagePercentElement.value, memoryUsagePercentElement.label, memoryUsagePercentElement.units, memoryUsagePercentElement.color); - ry = y + rh; - - rh += 25; - p.setBrush(QColor(0, 0, 0, 0)); - QRect ldu(x, y, 184, rh); -} - -int AnnotatedCameraWidget::drawNewDevUiElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) { - p.setFont(InterFont(38, QFont::Bold)); - drawCenteredLeftText(p, x, y, label, whiteColor(), value, units, color); - - return 430; -} - -void AnnotatedCameraWidget::drawNewDevUi2(QPainter &p, int x, int y) { - int rw = 90; - - // Add Acceleration from Car - // Unit: Meters per Second Squared - UiElement aEgoElement = getAEgo(); - rw += drawNewDevUiElement(p, rw, y, aEgoElement.value, aEgoElement.label, aEgoElement.units, aEgoElement.color); - - // Add Velocity of Primary Lead Car - // Unit: kph if metric, else mph - UiElement vEgoLeadElement = getVEgoLead(); - rw += drawNewDevUiElement(p, rw, y, vEgoLeadElement.value, vEgoLeadElement.label, vEgoLeadElement.units, vEgoLeadElement.color); - - if (torquedUseParams) { - // Add Friction Coefficient Raw from torqued - // Unit: None - UiElement frictionCoefficientFilteredElement = getFrictionCoefficientFiltered(); - rw += drawNewDevUiElement(p, rw, y, frictionCoefficientFilteredElement.value, frictionCoefficientFilteredElement.label, frictionCoefficientFilteredElement.units, frictionCoefficientFilteredElement.color); - - // Add Lateral Acceleration Factor Raw from torqued - // Unit: m/s² - UiElement latAccelFactorFilteredElement = getLatAccelFactorFiltered(); - rw += drawNewDevUiElement(p, rw, y, latAccelFactorFilteredElement.value, latAccelFactorFilteredElement.label, latAccelFactorFilteredElement.units, latAccelFactorFilteredElement.color); - } else { - // Add Steering Torque from Car EPS - // Unit: Newton Meters - UiElement steeringTorqueEpsElement = getSteeringTorqueEps(); - rw += drawNewDevUiElement(p, rw, y, steeringTorqueEpsElement.value, steeringTorqueEpsElement.label, steeringTorqueEpsElement.units, steeringTorqueEpsElement.color); - - // Add Bearing Degree and Direction from Car (Compass) - // Unit: Meters - UiElement bearingDegElement = getBearingDeg(); - rw += drawNewDevUiElement(p, rw, y, bearingDegElement.value, bearingDegElement.label, bearingDegElement.units, bearingDegElement.color); - } - - // Add Altitude of Current Location - // Unit: Meters - UiElement altitudeElement = getAltitude(); - rw += drawNewDevUiElement(p, rw, y, altitudeElement.value, altitudeElement.label, altitudeElement.units, altitudeElement.color); -} - -// ############################## DEV UI END ############################## - -void AnnotatedCameraWidget::drawE2eStatus(QPainter &p, int x, int y, int w, int h, int e2e_long_status) { - QColor status_color; - QRect e2eStatusIcon(x, y, w, h); - p.setPen(Qt::NoPen); - p.setBrush(QBrush(blackColor(70))); - p.drawEllipse(e2eStatusIcon); - e2eStatusIcon -= QMargins(25, 25, 25, 25); - p.setPen(Qt::NoPen); - if (e2e_long_status == 2) { - status_color = QColor::fromRgbF(0.0, 1.0, 0.0, 0.9); - } else if (e2e_long_status == 1) { - status_color = QColor::fromRgbF(1.0, 0.0, 0.0, 0.9); - } - p.setBrush(QBrush(status_color)); - p.drawEllipse(e2eStatusIcon); -} - -void AnnotatedCameraWidget::drawLeftTurnSignal(QPainter &painter, int x, int y, int circle_size, int state) { - painter.setRenderHint(QPainter::Antialiasing, true); - - QColor circle_color, circle_color_0, circle_color_1; - QColor arrow_color, arrow_color_0, arrow_color_1; - if ((left_blindspot || lane_change_edge_block) && !(left_blinker && right_blinker)) { - circle_color_0 = QColor(164, 0, 1); - circle_color_1 = QColor(204, 0, 1); - arrow_color_0 = QColor(72, 1, 1); - arrow_color_1 = QColor(255, 255, 255); - } else { - circle_color_0 = QColor(22, 156, 69); - circle_color_1 = QColor(30, 215, 96); - arrow_color_0 = QColor(9, 56, 27); - arrow_color_1 = QColor(255, 255, 255); - } - - if (state == 1) { - circle_color = circle_color_1; - arrow_color = arrow_color_1; - } else if (state == 0) { - circle_color = circle_color_0; - arrow_color = arrow_color_0; - } - - // Draw the circle - int circleX = x; - int circleY = y; - painter.setPen(Qt::NoPen); - painter.setBrush(circle_color); - painter.drawEllipse(circleX, circleY, circle_size, circle_size); - - // Draw the arrow - int arrowSize = 50; - int arrowX = circleX + (circle_size - arrowSize) / 4; - int arrowY = circleY + (circle_size - arrowSize) / 2; - painter.setPen(Qt::NoPen); - painter.setBrush(arrow_color); - - // Draw the arrow shape - QPolygon arrowPolygon; - arrowPolygon << QPoint(arrowX + 10, arrowY + arrowSize / 2) - << QPoint(arrowX + arrowSize - 3, arrowY) - << QPoint(arrowX + arrowSize, arrowY) - << QPoint(arrowX + arrowSize, arrowY + arrowSize) - << QPoint(arrowX + arrowSize - 3, arrowY + arrowSize) - << QPoint(arrowX + 10, arrowY + arrowSize / 2); - painter.drawPolygon(arrowPolygon); - - // Draw the tail rectangle - int tailWidth = arrowSize / 2.25; - int tailHeight = arrowSize / 2; - QRect tailRect(arrowX + arrowSize - 3, arrowY + arrowSize / 4, tailWidth, tailHeight); - painter.fillRect(tailRect, arrow_color); -} - -void AnnotatedCameraWidget::drawRightTurnSignal(QPainter &painter, int x, int y, int circle_size, int state) { - painter.setRenderHint(QPainter::Antialiasing, true); - - QColor circle_color, circle_color_0, circle_color_1; - QColor arrow_color, arrow_color_0, arrow_color_1; - if ((right_blindspot || lane_change_edge_block) && !(left_blinker && right_blinker)) { - circle_color_0 = QColor(164, 0, 1); - circle_color_1 = QColor(204, 0, 1); - arrow_color_0 = QColor(72, 1, 1); - arrow_color_1 = QColor(255, 255, 255); - } else { - circle_color_0 = QColor(22, 156, 69); - circle_color_1 = QColor(30, 215, 96); - arrow_color_0 = QColor(9, 56, 27); - arrow_color_1 = QColor(255, 255, 255); - } - - if (state == 1) { - circle_color = circle_color_1; - arrow_color = arrow_color_1; - } else if (state == 0) { - circle_color = circle_color_0; - arrow_color = arrow_color_0; - } - - - // Draw the circle - int circleX = x; - int circleY = y; - painter.setPen(Qt::NoPen); - painter.setBrush(circle_color); - painter.drawEllipse(circleX, circleY, circle_size, circle_size); - - // Draw the arrow - int arrowSize = 50; - int arrowX = circleX + (circle_size - arrowSize) / 2 + (arrowSize / 2.5) - 3; - int arrowY = circleY + (circle_size - arrowSize) / 2; - painter.setPen(Qt::NoPen); - painter.setBrush(arrow_color); - - // Draw the arrow shape - QPolygon arrowPolygon; - arrowPolygon << QPoint(arrowX + arrowSize - 10, arrowY + arrowSize / 2) - << QPoint(arrowX + 3, arrowY) - << QPoint(arrowX, arrowY) - << QPoint(arrowX, arrowY + arrowSize) - << QPoint(arrowX + 3, arrowY + arrowSize) - << QPoint(arrowX + arrowSize - 10, arrowY + arrowSize / 2); - painter.drawPolygon(arrowPolygon); - - // Draw the tail rectangle - int tailWidth = arrowSize / 2.25; - int tailHeight = arrowSize / 2; - QRect tailRect(arrowX - tailWidth + 3, arrowY + arrowSize / 4, tailWidth, tailHeight); - painter.fillRect(tailRect, arrow_color); -} - -int AnnotatedCameraWidget::blinkerPulse(int frame) { - if (frame % UI_FREQ < (UI_FREQ / 2)) { - blinker_state = 1; - } else { - blinker_state = 0; - } - - return blinker_state; -} - -void AnnotatedCameraWidget::speedLimitSignPulse(int frame) { - if (frame % UI_FREQ < (UI_FREQ / 2.5)) { - slcShowSign = false; - } else { - slcShowSign = true; - } -} - -void AnnotatedCameraWidget::drawFeatureStatusText(QPainter &p, int x, int y) { - const FeatureStatusText feature_text; - const FeatureStatusColor feature_color; - const QColor text_color = whiteColor(); - const QColor shadow_color = blackColor(38); - const int text_height = 34; - const int drop_shadow_size = 2; - const int eclipse_x_offset = 25; - const int eclipse_y_offset = 20; - const int w = 16; - const int h = 16; - - const bool longitudinal = hasLongitudinalControl(car_params); - - p.setFont(InterFont(32, QFont::Bold)); - - // Define a function to draw a feature status button - auto drawFeatureStatusElement = [&](int value, const QStringList& text_list, const QStringList& color_list, bool condition, const QString& off_text, const QString& label) { - std::pair feature_status = getFeatureStatus(value, text_list, color_list, condition, off_text); - QRect btn(x - eclipse_x_offset, y - eclipse_y_offset, w, h); - QRect btn_shadow(x - eclipse_x_offset + drop_shadow_size, y - eclipse_y_offset + drop_shadow_size, w, h); - p.setPen(Qt::NoPen); - p.setBrush(shadow_color); - p.drawEllipse(btn_shadow); - p.setBrush(feature_status.second); - p.drawEllipse(btn); - QString status_text; - status_text.sprintf("%s: %s", label.toStdString().c_str(), (feature_status.first).toStdString().c_str()); - p.setPen(QPen(shadow_color, 2)); - p.drawText(x + drop_shadow_size, y + drop_shadow_size, status_text); - p.setPen(QPen(text_color, 2)); - p.drawText(x, y, status_text); - y += text_height; - }; - - // Driving Personality / Gap Adjust Cruise - if (longitudinal) { - drawFeatureStatusElement(longitudinalPersonality, feature_text.gac_list_text, feature_color.gac_list_color, longitudinal, "N/A", "GAP"); - } - - // Dynamic Lane Profile - if (drivingModelGen == cereal::ModelGeneration::ONE) { - drawFeatureStatusElement(dynamicLaneProfile, feature_text.dlp_list_text, feature_color.dlp_list_color, true, "OFF", "DLP"); - } - - // TODO: Add toggle variables to cereal, and parse from cereal - if (longitudinal) { - QColor dec_color((cruiseStateEnabled && dynamicExperimentalControlToggle) ? "#4bff66" : "#ffffff"); - QRect dec_btn(x - eclipse_x_offset, y - eclipse_y_offset, w, h); - QRect dec_btn_shadow(x - eclipse_x_offset + drop_shadow_size, y - eclipse_y_offset + drop_shadow_size, w, h); - p.setPen(Qt::NoPen); - p.setBrush(shadow_color); - p.drawEllipse(dec_btn_shadow); - p.setBrush(dec_color); - p.drawEllipse(dec_btn); - QString dec_status_text; - dec_status_text.sprintf("DEC: %s\n", dynamicExperimentalControlToggle ? (experimentalMode ? QString(mpcMode).toStdString().c_str() : QString("Inactive").toStdString().c_str()) : "OFF"); - p.setPen(QPen(shadow_color, 2)); - p.drawText(x + drop_shadow_size, y + drop_shadow_size, dec_status_text); - p.setPen(QPen(text_color, 2)); - p.drawText(x, y, dec_status_text); - y += text_height; - } - - // TODO: Add toggle variables to cereal, and parse from cereal - // Speed Limit Control - if (longitudinal || !car_params.getPcmCruiseSpeed()) { - drawFeatureStatusElement(int(slcState), feature_text.slc_list_text, feature_color.slc_list_color, speedLimitControlToggle, "OFF", "SLC"); - } -} - -void AnnotatedCameraWidget::speedLimitWarning(QPainter &p, QRect sign_rect, const int sign_margin) { - // PRE ACTIVE - if (slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::PRE_ACTIVE) { - int set_speed = std::nearbyint(setSpeed); - int speed_limit_offsetted = std::nearbyint(speedLimitSLC + speedLimitSLCOffset); - - // Calculate the vertical offset using a sinusoidal function for smooth bouncing - double bounce_frequency = 2.0 * M_PI / 20.0; // 20 frames for one full oscillation - int bounce_offset = 20 * sin(speed_limit_frame * bounce_frequency); // Adjust the amplitude (20 pixels) as needed - - if (set_speed < speed_limit_offsetted) { - QPoint iconPosition(sign_rect.right() + sign_margin * 3, sign_rect.center().y() - plus_arrow_up_img.height() / 2 + bounce_offset); - p.drawPixmap(iconPosition, plus_arrow_up_img); - } else if (set_speed > speed_limit_offsetted) { - QPoint iconPosition(sign_rect.right() + sign_margin * 3, sign_rect.center().y() - minus_arrow_down_img.height() / 2 - bounce_offset); - p.drawPixmap(iconPosition, minus_arrow_down_img); - } - - speed_limit_frame++; - speedLimitSignPulse(speed_limit_frame); - } - - // current speed over speed limit - else if (overSpeedLimit && speedLimitWarningFlash) { - speed_limit_frame++; - speedLimitSignPulse(speed_limit_frame); - } - - else { - speed_limit_frame = 0; - slcShowSign = true; - } -} - - void AnnotatedCameraWidget::initializeGL() { CameraWidget::initializeGL(); qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION)); @@ -1309,29 +155,12 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { const UIScene &scene = s->scene; SubMaster &sm = *(s->sm); - const auto car_state = sm["carState"].getCarState(); - - // Shane's colored lanelines + // lanelines for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) { - if (i == 1 || i == 2) { - // TODO: can we just use the projected vertices somehow? - const cereal::XYZTData::Reader &line = (*s->sm)["modelV2"].getModelV2().getLaneLines()[i]; - const float default_pos = 1.4; // when lane poly isn't available - const float lane_pos = line.getY().size() > 0 ? std::abs(line.getY()[5]) : default_pos; // get redder when line is closer to car - float hue = 332.5 * lane_pos - 332.5; // equivalent to {1.4, 1.0}: {133, 0} (green to red) - hue = std::fmin(133, fmax(0, hue)) / 360.; // clip and normalize - painter.setBrush(QColor::fromHslF(hue, 1.0, 0.50, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); - } else { - painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); - } + painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); painter.drawPolygon(scene.lane_line_vertices[i]); } - // TODO: Fix empty spaces when curiving back on itself - painter.setBrush(QColor::fromRgbF(1.0, 0.0, 0.0, 0.2)); - if (left_blindspot) painter.drawPolygon(scene.lane_barrier_vertices[0]); - if (right_blindspot) painter.drawPolygon(scene.lane_barrier_vertices[1]); - // road edges for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) { painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); @@ -1340,70 +169,41 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { // paint path QLinearGradient bg(0, height(), 0, 0); - if (madsEnabled || car_state.getCruiseState().getEnabled()) { - if (steerOverride && latActive) { - bg.setColorAt(0.0, QColor::fromHslF(20 / 360., 0.94, 0.51, 0.17)); - bg.setColorAt(0.5, QColor::fromHslF(20 / 360., 1.0, 0.68, 0.17)); - bg.setColorAt(1.0, QColor::fromHslF(20 / 360., 1.0, 0.68, 0.0)); - } else if (!(latActive || car_state.getCruiseState().getEnabled())) { - bg.setColorAt(0, whiteColor()); - bg.setColorAt(1, whiteColor(0)); - } else if (sm["controlsState"].getControlsState().getExperimentalMode()) { - // The first half of track_vertices are the points for the right side of the path - const auto &acceleration = sm["modelV2"].getModelV2().getAcceleration().getX(); - const int max_len = std::min(scene.track_vertices.length() / 2, acceleration.size()); + if (sm["controlsState"].getControlsState().getExperimentalMode()) { + // The first half of track_vertices are the points for the right side of the path + const auto &acceleration = sm["modelV2"].getModelV2().getAcceleration().getX(); + const int max_len = std::min(scene.track_vertices.length() / 2, acceleration.size()); - for (int i = 0; i < max_len; ++i) { - // Some points are out of frame - if (scene.track_vertices[i].y() < 0 || scene.track_vertices[i].y() > height()) continue; + for (int i = 0; i < max_len; ++i) { + // Some points are out of frame + if (scene.track_vertices[i].y() < 0 || scene.track_vertices[i].y() > height()) continue; - // Flip so 0 is bottom of frame - float lin_grad_point = (height() - scene.track_vertices[i].y()) / height(); + // Flip so 0 is bottom of frame + float lin_grad_point = (height() - scene.track_vertices[i].y()) / height(); - // speed up: 120, slow down: 0 - float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0); - // FIXME: painter.drawPolygon can be slow if hue is not rounded - path_hue = int(path_hue * 100 + 0.5) / 100; + // speed up: 120, slow down: 0 + float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0); + // FIXME: painter.drawPolygon can be slow if hue is not rounded + path_hue = int(path_hue * 100 + 0.5) / 100; - float saturation = fmin(fabs(acceleration[i] * 1.5), 1); - float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey - float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade - bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha)); + float saturation = fmin(fabs(acceleration[i] * 1.5), 1); + float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey + float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade + bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha)); - // Skip a point, unless next is last - i += (i + 2) < max_len ? 1 : 0; - } - - } else { - bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4)); - bg.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.35)); - bg.setColorAt(1.0, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.0)); + // Skip a point, unless next is last + i += (i + 2) < max_len ? 1 : 0; } + } else { - bg.setColorAt(0.0, whiteColor(102)); - bg.setColorAt(0.5, whiteColor(89)); - bg.setColorAt(1.0, whiteColor(0)); + bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4)); + bg.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.35)); + bg.setColorAt(1.0, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.0)); } painter.setBrush(bg); painter.drawPolygon(scene.track_vertices); - // create path combining track vertices and track edge vertices - QPainterPath path; - path.addPolygon(scene.track_vertices); - path.addPolygon(scene.track_edge_vertices); - - // paint path edges - QLinearGradient pe(0, height(), 0, height() / 4); - if (!scene.dynamic_lane_profile_status) { - pe.setColorAt(0.0, QColor::fromHslF(240 / 360., 0.94, 0.51, 1.0)); - pe.setColorAt(0.5, QColor::fromHslF(204 / 360., 1.0, 0.68, 0.5)); - pe.setColorAt(1.0, QColor::fromHslF(204 / 360., 1.0, 0.68, 0.0)); - - painter.setBrush(pe); - painter.drawPath(path); - } - painter.restore(); } @@ -1415,7 +215,7 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s) // base icon int offset = UI_BORDER_SIZE + btn_size / 2; int x = rightHandDM ? width() - offset : offset; - int y = height() - offset - rn_offset; + int y = height() - offset; float opacity = dmActive ? 0.65 : 0.2; drawIcon(painter, QPoint(x, y), dm_img, blackColor(70), opacity); @@ -1448,56 +248,13 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s) painter.restore(); } -void AnnotatedCameraWidget::rocketFuel(QPainter &p) { - - static const int ct_n = 1; - static float ct; - - int rect_w = rect().width(); - int rect_h = rect().height(); - - const int n = 15 + 1; //Add one off screen due to timing issues - static float t[n]; - int dim_n = (sin(ct / 5) + 1) * (n - 0.01); - t[dim_n] = 1.0; - t[(int)(ct/ct_n)] = 1.0; - - UIState *s = uiState(); - float vc_accel0 = (*s->sm)["carState"].getCarState().getAEgo(); - static float vc_accel; - vc_accel = vc_accel + (vc_accel0 - vc_accel) / 5; - float hha = 0; - if (vc_accel > 0) { - hha = 0.85 - 0.1 / vc_accel; // only extend up to 85% - p.setBrush(QColor(0, 245, 0, 200)); - } - if (vc_accel < 0) { - hha = 0.85 + 0.1 / vc_accel; // only extend up to 85% - p.setBrush(QColor(245, 0, 0, 200)); - } - if (hha < 0) { - hha = 0; - } - hha = hha * rect_h; - float wp = 28; - if (vc_accel > 0) { - QRect ra = QRect(rect_w - wp, rect_h / 2 - hha / 2, wp, hha / 2); - p.drawRect(ra); - } else { - QRect ra = QRect(rect_w - wp, rect_h / 2, wp, hha / 2); - p.drawRect(ra); - } -} - -void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, - int num, const cereal::CarState::Reader &car_data, int chevron_data) { +void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd) { painter.save(); const float speedBuff = 10.; const float leadBuff = 40.; const float d_rel = lead_data.getDRel(); const float v_rel = lead_data.getVRel(); - const float v_ego = car_data.getVEgo(); float fillAlpha = 0; if (d_rel < leadBuff) { @@ -1524,62 +281,15 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState painter.setBrush(redColor(fillAlpha)); painter.drawPolygon(chevron, std::size(chevron)); - if (num == 0) { // Display metrics to the 0th lead car - const int chevron_types = 3; - const int chevron_all = chevron_types + 1; // All metrics - QStringList chevron_text[chevron_types]; - int position; - float val; - if (chevron_data == 1 || chevron_data == chevron_all) { - position = 0; - val = std::max(0.0f, d_rel); - chevron_text[position].append(QString::number(val,'f', 0) + " " + "m"); - } - if (chevron_data == 2 || chevron_data == chevron_all) { - position = (chevron_data == 2) ? 0 : 1; - val = std::max(0.0f, (v_rel + v_ego) * (is_metric ? static_cast(MS_TO_KPH) : static_cast(MS_TO_MPH))); - chevron_text[position].append(QString::number(val,'f', 0) + " " + (is_metric ? "km/h" : "mph")); - } - if (chevron_data == 3 || chevron_data == chevron_all) { - position = (chevron_data == 3) ? 0 : 2; - val = (d_rel > 0 && v_ego > 0) ? std::max(0.0f, d_rel / v_ego) : 0.0f; - - QString ttc_str = (val > 0 && val < 200) ? QString::number(val, 'f', 1) + "s" : "---"; - chevron_text[position].append(ttc_str); - } - - float str_w = 200; // Width of the text box, might need adjustment - float str_h = 50; // Height of the text box, adjust as necessary - painter.setFont(InterFont(45, QFont::Bold)); - // Calculate the center of the chevron and adjust the text box position - float text_y = y + sz + 12; // Position the text at the bottom of the chevron - QRect textRect(x - str_w / 2, text_y, str_w, str_h); // Adjust the rectangle to center the text horizontally at the chevron's bottom - QPoint shadow_offset(2, 2); - for (int i = 0; i < chevron_types; ++i) { - if (!chevron_text[i].isEmpty()) { - painter.setPen(QColor(0x0, 0x0, 0x0, 200)); // Draw shadow - painter.drawText(textRect.translated(shadow_offset.x(), shadow_offset.y() + i * str_h), Qt::AlignBottom | Qt::AlignHCenter, chevron_text[i].at(0)); - painter.setPen(QColor(0xff, 0xff, 0xff)); // Draw text - painter.drawText(textRect.translated(0, i * str_h), Qt::AlignBottom | Qt::AlignHCenter, chevron_text[i].at(0)); - painter.setPen(Qt::NoPen); // Reset pen to default - } - } - } - painter.restore(); } void AnnotatedCameraWidget::paintGL() { -} - -void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { UIState *s = uiState(); SubMaster &sm = *(s->sm); const double start_draw_t = millis_since_boot(); const cereal::ModelDataV2::Reader &model = sm["modelV2"].getModelV2(); - QPainter painter(this); - // draw camera frame { std::lock_guard lk(frame_lock); @@ -1601,10 +311,9 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { bool has_wide_cam = available_streams.count(VISION_STREAM_WIDE_ROAD); if (has_wide_cam) { float v_ego = sm["carState"].getCarState().getVEgo(); - float steer_angle = sm["carState"].getCarState().getSteeringAngleDeg(); - if ((v_ego < 10) || available_streams.size() == 1 || (std::fabs(steer_angle) > 45)) { + if ((v_ego < 10) || available_streams.size() == 1) { wide_cam_requested = true; - } else if ((v_ego > 15) && (std::fabs(steer_angle) < 30)) { + } else if (v_ego > 15) { wide_cam_requested = false; } wide_cam_requested = wide_cam_requested && sm["controlsState"].getControlsState().getExperimentalMode(); @@ -1613,10 +322,6 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { } CameraWidget::setStreamType(wide_cam_requested ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD); - if (reversing && s->scene.reverse_dm_cam) { - CameraWidget::setStreamType(VISION_STREAM_DRIVER, s->scene.reverse_dm_cam); - } - s->scene.wide_cam = CameraWidget::getStreamType() == VISION_STREAM_WIDE_ROAD; if (s->scene.calibration_valid) { auto calib = s->scene.wide_cam ? s->scene.view_from_wide_calib : s->scene.view_from_calib; @@ -1624,12 +329,11 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { } else { CameraWidget::updateCalibration(DEFAULT_CALIBRATION); } - painter.beginNativePainting(); CameraWidget::setFrameId(model.getFrameId()); CameraWidget::paintGL(); - painter.endNativePainting(); } + QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); @@ -1639,18 +343,15 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { if (s->scene.longitudinal_control && sm.rcv_frame("radarState") > s->scene.started_frame) { auto radar_state = sm["radarState"].getRadarState(); - auto car_state = sm["carState"].getCarState(); update_leads(s, radar_state, model.getPosition()); auto lead_one = radar_state.getLeadOne(); auto lead_two = radar_state.getLeadTwo(); if (lead_one.getStatus()) { - drawLead(painter, lead_one, s->scene.lead_vertices[0], 0, car_state, s->scene.chevron_data); + drawLead(painter, lead_one, s->scene.lead_vertices[0]); } if (lead_two.getStatus() && (std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0)) { - drawLead(painter, lead_two, s->scene.lead_vertices[1], 1, car_state, s->scene.chevron_data); + drawLead(painter, lead_two, s->scene.lead_vertices[1]); } - - rocketFuel(painter); } } @@ -1662,21 +363,6 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { drawHud(painter); - if (left_blinker || right_blinker) { - blinker_frame++; - int state = blinkerPulse(blinker_frame); - int blinker_x = splitPanelVisible ? 150 : 180; - int blinker_y = splitPanelVisible ? 220 : 90; - if (left_blinker) { - drawLeftTurnSignal(painter, rect().center().x() - (blinker_x + blinker_size), blinker_y, blinker_size, state); - } - if (right_blinker) { - drawRightTurnSignal(painter, rect().center().x() + blinker_x, blinker_y, blinker_size, state); - } - } else { - blinker_frame = 0; - } - double cur_draw_t = millis_since_boot(); double dt = cur_draw_t - prev_draw_t; double fps = fps_filter.update(1. / dt * 1000); @@ -1690,11 +376,6 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { auto m = msg.initEvent().initUiDebug(); m.setDrawTimeMillis(cur_draw_t - start_draw_t); pm->send("uiDebug", msg); - - MessageBuilder e2e_long_msg; - auto e2eLongStatus = e2e_long_msg.initEvent().initE2eLongStateSP(); - e2eLongStatus.setStatus(e2eStatus); - e2e_state->send("e2eLongStateSP", e2e_long_msg); } void AnnotatedCameraWidget::showEvent(QShowEvent *event) { diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index 29b9c4833b..465ba23e04 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -2,14 +2,10 @@ #include #include -#include #include "selfdrive/ui/qt/onroad/buttons.h" #include "selfdrive/ui/qt/widgets/cameraview.h" -const int subsign_img_size = 35; -const int blinker_size = 120; - class AnnotatedCameraWidget : public CameraWidget { Q_OBJECT @@ -17,68 +13,8 @@ public: explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0); void updateState(const UIState &s); - MapSettingsButton *map_settings_btn; - OnroadSettingsButton *onroad_settings_btn; - private: void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255); - void drawCenteredText(QPainter &p, int x, int y, const QString &text, QColor color); - void drawVisionTurnControllerUI(QPainter &p, int x, int y, int size, const QColor &color, const QString &speed, - int alpha); - void drawCircle(QPainter &p, int x, int y, int r, QBrush bg); - void drawSpeedSign(QPainter &p, QRect rc, const QString &speed, const QString &sub_text, int subtext_size, - bool is_map_sourced, bool is_active); - void drawTrunSpeedSign(QPainter &p, QRect rc, const QString &speed, const QString &sub_text, int curv_sign, - bool is_active); - - void drawColoredText(QPainter &p, int x, int y, const QString &text, QColor color); - void drawStandstillTimer(QPainter &p, int x, int y); - - // ############################## DEV UI START ############################## - void drawRightDevUi(QPainter &p, int x, int y); - int drawDevUiElementRight(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color); - int drawNewDevUiElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color); - void drawNewDevUi2(QPainter &p, int x, int y); - void drawCenteredLeftText(QPainter &p, int x, int y, const QString &text1, QColor color1, const QString &text2, const QString &text3, QColor color2); - struct UiElement { - QString value; - QString label; - QString units; - QColor color; - - UiElement(const QString &value, const QString &label, const QString &units, const QColor &color = QColor(255, 255, 255, 255)) - : value(value), label(label), units(units), color(color) {} - }; - - UiElement getDRel(); - UiElement getVRel(); - UiElement getSteeringAngleDeg(); - UiElement getActualLateralAccel(); - UiElement getSteeringAngleDesiredDeg(); - UiElement getMemoryUsagePercent(); - - UiElement getAEgo(); - UiElement getVEgoLead(); - UiElement getFrictionCoefficientFiltered(); - UiElement getLatAccelFactorFiltered(); - UiElement getSteeringTorqueEps(); - UiElement getBearingDeg(); - UiElement getAltitude(); - - // ############################## DEV UI END ############################## - - void drawE2eStatus(QPainter &p, int x, int y, int w, int h, int e2e_long_status); - - void drawLeftTurnSignal(QPainter &painter, int x, int y, int circle_size, int state); - void drawRightTurnSignal(QPainter &painter, int x, int y, int circle_size, int state); - int blinkerPulse(int frame); - void updateButtonsLayout(bool is_rhd); - - void drawFeatureStatusText(QPainter &p, int x, int y); - void speedLimitSignPulse(int frame); - void speedLimitWarning(QPainter &p, QRect sign_rect, const int sign_margin); - void mousePressEvent(QMouseEvent* e) override; - void drawRoadNameText(QPainter &p, int x, int y, const QString &text, QColor color); QVBoxLayout *main_layout; ExperimentalButton *experimental_btn; @@ -86,15 +22,12 @@ private: float speed; QString speedUnit; float setSpeed; - float speedLimit; bool is_cruise_set = false; bool is_metric = false; bool dmActive = false; bool hideBottomIcons = false; bool rightHandDM = false; float dm_fade_state = 1.0; - bool has_us_speed_limit = false; - bool has_eu_speed_limit = false; bool v_ego_cluster_seen = false; int status = STATUS_DISENGAGED; std::unique_ptr pm; @@ -102,126 +35,19 @@ private: int skip_frame_count = 0; bool wide_cam_requested = false; - Params params; - QHBoxLayout *buttons_layout; - QPixmap map_img; - QPixmap left_img; - QPixmap right_img; - bool left_blindspot = false; - bool right_blindspot = false; - std::unique_ptr e2e_state; - - bool steerOverride = false; - bool gasOverride = false; - bool latActive = false; - bool madsEnabled = false; - - bool brakeLights; - - bool standStillTimer; - bool standStill; - float standstillElapsedTime; - - bool showVTC = false; - QString vtcSpeed; - QColor vtcColor; - - bool showDebugUI = false; - - QString roadName = ""; - - bool showSpeedLimit = false; - float speedLimitSLC; - float speedLimitSLCOffset; - float speedLimitWarningOffset; - QString slcSubText; - float slcSubTextSize = 0.0; - bool overSpeedLimit; - bool mapSourcedSpeedLimit = false; - bool slcActive = false; - - bool showTurnSpeedLimit = false; - QString turnSpeedLimit; - QString tscSubText; - bool tscActive = false; - int curveSign = 0; - - bool hideVEgoUi; - - bool splitPanelVisible; - - // ############################## DEV UI START ############################## - bool lead_status; - float lead_d_rel = 0; - float lead_v_rel = 0; - QString lateralState; - float angleSteers = 0; - float steerAngleDesired = 0; - float curvature; - float roll; - int memoryUsagePercent; - int devUiInfo; - float gpsAccuracy; - float altitude; - float vEgo; - float aEgo; - float steeringTorqueEps; - float bearingAccuracyDeg; - float bearingDeg; - bool torquedUseParams; - float latAccelFactorFiltered; - float frictionCoefficientFiltered; - bool liveValid; - // ############################## DEV UI END ############################## - - float btnPerc; - - bool reversing; - - int e2eState; - int e2eStatus; - - bool left_blinker, right_blinker, lane_change_edge_block; - int blinker_frame; - int blinker_state = 0; - - cereal::LongitudinalPlanSP::SpeedLimitControlState slcState; - int longitudinalPersonality; - int dynamicLaneProfile; - QString mpcMode; - - int speed_limit_frame; - bool slcShowSign = true; - QPixmap plus_arrow_up_img; - QPixmap minus_arrow_down_img; - QRect sl_sign_rect; - int rn_offset = 0; - bool e2eLongAlertUi, dynamicExperimentalControlToggle, speedLimitControlToggle, speedLimitWarningFlash; - cereal::CarParams::Reader car_params; - bool cruiseStateEnabled; - bool experimentalMode; - - bool featureStatusToggle; - - cereal::ModelGeneration drivingModelGen; - protected: void paintGL() override; void initializeGL() override; void showEvent(QShowEvent *event) override; void updateFrameMat() override; void drawLaneLines(QPainter &painter, const UIState *s); - void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, - int num, const cereal::CarState::Reader &car_data, int chevron_data); + void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd); void drawHud(QPainter &p); void drawDriverState(QPainter &painter, const UIState *s); inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); } inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); } inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); } - void paintEvent(QPaintEvent *event) override; - void rocketFuel(QPainter &p); - double prev_draw_t = 0; FirstOrderFilter fps_filter; }; diff --git a/selfdrive/ui/qt/onroad/buttons.cc b/selfdrive/ui/qt/onroad/buttons.cc index fbca42da7b..92bcea11b5 100644 --- a/selfdrive/ui/qt/onroad/buttons.cc +++ b/selfdrive/ui/qt/onroad/buttons.cc @@ -15,18 +15,6 @@ void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrus p.setOpacity(1.0); } -static void drawCustomButtonIcon(QPainter &p, const int btn_size_x, const int btn_size_y, const QPixmap &img, const QBrush &bg, float opacity) { - QPoint center(btn_size_x / 2, btn_size_y / 2); - p.setRenderHint(QPainter::Antialiasing); - p.setOpacity(1.0); // bg dictates opacity of ellipse - p.setPen(Qt::NoPen); - p.setBrush(bg); - p.drawEllipse(center, btn_size_x / 2, btn_size_y / 2); - p.setOpacity(opacity); - p.drawPixmap(center - QPoint(img.width() / 2, img.height() / 2), img); - p.setOpacity(1.0); -} - // ExperimentalButton ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) { setFixedSize(btn_size, btn_size); @@ -46,8 +34,7 @@ void ExperimentalButton::changeMode() { void ExperimentalButton::updateState(const UIState &s) { const auto cs = (*s.sm)["controlsState"].getControlsState(); - const auto lp_sp = (*s.sm)["longitudinalPlanSP"].getLongitudinalPlanSP(); - bool eng = (cs.getEngageable() || cs.getEnabled()) && !(lp_sp.getVisionTurnControllerState() > cereal::LongitudinalPlanSP::VisionTurnControllerState::DISABLED); + bool eng = cs.getEngageable() || cs.getEnabled(); if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) { engageable = eng; experimental_mode = cs.getExperimentalMode(); @@ -60,48 +47,3 @@ void ExperimentalButton::paintEvent(QPaintEvent *event) { QPixmap img = experimental_mode ? experimental_img : engage_img; drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, QColor(0, 0, 0, 166), (isDown() || !engageable) ? 0.6 : 1.0); } - - -// MapSettingsButton -MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) { - // btn_size: 192 * 80% ~= 152 - // img_size: (152 / 4) * 3 = 114 - setFixedSize(152, 152); - settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {114, 114}); - - // hidden by default, made visible if map is created (has prime or mapbox token) - setVisible(false); - setEnabled(false); -} - -void MapSettingsButton::paintEvent(QPaintEvent *event) { - QPainter p(this); - drawCustomButtonIcon(p, 152, 152, settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0); -} - - -// OnroadSettingsButton -OnroadSettingsButton::OnroadSettingsButton(QWidget *parent) : QPushButton(parent) { - // btn_size: 192 * 80% ~= 152 - // img_size: (152 / 4) * 3 = 114 - setFixedSize(152, 152); - settings_img = loadPixmap("../assets/navigation/icon_settings.svg", {114, 114}); - - // hidden by default, made visible if Driving Personality / GAC, DLP, DEC, or SLC is enabled - setVisible(false); - setEnabled(false); -} - -void OnroadSettingsButton::paintEvent(QPaintEvent *event) { - QPainter p(this); - drawCustomButtonIcon(p, 152, 152, settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0); -} - -void OnroadSettingsButton::updateState(const UIState &s) { - const auto cp = (*s.sm)["carParams"].getCarParams(); - auto dlp_enabled = s.scene.driving_model_generation == cereal::ModelGeneration::ONE; - bool allow_btn = s.scene.onroad_settings_toggle && (dlp_enabled || hasLongitudinalControl(cp) || !cp.getPcmCruiseSpeed()); - - setVisible(allow_btn); - setEnabled(allow_btn); -} diff --git a/selfdrive/ui/qt/onroad/buttons.h b/selfdrive/ui/qt/onroad/buttons.h index feee1ba639..e999480d5c 100644 --- a/selfdrive/ui/qt/onroad/buttons.h +++ b/selfdrive/ui/qt/onroad/buttons.h @@ -2,7 +2,11 @@ #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif const int btn_size = 192; const int img_size = (btn_size / 4) * 3; @@ -14,6 +18,10 @@ public: explicit ExperimentalButton(QWidget *parent = 0); void updateState(const UIState &s); +protected: + bool experimental_mode; + bool engageable; + private: void paintEvent(QPaintEvent *event) override; void changeMode(); @@ -21,35 +29,6 @@ private: Params params; QPixmap engage_img; QPixmap experimental_img; - bool experimental_mode; - bool engageable; -}; - - -class MapSettingsButton : public QPushButton { - Q_OBJECT - -public: - explicit MapSettingsButton(QWidget *parent = 0); - -private: - void paintEvent(QPaintEvent *event) override; - - QPixmap settings_img; -}; - - -class OnroadSettingsButton : public QPushButton { - Q_OBJECT - -public: - explicit OnroadSettingsButton(QWidget *parent = 0); - void updateState(const UIState &s); - -private: - void paintEvent(QPaintEvent *event) override; - - QPixmap settings_img; }; void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity); diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 7413cdff7e..e93ded6cc2 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -3,12 +3,6 @@ #include #include -#ifdef ENABLE_MAPS -#include "selfdrive/ui/qt/maps/map_helpers.h" -#include "selfdrive/ui/qt/maps/map_panel.h" -#endif -#include "selfdrive/ui/qt/onroad_settings_panel.h" - #include "selfdrive/ui/qt/util.h" OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { @@ -31,11 +25,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { split->insertWidget(0, arCam); } - if (getenv("MAP_RENDER_VIEW")) { - CameraWidget *map_render = new CameraWidget("navd", VISION_STREAM_MAP, false, this); - split->insertWidget(0, map_render); - } - stacked_layout->addWidget(split_wrapper); alerts = new OnroadAlerts(this); @@ -46,9 +35,12 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { alerts->raise(); setAttribute(Qt::WA_OpaquePaintEvent); + + // We handle the connection of the signals on the derived class +#ifndef SUNNYPILOT QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState); QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition); - QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged); +#endif } void OnroadWindow::updateState(const UIState &s) { @@ -56,12 +48,6 @@ void OnroadWindow::updateState(const UIState &s) { return; } - if (s.scene.map_on_left || s.scene.mapbox_fullscreen) { - split->setDirection(QBoxLayout::LeftToRight); - } else { - split->setDirection(QBoxLayout::RightToLeft); - } - alerts->updateState(s); nvg->updateState(s); @@ -73,99 +59,10 @@ void OnroadWindow::updateState(const UIState &s) { } } - -void OnroadWindow::mousePressEvent(QMouseEvent* e) { -#ifdef ENABLE_MAPS - UIState *s = uiState(); - UIScene &scene = s->scene; - if (map != nullptr && !isOnroadSettingsVisible()) { - if (wakeScreenTimeout()) { - // Switch between map and sidebar when using navigate on openpilot - bool sidebarVisible = geometry().x() > 0; - bool show_map = uiState()->scene.navigate_on_openpilot_deprecated ? sidebarVisible : !sidebarVisible; - updateMapSize(scene); - map->setVisible(show_map && !map->isVisible()); - } - } -#endif - if (onroad_settings != nullptr && !isMapVisible()) { - if (wakeScreenTimeout()) { - onroad_settings->setVisible(false); - } - } - // propagation event to parent(HomeWindow) - QWidget::mousePressEvent(e); -} - -void OnroadWindow::createMapWidget() { -#ifdef ENABLE_MAPS - auto m = new MapPanel(get_mapbox_settings()); - map = m; - - QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::mapPanelRequested); - QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::onroadSettingsPanelNotRequested); - QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings); - nvg->map_settings_btn->setEnabled(true); - - m->setFixedWidth(uiState()->scene.mapbox_fullscreen ? topWidget(this)->width() : - topWidget(this)->width() / 2 - UI_BORDER_SIZE); - split->insertWidget(0, m); - - // hidden by default, made visible when navRoute is published - m->setVisible(false); -#endif -} - -void OnroadWindow::createOnroadSettingsWidget() { - auto os = new OnroadSettingsPanel(this); - onroad_settings = os; - - QObject::connect(os, &OnroadSettingsPanel::onroadSettingsPanelRequested, this, &OnroadWindow::onroadSettingsPanelRequested); - QObject::connect(os, &OnroadSettingsPanel::onroadSettingsPanelRequested, this, &OnroadWindow::mapPanelNotRequested); - QObject::connect(nvg->onroad_settings_btn, &OnroadSettingsButton::clicked, os, &OnroadSettingsPanel::toggleOnroadSettings); - nvg->onroad_settings_btn->setEnabled(true); - - os->setFixedWidth(topWidget(this)->width() / 2.6 - UI_BORDER_SIZE); - split->insertWidget(0, os); - - // hidden by default - os->setVisible(false); -} - void OnroadWindow::offroadTransition(bool offroad) { - if (!offroad) { -#ifdef ENABLE_MAPS - if (map == nullptr && (uiState()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) { - createMapWidget(); - } -#endif - if (onroad_settings == nullptr) { - createOnroadSettingsWidget(); - } - } - alerts->clear(); } -void OnroadWindow::updateMapSize(const UIScene &scene) { - map->setFixedWidth(scene.mapbox_fullscreen ? topWidget(this)->width() : - topWidget(this)->width() / 2 - UI_BORDER_SIZE); - split->insertWidget(0, map); -} - -void OnroadWindow::primeChanged(bool prime) { -#ifdef ENABLE_MAPS - if (map && (!prime && MAPBOX_TOKEN.isEmpty())) { - nvg->map_settings_btn->setEnabled(false); - nvg->map_settings_btn->setVisible(false); - map->deleteLater(); - map = nullptr; - } else if (!map && (prime || !MAPBOX_TOKEN.isEmpty())) { - createMapWidget(); - } -#endif -} - void OnroadWindow::paintEvent(QPaintEvent *event) { QPainter p(this); p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255)); diff --git a/selfdrive/ui/qt/onroad/onroad_home.h b/selfdrive/ui/qt/onroad/onroad_home.h index 1beadcd13b..4ac37678fa 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.h +++ b/selfdrive/ui/qt/onroad/onroad_home.h @@ -1,51 +1,29 @@ #pragma once -#include "common/params.h" #include "selfdrive/ui/qt/onroad/alerts.h" + +#if SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h" +#define AnnotatedCameraWidget AnnotatedCameraWidgetSP +#define UIState UIStateSP +#else #include "selfdrive/ui/qt/onroad/annotated_camera.h" +#endif class OnroadWindow : public QWidget { Q_OBJECT public: OnroadWindow(QWidget* parent = 0); - bool isMapVisible() const { return map && map->isVisible(); } - void showMapPanel(bool show) { if (map) map->setVisible(show); } - bool isOnroadSettingsVisible() const { return onroad_settings && onroad_settings->isVisible(); } - bool isMapAvailable() const { return map; } - void mapPanelNotRequested() { if (map) map->setVisible(false); } - void onroadSettingsPanelNotRequested() { if (onroad_settings) onroad_settings->setVisible(false); } - - bool wakeScreenTimeout() { - if ((uiState()->scene.sleep_btn != 0 && uiState()->scene.sleep_btn_opacity != 0) || - (uiState()->scene.sleep_time != 0 && uiState()->scene.onroadScreenOff != -2)) { - return true; - } - return false; - } - -signals: - void mapPanelRequested(); - void onroadSettingsPanelRequested(); - -private: - void createMapWidget(); - void createOnroadSettingsWidget(); - void paintEvent(QPaintEvent *event); - void mousePressEvent(QMouseEvent* e) override; +protected: + void paintEvent(QPaintEvent *event) override; OnroadAlerts *alerts; AnnotatedCameraWidget *nvg; QColor bg = bg_colors[STATUS_DISENGAGED]; - QWidget *map = nullptr; - QWidget *onroad_settings = nullptr; QHBoxLayout* split; - Params params; - -private slots: - void offroadTransition(bool offroad); - void primeChanged(bool prime); - void updateState(const UIState &s); - void updateMapSize(const UIScene &scene); +protected slots: + virtual void offroadTransition(bool offroad); + virtual void updateState(const UIState &s); }; diff --git a/selfdrive/ui/qt/onroad_settings_panel.cc b/selfdrive/ui/qt/onroad_settings_panel.cc deleted file mode 100644 index 163f0fb31f..0000000000 --- a/selfdrive/ui/qt/onroad_settings_panel.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include "selfdrive/ui/qt/onroad_settings_panel.h" - -#include -#include - -#include "selfdrive/ui/qt/onroad_settings.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/ui.h" - -OnroadSettingsPanel::OnroadSettingsPanel(QWidget *parent) : QFrame(parent) { - content_stack = new QStackedLayout(this); - content_stack->setContentsMargins(0, 0, 0, 0); - - auto onroad_settings = new OnroadSettings(true, parent); - QObject::connect(onroad_settings, &OnroadSettings::closeSettings, this, &OnroadSettings::hide); - content_stack->addWidget(onroad_settings); -} - -void OnroadSettingsPanel::toggleOnroadSettings() { - if (isVisible()) { - hide(); - } else { - emit onroadSettingsPanelRequested(); - show(); - } -} diff --git a/selfdrive/ui/qt/onroad_settings_panel.h b/selfdrive/ui/qt/onroad_settings_panel.h deleted file mode 100644 index 8b81aef25a..0000000000 --- a/selfdrive/ui/qt/onroad_settings_panel.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#ifdef ENABLE_MAPS -#include -#endif -#include - -class OnroadSettingsPanel : public QFrame { - Q_OBJECT - -public: - explicit OnroadSettingsPanel(QWidget *parent = nullptr); - -signals: - void onroadSettingsPanelRequested(); - -public slots: - void toggleOnroadSettings(); - -private: - QStackedLayout *content_stack; -}; diff --git a/selfdrive/ui/qt/request_repeater.cc b/selfdrive/ui/qt/request_repeater.cc index 8212d3db94..d215fe42ca 100644 --- a/selfdrive/ui/qt/request_repeater.cc +++ b/selfdrive/ui/qt/request_repeater.cc @@ -1,36 +1,36 @@ #include "selfdrive/ui/qt/request_repeater.h" -#include "common/swaglog.h" - RequestRepeater::RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey, - int period, bool whileOnroad, bool sunnylink) : HttpRequest(parent, true, 20000, sunnylink) { - request_url = requestURL; - while_onroad = whileOnroad; + int period, bool while_onroad) : HttpRequest(parent) { timer = new QTimer(this); timer->setTimerType(Qt::VeryCoarseTimer); - connect(timer, &QTimer::timeout, [=]() { this->timerTick(); }); + + connectTimer(requestURL, while_onroad); + timer->start(period * 1000); + setupCacheProcess(cacheKey); +} + +void RequestRepeater::connectTimer(const QString &requestURL, bool while_onroad) { + QObject::connect(timer, &QTimer::timeout, [=]() { + if ((!uiState()->scene.started || while_onroad) && device()->isAwake() && !active()) { + sendRequest(requestURL); + } + }); +} + +void RequestRepeater::setupCacheProcess(const QString &cacheKey) { if (!cacheKey.isEmpty()) { prevResp = QString::fromStdString(params.get(cacheKey.toStdString())); if (!prevResp.isEmpty()) { QTimer::singleShot(500, [=]() { emit requestDone(prevResp, true, QNetworkReply::NoError); }); } - connect(this, &HttpRequest::requestDone, [=](const QString &resp, bool success) { + QObject::connect(this, &HttpRequest::requestDone, [=](const QString &resp, bool success) { if (success && resp != prevResp) { params.put(cacheKey.toStdString(), resp.toStdString()); prevResp = resp; } }); } - - // Don't wait for the timer to fire to send the first request - ForceUpdate(); -} - -void RequestRepeater::timerTick() { - if ((!uiState()->scene.started || while_onroad) && device()->isAwake() && !active()) { - LOGD("Sending request for %s", qPrintable(request_url)); - sendRequest(request_url); - } } diff --git a/selfdrive/ui/qt/request_repeater.h b/selfdrive/ui/qt/request_repeater.h index 45cb643b13..32e714b1cb 100644 --- a/selfdrive/ui/qt/request_repeater.h +++ b/selfdrive/ui/qt/request_repeater.h @@ -1,24 +1,23 @@ #pragma once -#include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/ui/qt/api.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/api.h" +#else #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/api.h" +#endif class RequestRepeater : public HttpRequest { +public: + void connectTimer(const QString& requestURL, bool while_onroad); + void setupCacheProcess(const QString& cacheKey); + RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey = "", int period = 0, bool while_onroad=false); private: Params params; QTimer *timer; QString prevResp; - QString request_url; - bool while_onroad; - void timerTick(); - -public: - RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey = "", int period = 0, bool whileOnroad=false, bool sunnylink = false); - void ForceUpdate() { - LOGD("Forcing update for %s", qPrintable(request_url)); - timerTick(); - } }; diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 8d45e62ce4..615276be00 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -1,11 +1,8 @@ #include "selfdrive/ui/qt/sidebar.h" -#include - #include #include "selfdrive/ui/qt/util.h" -#include "common/params.h" void Sidebar::drawMetric(QPainter &p, const QPair &label, QColor c, int y) { const QRect rect = {30, y, 240, 126}; @@ -93,58 +90,16 @@ void Sidebar::updateState(const UIState &s) { } setProperty("connectStatus", QVariant::fromValue(connectStatus)); - if (sm.frame % UI_FREQ == 0) { // Update every 1 Hz - switch (s.scene.sidebar_temp_options) { - case 0: - break; - case 1: - sidebar_temp = QString::number((int)deviceState.getMemoryTempC()); - break; - case 2: { - const auto& cpu_temp_list = deviceState.getCpuTempC(); - float max_cpu_temp = std::numeric_limits::lowest(); - - for (const float& temp : cpu_temp_list) { - max_cpu_temp = std::max(max_cpu_temp, temp); - } - - if (max_cpu_temp >= 0) { - sidebar_temp = QString::number(std::nearbyint(max_cpu_temp)); - } - break; - } - case 3: { - const auto& gpu_temp_list = deviceState.getGpuTempC(); - float max_gpu_temp = std::numeric_limits::lowest(); - - for (const float& temp : gpu_temp_list) { - max_gpu_temp = std::max(max_gpu_temp, temp); - } - - if (max_gpu_temp >= 0) { - sidebar_temp = QString::number(std::nearbyint(max_gpu_temp)); - } - break; - } - case 4: - sidebar_temp = QString::number((int)deviceState.getMaxTempC()); - break; - default: - break; - } - - setProperty("sidebarTemp", sidebar_temp + "°C"); - } - - bool show_sidebar_temp = s.scene.sidebar_temp_options != 0; - ItemStatus tempStatus = {{tr("TEMP"), show_sidebar_temp ? sidebar_temp_str : tr("HIGH")}, danger_color}; +#ifndef SUNNYPILOT + ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color}; auto ts = deviceState.getThermalStatus(); if (ts == cereal::DeviceState::ThermalStatus::GREEN) { - tempStatus = {{tr("TEMP"), show_sidebar_temp ? sidebar_temp_str : tr("GOOD")}, good_color}; + tempStatus = {{tr("TEMP"), tr("GOOD")}, good_color}; } else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) { - tempStatus = {{tr("TEMP"), show_sidebar_temp ? sidebar_temp_str : tr("OK")}, warning_color}; + tempStatus = {{tr("TEMP"), tr("OK")}, warning_color}; } setProperty("tempStatus", QVariant::fromValue(tempStatus)); +#endif ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, good_color}; if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) { @@ -153,32 +108,14 @@ void Sidebar::updateState(const UIState &s) { pandaStatus = {{tr("GPS"), tr("SEARCH")}, warning_color}; } setProperty("pandaStatus", QVariant::fromValue(pandaStatus)); - - ItemStatus sunnylinkStatus; - auto sl_dongle_id = getSunnylinkDongleId(); - auto last_sunnylink_ping_str = params.get("LastSunnylinkPingTime"); - auto last_sunnylink_ping = std::stoull(last_sunnylink_ping_str.empty() ? "0" : last_sunnylink_ping_str); - auto elapsed_sunnylink_ping = nanos_since_boot() - last_sunnylink_ping; - auto sunnylink_enabled = params.getBool("SunnylinkEnabled"); - - QString status = tr("DISABLED"); - QColor color = disabled_color; - - if (sunnylink_enabled && last_sunnylink_ping == 0) { - // If sunnylink is enabled, but we don't have a dongle id, and we haven't received a ping yet, we are registering - status = sl_dongle_id.has_value() ? tr("OFFLINE") : tr("REGIST..."); - color = sl_dongle_id.has_value() ? warning_color : progress_color; - } else if (sunnylink_enabled) { - // If sunnylink is enabled, we are considered online if we have received a ping in the last 80 seconds, else error. - status = elapsed_sunnylink_ping < 80000000000ULL ? tr("ONLINE") : tr("ERROR"); - color = elapsed_sunnylink_ping < 80000000000ULL ? good_color : danger_color; - } - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), status}, color }; - setProperty("sunnylinkStatus", QVariant::fromValue(sunnylinkStatus)); } void Sidebar::paintEvent(QPaintEvent *event) { QPainter p(this); + DrawSidebar(p); // Because derived classes implement this. Otherwise QPainter gets terminated before time. +} + +void Sidebar::DrawSidebar(QPainter &p){ p.setPen(Qt::NoPen); p.setRenderHint(QPainter::Antialiasing); @@ -204,10 +141,10 @@ void Sidebar::paintEvent(QPaintEvent *event) { p.setPen(QColor(0xff, 0xff, 0xff)); const QRect r = QRect(50, 247, 100, 50); p.drawText(r, Qt::AlignCenter, net_type); + RETURN_IF_SUNNYPILOT // Because we draw ourselves // metrics - drawMetric(p, temp_status.first, temp_status.second, 310); - drawMetric(p, panda_status.first, panda_status.second, 440); - drawMetric(p, connect_status.first, connect_status.second, 570); - drawMetric(p, sunnylink_status.first, sunnylink_status.second, 700); + drawMetric(p, temp_status.first, temp_status.second, 338); + drawMetric(p, panda_status.first, panda_status.second, 496); + drawMetric(p, connect_status.first, connect_status.second, 654); } diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h index 9012f25135..62a5486809 100644 --- a/selfdrive/ui/qt/sidebar.h +++ b/selfdrive/ui/qt/sidebar.h @@ -4,8 +4,13 @@ #include #include +#include "cereal/messaging/messaging.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif typedef QPair, QColor> ItemStatus; Q_DECLARE_METATYPE(ItemStatus); @@ -13,12 +18,10 @@ Q_DECLARE_METATYPE(ItemStatus); class Sidebar : public QFrame { Q_OBJECT Q_PROPERTY(ItemStatus connectStatus MEMBER connect_status NOTIFY valueChanged); - Q_PROPERTY(ItemStatus sunnylinkStatus MEMBER sunnylink_status NOTIFY valueChanged); Q_PROPERTY(ItemStatus pandaStatus MEMBER panda_status NOTIFY valueChanged); Q_PROPERTY(ItemStatus tempStatus MEMBER temp_status NOTIFY valueChanged); Q_PROPERTY(QString netType MEMBER net_type NOTIFY valueChanged); Q_PROPERTY(int netStrength MEMBER net_strength NOTIFY valueChanged); - Q_PROPERTY(QString sidebarTemp MEMBER sidebar_temp_str NOTIFY valueChanged); public: explicit Sidebar(QWidget* parent = 0); @@ -36,6 +39,7 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void drawMetric(QPainter &p, const QPair &label, QColor c, int y); + virtual void DrawSidebar(QPainter &p); QPixmap home_img, flag_img, settings_img; bool onroad, flag_pressed, settings_pressed; @@ -52,19 +56,13 @@ protected: const QRect home_btn = QRect(60, 860, 180, 180); const QRect settings_btn = QRect(50, 35, 200, 117); const QColor good_color = QColor(255, 255, 255); - const QColor progress_color = QColor(3, 132, 252); const QColor warning_color = QColor(218, 202, 37); const QColor danger_color = QColor(201, 34, 49); - const QColor disabled_color = QColor(128, 128, 128); - ItemStatus connect_status, panda_status, temp_status, sunnylink_status; + ItemStatus connect_status, panda_status, temp_status; QString net_type; int net_strength = 0; private: - Params params; std::unique_ptr pm; - - QString sidebar_temp = "0"; - QString sidebar_temp_str = "0"; }; diff --git a/selfdrive/ui/qt/text.cc b/selfdrive/ui/qt/text.cc index 4b5f8ee21e..21ec5eedcf 100644 --- a/selfdrive/ui/qt/text.cc +++ b/selfdrive/ui/qt/text.cc @@ -4,41 +4,12 @@ #include #include #include -#include -#include -#include -#include "common/params.h" -#include "common/swaglog.h" #include "system/hardware/hw.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/widgets/scrollview.h" -std::string executeCommand(const char* cmd) { - std::array buffer{}; - std::ostringstream result; - std::unique_ptr pipe(popen(cmd, "r"), pclose); - if (!pipe) { - LOGW("Failed to open pipe"); - throw std::runtime_error("popen() failed!"); - } - while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { - result << buffer.data(); - } - return result.str(); -} - -// The format is intentional! -QString text = QString("%1\n%2\n%3\n%4\n%5\n%6\n%7") - .arg(" ________________________________________") - .arg("| |") - .arg("| " + QObject::tr("Update downloaded. Ready to reboot.") + " |") - .arg("| |") - .arg("| " + QObject::tr("Update: Check and Download Update") + " |") - .arg("| " + QObject::tr("Reboot: Reboot Device") + " |") - .arg("|________________________________________|"); - int main(int argc, char *argv[]) { initApp(argc, argv); QApplication a(argc, argv); @@ -51,7 +22,6 @@ int main(int argc, char *argv[]) { QLabel *label = new QLabel(argv[1]); label->setWordWrap(true); label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - label->setAlignment(Qt::AlignTop | Qt::AlignLeft); ScrollView *scroll = new ScrollView(label); scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); main_layout->addWidget(scroll, 0, 0, Qt::AlignTop); @@ -62,54 +32,16 @@ int main(int argc, char *argv[]) { }); QPushButton *btn = new QPushButton(); - QPushButton *update_btn = new QPushButton(); - update_btn->setText(QObject::tr("Update")); #ifdef __aarch64__ btn->setText(QObject::tr("Reboot")); - QFutureWatcher watcher; QObject::connect(btn, &QPushButton::clicked, [=]() { Hardware::reboot(); }); - QObject::connect(update_btn, &QPushButton::clicked, [=, &watcher]() { - btn->setEnabled(false); - update_btn->setEnabled(false); - update_btn->setText(QObject::tr("Updating...")); - const std::string git_branch = Params().get("GitBranch"); - const std::string git_remote = Params().get("GitRemote"); - const std::string to_home_dir = "cd /data/openpilot"; - const std::string check_remote = "git remote | grep origin-update"; - const std::string reset_remote = "git remote remove origin-update && git remote add origin-update " + git_remote; - const std::string add_remote = "git remote add origin-update " + git_remote; - const std::string fetch_remote = "git fetch origin-update " + git_branch; - const std::string reset_branch = "git reset --hard origin-update/" + git_branch + " 2>&1"; - - std::string remote_cmd = add_remote; - if (!std::system((to_home_dir + " && " + check_remote).c_str())) { - remote_cmd = reset_remote; - } - const std::string cmd = to_home_dir + "; " + remote_cmd + "; " + fetch_remote + "; " + reset_branch; - - QFuture future = QtConcurrent::run([=]() { - label->clear(); - std::string output = executeCommand(cmd.c_str()); - //LOGW("CHECK OUTPUT PLS\n%s", output.c_str()); - QMetaObject::invokeMethod(label, "setText", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(output) + text)); - }); - QObject::connect(&watcher, &QFutureWatcher::finished, [=]() { - btn->setEnabled(true); - update_btn->setEnabled(true); - update_btn->setText(QObject::tr("Update")); - }); - watcher.setFuture(future); - }); #else - update_btn->setEnabled(false); btn->setText(QObject::tr("Exit")); QObject::connect(btn, &QPushButton::clicked, &a, &QApplication::quit); #endif main_layout->addWidget(btn, 0, 0, Qt::AlignRight | Qt::AlignBottom); - main_layout->addWidget(update_btn, 0, 0, Qt::AlignLeft | Qt::AlignBottom); window.setStyleSheet(R"( * { @@ -126,10 +58,6 @@ int main(int argc, char *argv[]) { border-radius: 20px; margin-right: 40px; } - QPushButton:disabled { - color: #33FFFFFF; - border: 2px solid #33FFFFFF; - } )"); return a.exec(); diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc index 173d9ba970..886402b157 100644 --- a/selfdrive/ui/qt/util.cc +++ b/selfdrive/ui/qt/util.cc @@ -29,54 +29,38 @@ QString getBrand() { return QObject::tr("sunnypilot"); } -QString getUserAgent(bool sunnylink) { - return (sunnylink ? "sunnypilot-" : "openpilot-") + getVersion(); +QString getUserAgent() { + return "openpilot-" + getVersion(); +} + +std::optional getParamIgnoringDefault(const std::string ¶m_name, const std::string &default_value) { + std::string value = Params().get(param_name); + + if (!value.empty() && value != default_value) + return QString::fromStdString(value); + + return {}; } std::optional getDongleId() { - std::string id = Params().get("DongleId"); - - if (!id.empty() && (id != "UnregisteredDevice")) { - return QString::fromStdString(id); - } else { - return {}; - } + return getParamIgnoringDefault("DongleId", "UnregisteredDevice"); } -std::optional getSunnylinkDongleId() { - std::string id = Params().get("SunnylinkDongleId"); +QMap getFromJsonFile(const QString &path) { + QFile f(path); + f.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = f.readAll(); - if (!id.empty() && (id != "UnregisteredDevice")) { - return QString::fromStdString(id); - } else { - return {}; + QJsonObject obj = QJsonDocument::fromJson(val.toUtf8()).object(); + QMap map; + for (auto key : obj.keys()) { + map[key] = obj[key].toString(); } + return map; } QMap getSupportedLanguages() { - QFile f(":/languages.json"); - f.open(QIODevice::ReadOnly | QIODevice::Text); - QString val = f.readAll(); - - QJsonObject obj = QJsonDocument::fromJson(val.toUtf8()).object(); - QMap map; - for (auto key : obj.keys()) { - map[key] = obj[key].toString(); - } - return map; -} - -QMap getCarNames() { - QFile f("/data/openpilot/selfdrive/car/sunnypilot_carname.json"); - f.open(QIODevice::ReadOnly | QIODevice::Text); - QString val = f.readAll(); - - QJsonObject obj = QJsonDocument::fromJson(val.toUtf8()).object(); - QMap map; - for (auto key : obj.keys()) { - map[key] = obj[key].toString(); - } - return map; + return getFromJsonFile(":/languages.json"); } QString timeAgo(const QDateTime &date) { @@ -179,60 +163,6 @@ QPixmap loadPixmap(const QString &fileName, const QSize &size, Qt::AspectRatioMo } } -void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom){ - qreal w_2 = rect.width() / 2; - qreal h_2 = rect.height() / 2; - - xRadiusTop = 100 * qMin(xRadiusTop, w_2) / w_2; - yRadiusTop = 100 * qMin(yRadiusTop, h_2) / h_2; - - xRadiusBottom = 100 * qMin(xRadiusBottom, w_2) / w_2; - yRadiusBottom = 100 * qMin(yRadiusBottom, h_2) / h_2; - - qreal x = rect.x(); - qreal y = rect.y(); - qreal w = rect.width(); - qreal h = rect.height(); - - qreal rxx2Top = w*xRadiusTop/100; - qreal ryy2Top = h*yRadiusTop/100; - - qreal rxx2Bottom = w*xRadiusBottom/100; - qreal ryy2Bottom = h*yRadiusBottom/100; - - QPainterPath path; - path.arcMoveTo(x, y, rxx2Top, ryy2Top, 180); - path.arcTo(x, y, rxx2Top, ryy2Top, 180, -90); - path.arcTo(x+w-rxx2Top, y, rxx2Top, ryy2Top, 90, -90); - path.arcTo(x+w-rxx2Bottom, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 0, -90); - path.arcTo(x, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 270, -90); - path.closeSubpath(); - - painter.drawPath(path); -} - -QColor interpColor(float xv, std::vector xp, std::vector fp) { - assert(xp.size() == fp.size()); - - int N = xp.size(); - int hi = 0; - - while (hi < N and xv > xp[hi]) hi++; - int low = hi - 1; - - if (hi == N && xv > xp[low]) { - return fp[fp.size() - 1]; - } else if (hi == 0){ - return fp[0]; - } else { - return QColor( - (xv - xp[low]) * (fp[hi].red() - fp[low].red()) / (xp[hi] - xp[low]) + fp[low].red(), - (xv - xp[low]) * (fp[hi].green() - fp[low].green()) / (xp[hi] - xp[low]) + fp[low].green(), - (xv - xp[low]) * (fp[hi].blue() - fp[low].blue()) / (xp[hi] - xp[low]) + fp[low].blue(), - (xv - xp[low]) * (fp[hi].alpha() - fp[low].alpha()) / (xp[hi] - xp[low]) + fp[low].alpha()); - } -} - static QHash load_bootstrap_icons() { QHash icons; @@ -297,4 +227,4 @@ void ParamWatcher::fileChanged(const QString &path) { void ParamWatcher::addParam(const QString ¶m_name) { watcher->addPath(QString::fromStdString(params.getParamPath(param_name.toStdString()))); -} +} \ No newline at end of file diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h index 438b9677e7..20443db468 100644 --- a/selfdrive/ui/qt/util.h +++ b/selfdrive/ui/qt/util.h @@ -13,13 +13,19 @@ #include "cereal/gen/cpp/car.capnp.h" #include "common/params.h" +#ifdef SUNNYPILOT +#define RETURN_IF_SUNNYPILOT return; +#else +#define RETURN_IF_SUNNYPILOT // Do nothing +#endif + QString getVersion(); QString getBrand(); -QString getUserAgent(bool sunnylink = false); +QString getUserAgent(); +std::optional getParamIgnoringDefault(const std::string ¶m_name, const std::string &default_value); std::optional getDongleId(); -std::optional getSunnylinkDongleId(); +QMap getFromJsonFile(const QString &path); QMap getSupportedLanguages(); -QMap getCarNames(); void setQtSurfaceFormat(); void sigTermHandler(int s); QString timeAgo(const QDateTime &date); @@ -28,9 +34,6 @@ void initApp(int argc, char *argv[], bool disable_hidpi = true); QWidget* topWidget(QWidget* widget); QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio); QPixmap bootstrapPixmap(const QString &id); - -void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom); -QColor interpColor(float xv, std::vector xp, std::vector fp); bool hasLongitudinalControl(const cereal::CarParams::Reader &car_params); struct InterFont : public QFont { diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h index 526c17e77c..62999b2aa4 100644 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ b/selfdrive/ui/qt/widgets/cameraview.h @@ -24,7 +24,11 @@ #include "msgq/visionipc/visionipc_client.h" #include "system/camerad/cameras/camera_common.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif const int FRAME_BUFFER_SIZE = 5; static_assert(FRAME_BUFFER_SIZE <= YUV_BUFFER_COUNT); diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index 238051cf5e..8af48185aa 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -2,20 +2,10 @@ #include #include - -QFrame *horizontal_line(QWidget *parent) { - QFrame *line = new QFrame(parent); - line->setFrameShape(QFrame::StyledPanel); - line->setStyleSheet(R"( - border-width: 2px; - border-bottom-style: solid; - border-color: gray; - )"); - line->setFixedHeight(10); - return line; -} +#include AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) { + RETURN_IF_SUNNYPILOT QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setMargin(0); @@ -36,7 +26,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons // title title_label = new QPushButton(title); title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); + title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; border: none;"); hlayout->addWidget(title_label, 1); // value next to control button @@ -74,51 +64,6 @@ void AbstractControl::hideEvent(QHideEvent *e) { } } -SPAbstractControl::SPAbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setMargin(0); - - hlayout = new QHBoxLayout; - hlayout->setMargin(0); - hlayout->setSpacing(0); - - // title - if (!title.isEmpty()) { - title_label = new QPushButton(title); - title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); - main_layout->addWidget(title_label, 1); - - connect(title_label, &QPushButton::clicked, [=]() { - if (!description->isVisible()) { - emit showDescriptionEvent(); - } - - if (!description->text().isEmpty()) { - description->setVisible(!description->isVisible()); - } - }); - } else { - main_layout->addSpacing(20); - } - - main_layout->addLayout(hlayout); - main_layout->addSpacing(2); - - // description - description = new QLabel(desc); - description->setContentsMargins(0, 20, 40, 20); - description->setStyleSheet("font-size: 40px; color: grey"); - description->setWordWrap(true); - description->setVisible(false); - main_layout->addWidget(description); - - main_layout->addStretch(); -} - -void SPAbstractControl::hideEvent(QHideEvent *e) { -} - // controls ButtonControl::ButtonControl(const QString &title, const QString &text, const QString &desc, QWidget *parent) : AbstractControl(title, desc, "", parent) { @@ -178,15 +123,6 @@ ParamControl::ParamControl(const QString ¶m, const QString &title, const QSt : ToggleControl(title, desc, icon, false, parent) { key = param.toStdString(); QObject::connect(this, &ParamControl::toggleFlipped, this, &ParamControl::toggleClicked); - - hlayout->removeWidget(&toggle); - hlayout->insertWidget(0, &toggle); - - hlayout->removeWidget(this->icon_label); - this->icon_pixmap = QPixmap(icon).scaledToWidth(20, Qt::SmoothTransformation); - this->icon_label->setPixmap(this->icon_pixmap); - this->icon_label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - hlayout->insertWidget(1, this->icon_label); } void ParamControl::toggleClicked(bool state) { diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index 3466b67f5e..aa304e0df6 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -15,8 +14,6 @@ #include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/toggle.h" -QFrame *horizontal_line(QWidget *parent = nullptr); - class ElidedLabel : public QLabel { Q_OBJECT @@ -24,10 +21,6 @@ public: explicit ElidedLabel(QWidget *parent = 0); explicit ElidedLabel(const QString &text, QWidget *parent = 0); - void setColor(const QString &color) { - setStyleSheet("QLabel { color : " + color + "; }"); - } - signals: void clicked(); @@ -55,11 +48,8 @@ public: title_label->setText(title); } - void setValue(const QString &val, std::optional color = std::nullopt) { + void setValue(const QString &val) { value->setText(val); - if (color.has_value()) { - value->setColor(color.value()); - } } const QString getDescription() { @@ -74,10 +64,6 @@ public slots: description->setVisible(true); } - void hideDescription() { - description->setVisible(false); - } - signals: void showDescriptionEvent(); @@ -117,7 +103,6 @@ public: ButtonControl(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr); inline void setText(const QString &text) { btn.setText(text); } inline QString text() const { return btn.text(); } - inline void click() { btn.click(); } signals: void clicked(); @@ -181,8 +166,6 @@ public: refresh(); } - bool isToggled() { return params.getBool(key); } - private: void toggleClicked(bool state); void setIcon(bool state) { @@ -200,71 +183,29 @@ private: bool store_confirm = false; }; -class SPAbstractControl : public QFrame { - Q_OBJECT - -public: - void setDescription(const QString &desc) { - if (description) description->setText(desc); - } - - void setTitle(const QString &title) { - title_label->setText(title); - } - - const QString getDescription() { - return description->text(); - } - -public slots: - void showDescription() { - description->setVisible(true); - } - - void hideDescription() { - description->setVisible(false); - } - -signals: - void showDescriptionEvent(); - -protected: - SPAbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); - void hideEvent(QHideEvent *e) override; - - QHBoxLayout *hlayout; - QPushButton *title_label; - -private: - QLabel *description = nullptr; -}; - -class ButtonParamControl : public SPAbstractControl { +class ButtonParamControl : public AbstractControl { Q_OBJECT public: ButtonParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const std::vector &button_texts, const int minimum_button_width = 300) : SPAbstractControl(title, desc, icon), button_texts(button_texts) { + const std::vector &button_texts, const int minimum_button_width = 225) : AbstractControl(title, desc, icon) { const QString style = R"( QPushButton { - border-radius: 20px; - font-size: 50px; - font-weight: 450; - height:150px; + border-radius: 50px; + font-size: 40px; + font-weight: 500; + height:100px; padding: 0 25 0 25; - color: #FFFFFF; + color: #E4E4E4; + background-color: #393939; } QPushButton:pressed { background-color: #4a4a4a; } QPushButton:checked:enabled { - background-color: #696868; + background-color: #33Ab4C; } QPushButton:disabled { - color: #33FFFFFF; - } - QPushButton:checked:disabled { - background-color: #121212; - color: #33FFFFFF; + color: #33E4E4E4; } )"; key = param.toStdString(); @@ -278,16 +219,12 @@ public: button->setChecked(i == value); button->setStyleSheet(style); button->setMinimumWidth(minimum_button_width); - if (i == 0) hlayout->addSpacing(2); hlayout->addWidget(button); button_group->addButton(button, i); } - hlayout->setAlignment(Qt::AlignLeft); - QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), [=](int id) { params.put(key, std::to_string(id)); - emit buttonToggled(id); }); } @@ -295,9 +232,6 @@ public: for (auto btn : button_group->buttons()) { btn->setEnabled(enable); } - button_group_enabled = enable; - - update(); } void setCheckedButton(int id) { @@ -306,14 +240,6 @@ public: void refresh() { int value = atoi(params.get(key).c_str()); - - if (value >= button_texts.size()) { - value = button_texts.size() - 1; - } - if (value < 0) { - value = 0; - } - button_group->button(value)->setChecked(true); } @@ -321,59 +247,16 @@ public: refresh(); } - void setButton(QString param) { - key = param.toStdString(); - int value = atoi(params.get(key).c_str()); - for (int i = 0; i < button_group->buttons().size(); i++) { - button_group->buttons()[i]->setChecked(i == value); - } - } - - void setDisabledSelectedButton(std::string val) { - int value = atoi(val.c_str()); - for (int i = 0; i < button_group->buttons().size(); i++) { - button_group->buttons()[i]->setEnabled(i != value); - } - } - -protected: - void paintEvent(QPaintEvent *event) override { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - // Calculate the total width and height for the background rectangle - int w = 0; - int h = 150; - - for (int i = 0; i < hlayout->count(); ++i) { - QPushButton *button = qobject_cast(hlayout->itemAt(i)->widget()); - if (button) { - w += button->width(); - } - } - - // Draw the rectangle - QRect rect(0 + 2, h - 24, w, h); - p.setPen(QPen(QColor(button_group_enabled ? "#696868" : "#121212"), 3)); - p.drawRoundedRect(rect, 20, 20); - } - -signals: - void buttonToggled(int btn_id); - private: std::string key; Params params; QButtonGroup *button_group; - std::vector button_texts; - - bool button_group_enabled = true; }; class ListWidget : public QWidget { Q_OBJECT public: - explicit ListWidget(QWidget *parent = 0, const bool split_line = true) : QWidget(parent), _split_line(split_line), outer_layout(this) { + explicit ListWidget(QWidget *parent = 0) : QWidget(parent), outer_layout(this) { outer_layout.setMargin(0); outer_layout.setSpacing(0); outer_layout.addLayout(&inner_layout); @@ -385,30 +268,13 @@ class ListWidget : public QWidget { inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); } inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); } - inline void AddWidgetAt(const int index, QWidget *new_widget) { inner_layout.insertWidget(index, new_widget); } - inline void RemoveWidgetAt(const int index) { - if (QLayoutItem* item; (item = inner_layout.takeAt(index)) != nullptr) { - if(item->widget()) delete item->widget(); - delete item; - } - } - - inline void ReplaceOrAddWidget(QWidget *old_widget, QWidget *new_widget) { - if (const int index = inner_layout.indexOf(old_widget); index != -1) { - RemoveWidgetAt(index); - AddWidgetAt(index, new_widget); - } else { - addItem(new_widget); - } - } - private: void paintEvent(QPaintEvent *) override { QPainter p(this); p.setPen(Qt::gray); for (int i = 0; i < inner_layout.count() - 1; ++i) { QWidget *widget = inner_layout.itemAt(i)->widget(); - if ((widget == nullptr || widget->isVisible()) && _split_line) { + if (widget == nullptr || widget->isVisible()) { QRect r = inner_layout.itemAt(i)->geometry(); int bottom = r.bottom() + inner_layout.spacing() / 2; p.drawLine(r.left() + 40, bottom, r.right() - 40, bottom); @@ -417,8 +283,6 @@ private: } QVBoxLayout outer_layout; QVBoxLayout inner_layout; - - bool _split_line; }; // convenience class for wrapping layouts @@ -430,178 +294,3 @@ public: setLayout(l); } }; - -class SPOptionControl : public SPAbstractControl { - Q_OBJECT - -private: - struct MinMaxValue { - int min_value; - int max_value; - }; - -public: - SPOptionControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const MinMaxValue &range, const int per_value_change = 1) : _title(title), SPAbstractControl(title, desc, icon) { - const QString style = R"( - QPushButton { - border-radius: 20px; - font-size: 60px; - font-weight: 500; - width: 150px; - height: 150px; - padding: -3 25 3 25; - color: #FFFFFF; - font-weight: bold; - } - QPushButton:pressed { - color: #5C5C5C; - } - QPushButton:disabled { - color: #5C5C5C; - } - )"; - - label.setStyleSheet(label_enabled_style); - label.setFixedWidth(300); - label.setAlignment(Qt::AlignCenter); - - const std::vector button_texts{"-", "+"}; - - key = param.toStdString(); - value = atoi(params.get(key).c_str()); - - button_group = new QButtonGroup(this); - button_group->setExclusive(true); - for (int i = 0; i < button_texts.size(); i++) { - QPushButton *button = new QPushButton(button_texts[i], this); - button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" : - "QPushButton { text-align: right; }")); - hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); - if (i == 0) { - hlayout->addWidget(&label, 0, Qt::AlignCenter); - } - button_group->addButton(button, i); - - QObject::connect(button, &QPushButton::clicked, [=]() { - int change_value = (i == 0) ? -per_value_change : per_value_change; - key = param.toStdString(); - value = atoi(params.get(key).c_str()); - value += change_value; - value = std::clamp(value, range.min_value, range.max_value); - params.put(key, QString::number(value).toStdString()); - - button_group->button(0)->setEnabled(!(value <= range.min_value)); - button_group->button(1)->setEnabled(!(value >= range.max_value)); - - updateLabels(); - - if (request_update) { - emit updateOtherToggles(); - } - }); - } - - hlayout->setAlignment(Qt::AlignLeft); - } - - void setUpdateOtherToggles(bool _update) { - request_update = _update; - } - - inline void setLabel(const QString &text) { label.setText(text); } - - void setEnabled(bool enabled) { - for (auto btn : button_group->buttons()) { - btn->setEnabled(enabled); - } - label.setEnabled(enabled); - label.setStyleSheet(enabled ? label_enabled_style : label_disabled_style); - button_enabled = enabled; - - update(); - } - -protected: - void paintEvent(QPaintEvent *event) override { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - // Calculate the total width and height for the background rectangle - int w = 0; - int h = 150; - - for (int i = 0; i < hlayout->count(); ++i) { - QWidget *widget = qobject_cast(hlayout->itemAt(i)->widget()); - if (widget) { - w += widget->width(); - } - } - - // Draw the rectangle - QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h); - p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color - p.setPen(QPen(Qt::NoPen)); - p.drawRoundedRect(rect, 20, 20); - } - -signals: - void updateLabels(); - void updateOtherToggles(); - -private: - std::string key; - int value; - QButtonGroup *button_group; - QLabel label; - Params params; - std::map option_label = {}; - bool request_update = false; - QString _title = ""; - - const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;"; - const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;"; - - bool button_enabled = true; -}; - -class SubPanelButton : public QPushButton { - Q_OBJECT - -public: - SubPanelButton(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) { - const QString buttonStyle = R"( - QPushButton { - border-radius: 20px; - font-size: 50px; - font-weight: 450; - height: 150px; - padding: 0 25px 0 25px; - color: #FFFFFF; - } - QPushButton:enabled { - background-color: #393939; - } - QPushButton:pressed { - background-color: #4A4A4A; - } - QPushButton:disabled { - background-color: #121212; - color: #5C5C5C; - } - )"; - - setStyleSheet(buttonStyle); - setFixedWidth(minimum_button_width); - } -}; - -class PanelBackButton : public QPushButton { - Q_OBJECT - -public: - PanelBackButton(const QString &label = "Back", QWidget *parent = nullptr) : QPushButton(label, parent) { - setObjectName("back_btn"); - setFixedSize(400, 100); - } -}; diff --git a/selfdrive/ui/qt/widgets/scrollview.cc b/selfdrive/ui/qt/widgets/scrollview.cc index 7f6a5ff1cc..978bf83a63 100644 --- a/selfdrive/ui/qt/widgets/scrollview.cc +++ b/selfdrive/ui/qt/widgets/scrollview.cc @@ -47,11 +47,3 @@ ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent) { void ScrollView::hideEvent(QHideEvent *e) { verticalScrollBar()->setValue(0); } - -void ScrollView::setLastScrollPosition() { - lastScrollPosition = verticalScrollBar()->value(); -} - -void ScrollView::restoreScrollPosition() { - verticalScrollBar()->setValue(lastScrollPosition); -} diff --git a/selfdrive/ui/qt/widgets/scrollview.h b/selfdrive/ui/qt/widgets/scrollview.h index 7e4084412c..024331aa39 100644 --- a/selfdrive/ui/qt/widgets/scrollview.h +++ b/selfdrive/ui/qt/widgets/scrollview.h @@ -9,11 +9,4 @@ public: explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr); protected: void hideEvent(QHideEvent *e) override; - -public slots: - void setLastScrollPosition(); - void restoreScrollPosition(); - -private: - int lastScrollPosition = 0; }; diff --git a/selfdrive/ui/qt/widgets/ssh_keys.h b/selfdrive/ui/qt/widgets/ssh_keys.h index 920bd651e2..2834164702 100644 --- a/selfdrive/ui/qt/widgets/ssh_keys.h +++ b/selfdrive/ui/qt/widgets/ssh_keys.h @@ -3,7 +3,13 @@ #include #include "system/hardware/hw.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define ToggleControl ToggleControlSP +#define ButtonControl ButtonControlSP +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif // SSH enable toggle class SshToggle : public ToggleControl { diff --git a/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h b/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h deleted file mode 100644 index 5e2d96b240..0000000000 --- a/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -class DriveStats : public QFrame { - Q_OBJECT - -public: - explicit DriveStats(QWidget* parent = 0); - -private: - void showEvent(QShowEvent *event) override; - void updateStats(); - inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); } - - bool metric_; - QJsonDocument stats_; - struct StatsLabels { - QLabel *routes, *distance, *distance_unit, *hours; - } all_, week_; - -private slots: - void parseResponse(const QString &response, bool success); -}; diff --git a/selfdrive/ui/qt/widgets/toggle.cc b/selfdrive/ui/qt/widgets/toggle.cc index c8f12bc272..82302ad5bc 100644 --- a/selfdrive/ui/qt/widgets/toggle.cc +++ b/selfdrive/ui/qt/widgets/toggle.cc @@ -75,9 +75,9 @@ void Toggle::setEnabled(bool value) { enabled = value; if (value) { circleColor.setRgb(0xfafafa); - green.setRgb(0x1e79e8); + green.setRgb(0x33ab4c); } else { circleColor.setRgb(0x888888); - green.setRgb(0x125db8); + green.setRgb(0x227722); } } diff --git a/selfdrive/ui/qt/widgets/toggle.h b/selfdrive/ui/qt/widgets/toggle.h index e7263a008f..a0fa434a4c 100644 --- a/selfdrive/ui/qt/widgets/toggle.h +++ b/selfdrive/ui/qt/widgets/toggle.h @@ -23,14 +23,13 @@ public: update(); } bool getEnabled(); - void setEnabled(bool value); + virtual void setEnabled(bool value); protected: void paintEvent(QPaintEvent*) override; void mouseReleaseEvent(QMouseEvent*) override; void enterEvent(QEvent*) override; -private: QColor circleColor; QColor green; bool enabled = true; diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h index 60c865f2b8..3010abc6ff 100644 --- a/selfdrive/ui/qt/widgets/wifi.h +++ b/selfdrive/ui/qt/widgets/wifi.h @@ -4,7 +4,11 @@ #include #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif class WiFiPromptWidget : public QFrame { Q_OBJECT diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index 6b6f939af4..d9ff51763b 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -4,16 +4,18 @@ #include "system/hardware/hw.h" -MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { +MainWindow::MainWindow(QWidget* parent, HomeWindow* hw, SettingsWindow* sw, OnboardingWindow* ow) + : QWidget(parent), + homeWindow(hw ? hw : new HomeWindow(this)), + settingsWindow(sw ? sw : new SettingsWindow(this)), + onboardingWindow(ow ? ow : new OnboardingWindow(this)) { main_layout = new QStackedLayout(this); main_layout->setMargin(0); - homeWindow = new HomeWindow(this); main_layout->addWidget(homeWindow); QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings); QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings); - settingsWindow = new SettingsWindow(this); main_layout->addWidget(settingsWindow); QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings); QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() { @@ -24,7 +26,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { homeWindow->showDriverView(true); }); - onboardingWindow = new OnboardingWindow(this); main_layout->addWidget(onboardingWindow); QObject::connect(onboardingWindow, &OnboardingWindow::onboardingDone, [=]() { main_layout->setCurrentWidget(homeWindow); @@ -75,10 +76,6 @@ void MainWindow::closeSettings() { if (uiState()->scene.started) { homeWindow->showSidebar(false); - // Map is always shown when using navigate on openpilot - if (uiState()->scene.navigate_on_openpilot_deprecated) { - homeWindow->showMapPanel(true); - } } } diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h index 05b61e1f76..fa0c43486e 100644 --- a/selfdrive/ui/qt/window.h +++ b/selfdrive/ui/qt/window.h @@ -11,15 +11,18 @@ class MainWindow : public QWidget { Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0); + explicit MainWindow(QWidget *parent = nullptr) : MainWindow(parent, nullptr, nullptr, nullptr) {} + +protected: + explicit MainWindow(QWidget* parent, HomeWindow* hw = nullptr, SettingsWindow* sw = nullptr, OnboardingWindow* ow = nullptr); + HomeWindow *homeWindow; + SettingsWindow *settingsWindow; + OnboardingWindow *onboardingWindow; + virtual void closeSettings(); private: bool eventFilter(QObject *obj, QEvent *event) override; void openSettings(int index = 0, const QString ¶m = ""); - void closeSettings(); QStackedLayout *main_layout; - HomeWindow *homeWindow; - SettingsWindow *settingsWindow; - OnboardingWindow *onboardingWindow; }; diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 9290fcea58..945c9ccdb2 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -1,36 +1,63 @@ widgets_src = [ - "qt/offroad/sunnypilot/custom_offsets_settings.cc", - "qt/offroad/sunnypilot/display_settings.cc", - "qt/offroad/sunnypilot/lane_change_settings.cc", - "qt/offroad/sunnypilot/mads_settings.cc", - "qt/offroad/sunnypilot/models_fetcher.cc", - "qt/offroad/sunnypilot/monitoring_settings.cc", - "qt/offroad/sunnypilot/osm_settings.cc", - "qt/offroad/sunnypilot/software_settings_sp.cc", - "qt/offroad/sunnypilot/speed_limit_control_settings.cc", - "qt/offroad/sunnypilot/speed_limit_policy_settings.cc", - "qt/offroad/sunnypilot/speed_limit_warning_settings.cc", - "qt/offroad/sunnypilot/sunnypilot_settings.cc", - "qt/offroad/sunnypilot/sunnylink_settings.cc", - "qt/offroad/sunnypilot/trips_settings.cc", - "qt/offroad/sunnypilot/vehicle_settings.cc", - "qt/offroad/sunnypilot/visuals_settings.cc", - "qt/widgets/sunnypilot/drive_stats.cc" + "sunnypilot/ui.cc", + "sunnypilot/qt/window.cc", + "sunnypilot/qt/request_repeater.cc", + "sunnypilot/qt/network/networking.cc", + "sunnypilot/qt/offroad/settings/display_settings.cc", + "sunnypilot/qt/offroad/settings/sunnypilot_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle_settings.cc", + "sunnypilot/qt/offroad/settings/visuals_settings.cc", + "sunnypilot/qt/offroad/settings/trips_settings.cc", + "sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc", + "sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.cc", + "sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.cc", + "sunnypilot/qt/offroad/settings/monitoring_settings.cc", + "sunnypilot/qt/offroad/settings/osm_settings.cc", + "sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.cc", + "sunnypilot/qt/widgets/drive_stats.cc", + "sunnypilot/qt/offroad/settings/software_settings.cc", + "sunnypilot/qt/offroad/settings/osm/models_fetcher.cc", + "sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.cc", + "sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.cc", + "sunnypilot/qt/offroad/settings/sunnylink_settings.cc", + "sunnypilot/qt/widgets/controls.cc", + "sunnypilot/qt/widgets/scrollview.cc", + "sunnypilot/qt/widgets/toggle.cc" +] + +sp_maps_widgets_src = [ + "sunnypilot/qt/maps/map.cc" +] + +sp_qt_util = [ + # "#selfdrive/ui/sunnypilot/qt/api.cc", + "#selfdrive/ui/sunnypilot/qt/util.cc", ] network_src = [ - "qt/network/sunnylink/services/base_device_service.cc", - "qt/network/sunnylink/services/role_service.cc", - "qt/network/sunnylink/services/user_service.cc", - "qt/network/sunnylink/sunnylink_client.cc" + "sunnypilot/qt/network/sunnylink/sunnylink_client.cc", + "sunnypilot/qt/network/sunnylink/services/base_device_service.cc", + "sunnypilot/qt/network/sunnylink/services/role_service.cc", + "sunnypilot/qt/network/sunnylink/services/user_service.cc" ] qt_src = [ - "qt/onroad_settings.cc", - "qt/onroad_settings_panel.cc" + "sunnypilot/qt/api.cc", + "sunnypilot/qt/home.cc", + "sunnypilot/qt/offroad_home.cc", + "sunnypilot/qt/sidebar.cc", + "sunnypilot/qt/offroad/settings/onboarding.cc", + "sunnypilot/qt/offroad/settings/device_panel.cc", + "sunnypilot/qt/offroad/settings/settings.cc", + "sunnypilot/qt/onroad/buttons.cc", + "sunnypilot/qt/onroad/onroad_home.cc", + "sunnypilot/qt/onroad/onroad_settings.cc", + "sunnypilot/qt/onroad/annotated_camera.cc", + "sunnypilot/qt/onroad/onroad_settings_panel.cc", + "sunnypilot/qt/onroad/developer_ui/developer_ui.cc", ] sp_widgets_src = widgets_src + network_src sp_qt_src = qt_src -Export('sp_widgets_src', 'sp_qt_src') +Export('sp_widgets_src', 'sp_maps_widgets_src', 'sp_qt_src', "sp_qt_util") diff --git a/selfdrive/ui/sunnypilot/qt/api.cc b/selfdrive/ui/sunnypilot/qt/api.cc new file mode 100644 index 0000000000..fcf925fd1a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/api.cc @@ -0,0 +1,212 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/api.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#include "util.h" +#include "common/util.h" +#include "system/hardware/hw.h" +#include "selfdrive/ui/qt/util.h" + +namespace SunnylinkApi { + +QString create_jwt(const QJsonObject &payloads, int expiry, bool sunnylink) { + QJsonObject header = {{"alg", "RS256"}}; + + auto t = QDateTime::currentSecsSinceEpoch(); + auto dongle_id = sunnylink ? getSunnylinkDongleId() : getDongleId(); + QJsonObject payload = {{"identity", dongle_id.value_or("")}, {"nbf", t}, {"iat", t}, {"exp", t + expiry}}; + for (auto it = payloads.begin(); it != payloads.end(); ++it) { + payload.insert(it.key(), it.value()); + } + + auto b64_opts = QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals; + QString jwt = QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64(b64_opts) + '.' + + QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64(b64_opts); + + auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256); + return jwt + "." + CommaApi::rsa_sign(hash).toBase64(b64_opts); +} + + void derive_aes_key_iv_from_rsa(EVP_PKEY *rsa_key, unsigned char *aes_key, unsigned char *aes_iv) { + unsigned char hash[SHA256_DIGEST_LENGTH]; + size_t pub_len; + unsigned char *pub_key; + + // Convert RSA key to public key in DER format for simplicity + pub_len = i2d_PublicKey(rsa_key, NULL); + pub_key = (unsigned char *)malloc(pub_len); + unsigned char *tmp = pub_key; + i2d_PublicKey(rsa_key, &tmp); + + // Hash the public key to derive bytes for AES key and IV + SHA256(pub_key, pub_len, hash); + + // Assuming AES-256-CBC, we need 32 bytes for the key and 16 for the IV + memcpy(aes_key, hash, 32); // First 32 bytes for AES key + memcpy(aes_iv, hash + 32 - 16, 16); // Last 16 bytes for AES IV + + free(pub_key); +} + +EVP_PKEY *load_public_key(const char *file) { + EVP_PKEY *key = NULL; + FILE *fp = fopen(file, "r"); + if (fp) { + key = PEM_read_PUBKEY(fp, NULL, NULL, NULL); + fclose(fp); + } + return key; +} + +EVP_PKEY *load_private_key(const char *file) { + EVP_PKEY *key = NULL; + FILE *fp = fopen(file, "r"); + if (fp) { + key = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + fclose(fp); + } + return key; +} + +QByteArray rsa_encrypt(const QByteArray &data) { + EVP_PKEY *rsa_key = load_public_key(Path::rsa_pub_file().c_str()); // Load your RSA key + unsigned char aes_key[32], aes_iv[16]; + derive_aes_key_iv_from_rsa(rsa_key, aes_key, aes_iv); + + EVP_CIPHER_CTX *ctx; + if (!(ctx = EVP_CIPHER_CTX_new())) { + // Handle error: Allocate memory failed + return {}; + } + + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, aes_key, aes_iv) != 1) { + qDebug() << "Failed to initialize encryption"; + EVP_CIPHER_CTX_free(ctx); + return {}; + } + + int ciphertext_len; + int len = data.size(); + unsigned char out[len + AES_BLOCK_SIZE]; + auto *in = (unsigned char*) data.constData(); + if (EVP_EncryptUpdate(ctx, out, &ciphertext_len, in, len) != 1) { + qDebug() << "Failed to update encryption"; + EVP_CIPHER_CTX_free(ctx); + return {}; + } + + if (EVP_EncryptFinal_ex(ctx, out + ciphertext_len, &len) != 1) { + // Handle error: Encryption finalize failed + qDebug() << "Failed to finalize encryption"; + EVP_CIPHER_CTX_free(ctx); + return {}; + } + + //qDebug() << "Encrypted data length: %d", ciphertext_len + len; // Print the length of encrypted data + EVP_CIPHER_CTX_free(ctx); + return {reinterpret_cast(out), ciphertext_len + len}; +} + +QByteArray rsa_decrypt(const QByteArray &data) { + EVP_PKEY *rsa_key = load_public_key(Path::rsa_pub_file().c_str()); // Load your RSA key + unsigned char aes_key[32], aes_iv[16]; + derive_aes_key_iv_from_rsa(rsa_key, aes_key, aes_iv); + + EVP_CIPHER_CTX *ctx; + if (!(ctx = EVP_CIPHER_CTX_new())) { + qDebug() << "Failed to allocate memory for EVP_CIPHER_CTX"; + return {}; + } + + if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, aes_key, aes_iv) != 1) { + qDebug() << "Failed to initialize EVP"; + EVP_CIPHER_CTX_free(ctx); + return {}; + } + + int len = data.size(); + unsigned char out[len + AES_BLOCK_SIZE]; + auto *in = (unsigned char*) data.constData(); + if (EVP_DecryptUpdate(ctx, out, &len, in, len) != 1) { + qDebug() << "Failed to update decryption"; + EVP_CIPHER_CTX_free(ctx); + return {}; + } + + int final_len; + if (EVP_DecryptFinal_ex(ctx, out + len, &final_len) != 1) { + qDebug() << "Failed to finalize decryption"; + EVP_CIPHER_CTX_free(ctx); + return {}; + } + EVP_CIPHER_CTX_free(ctx); + + //qDebug() << "Decrypted data length: %d", len + final_len; // Print the length of decrypted data + return {reinterpret_cast(out), len + final_len}; +} + + +} // namespace SunnylinkApi + +void HttpRequestSP::sendRequest(const QString& requestURL, Method method, const QByteArray& payload) { + if (active()) { + return; + } + QNetworkRequest request = prepareRequest(requestURL); + + if(!payload.isEmpty() && (method == Method::POST || method == Method::PUT)) { + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + } + + switch (method) { + case Method::GET: + reply = nam()->get(request); + break; + case Method::DELETE: + reply = nam()->deleteResource(request); + break; + case Method::POST: + reply = nam()->post(request, payload); + break; + case Method::PUT: + reply = nam()->put(request, payload); + break; + } + + networkTimer->start(); + connect(reply, &QNetworkReply::finished, this, &HttpRequestSP::requestFinished); +} diff --git a/selfdrive/ui/sunnypilot/qt/api.h b/selfdrive/ui/sunnypilot/qt/api.h new file mode 100644 index 0000000000..b8e835dc91 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/api.h @@ -0,0 +1,55 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/api.h" +#include "selfdrive/ui/sunnypilot/qt/util.h" +#include "common/util.h" + +namespace SunnylinkApi { + QByteArray rsa_encrypt(const QByteArray& data); + QByteArray rsa_decrypt(const QByteArray& data); + QString create_jwt(const QJsonObject& payloads = {}, int expiry = 3600, bool sunnylink = false); +} + +class HttpRequestSP : public HttpRequest { + Q_OBJECT + +public: + explicit HttpRequestSP(QObject* parent, bool create_jwt = true, int timeout = 20000, bool sunnylink = false) : + HttpRequest(parent, create_jwt, timeout), sunnylink(sunnylink) {} + + using HttpRequest::sendRequest; + void sendRequest(const QString& requestURL, Method method, const QByteArray& payload); + +private: + bool sunnylink; + +protected: + [[nodiscard]] QString GetJwtToken() const override { return SunnylinkApi::create_jwt({}, 3600, sunnylink); } + [[nodiscard]] QString GetUserAgent() const override { return getUserAgent(sunnylink); } +}; diff --git a/selfdrive/ui/sunnypilot/qt/common/json_fetcher.h b/selfdrive/ui/sunnypilot/qt/common/json_fetcher.h new file mode 100644 index 0000000000..2002e72aa0 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/common/json_fetcher.h @@ -0,0 +1,60 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include +#include +#include + +class JsonFetcher { +public: + static QJsonObject getJsonFromURL(const QString &url) { + const auto qurl = QUrl(url); + QNetworkAccessManager manager; + const QNetworkRequest request(qurl); + QNetworkReply *reply = manager.get(request); + QEventLoop loop; + + // Send GET request + + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "Failed to fetch data from URL: " << reply->errorString(); + return QJsonObject(); + } + + const QByteArray responseData = reply->readAll(); + const QJsonDocument doc = QJsonDocument::fromJson(responseData); + QJsonObject json = doc.object(); + + reply->deleteLater(); + return json; + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/home.cc b/selfdrive/ui/sunnypilot/qt/home.cc new file mode 100644 index 0000000000..61abbff7dc --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/home.cc @@ -0,0 +1,80 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/home.h" + +#include +#include +#include + +#include "selfdrive/ui/qt/offroad/experimental_mode.h" +#include "selfdrive/ui/qt/util.h" + +// HomeWindowSP: the container for the offroad and onroad UIs +HomeWindowSP::HomeWindowSP(QWidget* parent) : HomeWindow(parent){ + QObject::connect(onroad, &OnroadWindow::mapPanelRequested, this, [=] { sidebar->hide(); }); + QObject::connect(onroad, &OnroadWindow::onroadSettingsPanelRequested, this, [=] { sidebar->hide(); }); +} + +void HomeWindowSP::showMapPanel(bool show) { + onroad->showMapPanel(show); +} + +void HomeWindowSP::updateState(const UIState &s) { //OVERRIDE + HomeWindow::updateState(s); + + uiStateSP()->scene.map_visible = onroad->isMapVisible(); + uiStateSP()->scene.onroad_settings_visible = onroad->isOnroadSettingsVisible(); +} + +void HomeWindowSP::mousePressEvent(QMouseEvent* e) { + // We are not calling the parent for the time being because it only handles sidebar, and the sidebar code conflicts + // with ours as we turn off the sidebar after it was turned on by the parent when the tap happens beyond the 300px of the left. + // HomeWindow::mousePressEvent(e); + + if (uiStateSP()->scene.started) { + if (uiStateSP()->scene.onroadScreenOff != -2) { + uiStateSP()->scene.touched2 = true; + QTimer::singleShot(500, []() { uiStateSP()->scene.touched2 = false; }); + } + if (uiStateSP()->scene.button_auto_hide) { + uiStateSP()->scene.touch_to_wake = true; + uiStateSP()->scene.sleep_btn_fading_in = true; + QTimer::singleShot(500, []() { uiStateSP()->scene.touch_to_wake = false; }); + } + } + + // TODO: a very similar, but not identical call is made by parent. Which made me question if I should override it here... + // Will have to revisit later if this is not behaving as expected. + // Handle sidebar collapsing + if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) { + LOGD("HomeWindowSP sidebar->isVisible() [%d] | e->x() [%d] | sidebar->width() [%d]", sidebar->isVisible(), e->x(), sidebar->width()); + if (onroad->wakeScreenTimeout()) { + LOGD("HomeWindowSP wakeScreenTimeout() [%d]", onroad->wakeScreenTimeout()); + sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible()); + } + } +} diff --git a/selfdrive/ui/sunnypilot/qt/home.h b/selfdrive/ui/sunnypilot/qt/home.h new file mode 100644 index 0000000000..8bb44d81b4 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/home.h @@ -0,0 +1,59 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/body.h" +#include "selfdrive/ui/qt/widgets/offroad_alerts.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/qt/home.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/sidebar.h" +#define OnroadWindow OnroadWindowSP +#else +#include "selfdrive/ui/qt/sidebar.h" +#include "selfdrive/ui/qt/onroad/onroad_home.h" +#endif + +class HomeWindowSP : public HomeWindow { + Q_OBJECT + +public: + explicit HomeWindowSP(QWidget* parent = 0); + +public slots: + void showMapPanel(bool show); + +protected: + void mousePressEvent(QMouseEvent* e) override; +private slots: + void updateState(const UIState &s) override; +}; diff --git a/selfdrive/ui/sunnypilot/qt/maps/map.cc b/selfdrive/ui/sunnypilot/qt/maps/map.cc new file mode 100644 index 0000000000..f1d649980a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/maps/map.cc @@ -0,0 +1,293 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/maps/map.h" + +#include "common/swaglog.h" +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" + +const float MAX_ZOOM = 17; +const float MIN_ZOOM = 14; +const float MAX_PITCH = 50; + +MapWindowSP::MapWindowSP(const QMapLibre::Settings &settings) : MapWindow(settings) { + QObject::disconnect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState); + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &MapWindowSP::updateState); +} + +MapWindowSP::~MapWindowSP() { + makeCurrent(); +} + +void MapWindowSP::initLayers() { + // This doesn't work from initializeGL + if (!m_map->layerExists("modelPathLayer")) { + qDebug() << "Initializing modelPathLayer"; + QVariantMap modelPath; + //modelPath["id"] = "modelPathLayer"; + modelPath["type"] = "line"; + modelPath["source"] = "modelPathSource"; + m_map->addLayer("modelPathLayer", modelPath); + m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red")); + m_map->setPaintProperty("modelPathLayer", "line-width", 5.0); + m_map->setLayoutProperty("modelPathLayer", "line-cap", "round"); + } + if (!m_map->layerExists("navLayer")) { + qDebug() << "Initializing navLayer"; + QVariantMap nav; + nav["type"] = "line"; + nav["source"] = "navSource"; + m_map->addLayer("navLayer", nav, "road-intersection"); + + QVariantMap transition; + transition["duration"] = 400; // ms + m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(uiStateSP()->scene.navigate_on_openpilot_deprecated)); + m_map->setPaintProperty("navLayer", "line-color-transition", transition); + m_map->setPaintProperty("navLayer", "line-width", 7.5); + m_map->setLayoutProperty("navLayer", "line-cap", "round"); + } + if (!m_map->layerExists("pinLayer")) { + qDebug() << "Initializing pinLayer"; + m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg")); + QVariantMap pin; + pin["type"] = "symbol"; + pin["source"] = "pinSource"; + m_map->addLayer("pinLayer", pin); + m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport"); + m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker"); + m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true); + m_map->setLayoutProperty("pinLayer", "icon-allow-overlap", true); + m_map->setLayoutProperty("pinLayer", "symbol-sort-key", 0); + m_map->setLayoutProperty("pinLayer", "icon-anchor", "bottom"); + } + if (!m_map->layerExists("carPosLayer")) { + qDebug() << "Initializing carPosLayer"; + m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg")); + + QVariantMap carPos; + carPos["type"] = "symbol"; + carPos["source"] = "carPosSource"; + m_map->addLayer("carPosLayer", carPos); + m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map"); + m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow"); + m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5); + m_map->setLayoutProperty("carPosLayer", "icon-ignore-placement", true); + m_map->setLayoutProperty("carPosLayer", "icon-allow-overlap", true); + // TODO: remove, symbol-sort-key does not seem to matter outside of each layer + m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0); + } + if ((!m_map->layerExists("buildingsLayer")) && uiStateSP()->scene.map_3d_buildings) { // Could put this behind the cellular metered toggle in case it increases data usage + qDebug() << "Initializing buildingsLayer"; + QVariantMap buildings; + buildings["id"] = "buildingsLayer"; + buildings["source"] = "composite"; + buildings["source-layer"] = "building"; + buildings["type"] = "fill-extrusion"; + buildings["minzoom"] = 15; + m_map->addLayer("buildingsLayer", buildings); + m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"})); + + QVariantList fillExtrusionHeight = { // scale buildings as you zoom in + "interpolate", + QVariantList{"linear"}, + QVariantList{"zoom"}, + 15, 0, + 15.05, QVariantList{"get", "height"} + }; + + QVariantList fillExtrusionBase = { + "interpolate", + QVariantList{"linear"}, + QVariantList{"zoom"}, + 15, 0, + 15.05, QVariantList{"get", "min_height"} + }; + + QVariantList fillExtrusionOpacity = { + "interpolate", + QVariantList{"linear"}, + QVariantList{"zoom"}, + 15, 0, // transparent at zoom level 15 + 15.5, .6, // fade in + 17, .6, // begin fading out + 20, 0 // fade out when zoomed in + }; + + m_map->setPaintProperty("buildingsLayer", "fill-extrusion-color", QColor("grey")); + m_map->setPaintProperty("buildingsLayer", "fill-extrusion-opacity", fillExtrusionOpacity); + m_map->setPaintProperty("buildingsLayer", "fill-extrusion-height", fillExtrusionHeight); + m_map->setPaintProperty("buildingsLayer", "fill-extrusion-base", fillExtrusionBase); + m_map->setLayoutProperty("buildingsLayer", "visibility", "visible"); + } +} + +void MapWindowSP::updateState(const UIStateSP &s) { + if (!uiState()->scene.started) { + return; + } + const SubMaster &sm = *(s.sm); + update(); + + // on rising edge of a valid system time, reinitialize the map to set a new token + if (sm.valid("clocks") && !prev_time_valid) { + LOGW("Time is now valid, reinitializing map"); + m_settings.setApiKey(get_mapbox_token()); + initializeGL(); + } + prev_time_valid = sm.valid("clocks"); + + if (sm.updated("modelV2")) { + // set path color on change, and show map on rising edge of navigate on openpilot + auto car_control = sm["carControl"].getCarControl(); + bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabledDEPRECATED() && + (sm["controlsState"].getControlsState().getEnabled() || car_control.getLatActive() || car_control.getLongActive()); + if (nav_enabled != uiState()->scene.navigate_on_openpilot_deprecated) { + if (loaded_once) { + m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled)); + } + if (nav_enabled) { + emit requestVisible(true); + } + } + uiState()->scene.navigate_on_openpilot_deprecated = nav_enabled; + } + + if (sm.updated("liveLocationKalman")) { + auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman(); + auto locationd_pos = locationd_location.getPositionGeodetic(); + auto locationd_orientation = locationd_location.getCalibratedOrientationNED(); + auto locationd_velocity = locationd_location.getVelocityCalibrated(); + auto locationd_ecef = locationd_location.getPositionECEF(); + + locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && locationd_ecef.getValid()); + if (locationd_valid) { + // Check std norm + auto pos_ecef_std = locationd_ecef.getStd(); + bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100; + locationd_valid = pos_accurate_enough; + } + + if (locationd_valid) { + last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]); + last_bearing = RAD2DEG(locationd_orientation.getValue()[2]); + velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0])); + } + } + + if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) { + auto nav_dest = coordinate_from_param("NavDestination"); + bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest && + nav_dest && !isVisible(); + qWarning() << "Got new navRoute from navd. Opening map:" << allow_open; + + // Show map on destination set/change + if (allow_open) { + emit requestSettings(false); + emit requestVisible(true); + } + } + + loaded_once = loaded_once || (m_map && m_map->isFullyLoaded()); + if (!loaded_once) { + setError(tr("Map Loading")); + return; + } + initLayers(); + + if (!locationd_valid) { + setError(tr("Waiting for GPS")); + } else if (routing_problem) { + setError(tr("Waiting for route")); + } else { + setError(""); + } + + if (locationd_valid) { + // Update current location marker + auto point = coordinate_to_collection(*last_position); + QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {}); + QVariantMap carPosSource; + carPosSource["type"] = "geojson"; + carPosSource["data"] = QVariant::fromValue(feature1); + m_map->updateSource("carPosSource", carPosSource); + + // Map bearing isn't updated when interacting, keep location marker up to date + if (last_bearing) { + m_map->setLayoutProperty("carPosLayer", "icon-rotate", *last_bearing - m_map->bearing()); + } + } + + if (interaction_counter == 0) { + if (last_position) m_map->setCoordinate(*last_position); + if (last_bearing) m_map->setBearing(*last_bearing); + m_map->setZoom(util::map_val(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM)); + } else { + interaction_counter--; + } + + if (sm.updated("navInstruction")) { + // an invalid navInstruction packet with a nav destination is only possible if: + // - API exception/no internet + // - route response is empty + // - any time navd is waiting for recompute_countdown + routing_problem = !sm.valid("navInstruction") && coordinate_from_param("NavDestination").has_value(); + + if (sm.valid("navInstruction")) { + auto i = sm["navInstruction"].getNavInstruction(); + map_eta->updateETA(i.getTimeRemaining(), i.getTimeRemainingTypical(), i.getDistanceRemaining()); + + if (locationd_valid) { + m_map->setPitch(MAX_PITCH); // TODO: smooth pitching based on maneuver distance + map_instructions->updateInstructions(i); + } + } else { + clearRoute(); + } + } + + if (sm.rcv_frame("navRoute") != route_rcv_frame) { + qWarning() << "Updating navLayer with new route"; + auto route = sm["navRoute"].getNavRoute(); + auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates()); + QMapLibre::Feature feature(QMapLibre::Feature::LineStringType, route_points, {}, {}); + QVariantMap navSource; + navSource["type"] = "geojson"; + navSource["data"] = QVariant::fromValue(feature); + m_map->updateSource("navSource", navSource); + m_map->setLayoutProperty("navLayer", "visibility", "visible"); + + route_rcv_frame = sm.rcv_frame("navRoute"); + updateDestinationMarker(); + } +} + +void MapWindowSP::offroadTransition(bool offroad) { + if (offroad) { + uiStateSP()->scene.navigate_on_openpilot_deprecated = false; + } + + MapWindow::offroadTransition(offroad); +} diff --git a/selfdrive/ui/sunnypilot/qt/maps/map.h b/selfdrive/ui/sunnypilot/qt/maps/map.h new file mode 100644 index 0000000000..00c20e2ae7 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/maps/map.h @@ -0,0 +1,53 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/maps/map.h" +#include "selfdrive/ui/sunnypilot/ui.h" + +#include + +class MapWindowSP : public MapWindow { + Q_OBJECT + +public: + explicit MapWindowSP(const QMapLibre::Settings &); + ~MapWindowSP(); + +private: + void initLayers(); + // Blue with normal nav, green when nav is input into the model + QColor getNavPathColor(bool nav_enabled) { + return nav_enabled ? QColor("#31ee73") : QColor("#31a1ee"); + } + +protected slots: + void updateState(const UIStateSP &s); + +public slots: + void offroadTransition(bool offroad); +}; diff --git a/selfdrive/ui/sunnypilot/qt/maps/map_helpers.h b/selfdrive/ui/sunnypilot/qt/maps/map_helpers.h new file mode 100644 index 0000000000..464b35995c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/maps/map_helpers.h @@ -0,0 +1,38 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/maps/map_helpers.h" + +#define MAPBOX_TOKEN MAPBOX_TOKEN_SP +#define MAPS_HOST MAPS_HOST_SP + +#include "common/params.h" + +const QString MAPBOX_TOKEN = QString::fromStdString(Params().get("CustomMapboxTokenSk")) != "" ? + QString::fromStdString(Params().get("CustomMapboxTokenSk")) : util::getenv("MAPBOX_TOKEN").c_str(); +const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str(); diff --git a/selfdrive/ui/sunnypilot/qt/network/networking.cc b/selfdrive/ui/sunnypilot/qt/network/networking.cc new file mode 100644 index 0000000000..bbe2cfb79c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/networking.cc @@ -0,0 +1,448 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/network/networking.h" + +#include + +#include +#include +#include +#include "selfdrive/ui/qt/widgets/ssh_keys.h" + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +static const int ICON_WIDTH = 49; + +// Networking functions + +NetworkingSP::NetworkingSP(QWidget* parent, bool show_advanced) : QFrame(parent) { + main_layout = new QStackedLayout(this); + + wifi = new WifiManager(this); + connect(wifi, &WifiManager::refreshSignal, this, &NetworkingSP::refresh); + connect(wifi, &WifiManager::wrongPassword, this, &NetworkingSP::wrongPassword); + + wifiScreen = new QWidget(this); + QVBoxLayout* vlayout = new QVBoxLayout(wifiScreen); + vlayout->setContentsMargins(20, 20, 20, 20); + QHBoxLayout* hlayout = new QHBoxLayout(); + QPushButton* scanButton = new QPushButton(tr("Scan")); + scanButton->setObjectName("scan_btn"); + scanButton->setFixedSize(400, 100); + connect(wifi, &WifiManager::refreshSignal, this, [=]() { scanButton->setText(tr("Scan")); scanButton->setEnabled(true); }); + connect(scanButton, &QPushButton::clicked, [=]() { scanButton->setText(tr("Scanning...")); scanButton->setEnabled(false); wifi->requestScan(); }); + + hlayout->addWidget(scanButton); + hlayout->addStretch(1); // Pushes the button all the way to the left + + if (show_advanced) { + hlayout->setSpacing(10); + + QPushButton* advancedSettings = new QPushButton(tr("Advanced")); + advancedSettings->setObjectName("advanced_btn"); + advancedSettings->setFixedSize(400, 100); + connect(advancedSettings, &QPushButton::clicked, [=]() { main_layout->setCurrentWidget(an); }); + hlayout->addWidget(advancedSettings); + } + + vlayout->addLayout(hlayout); + vlayout->addSpacing(10); + + wifiWidget = new WifiUISP(this, wifi); + wifiWidget->setObjectName("wifiWidget"); + connect(wifiWidget, &WifiUISP::connectToNetwork, this, &NetworkingSP::connectToNetwork); + + ScrollView *wifiScroller = new ScrollView(wifiWidget, this); + wifiScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + vlayout->addWidget(wifiScroller, 1); + main_layout->addWidget(wifiScreen); + + an = new AdvancedNetworkingSP(this, wifi); + connect(an, &AdvancedNetworkingSP::backPress, [=]() { main_layout->setCurrentWidget(wifiScreen); }); + connect(an, &AdvancedNetworkingSP::requestWifiScreen, [=]() { main_layout->setCurrentWidget(wifiScreen); }); + main_layout->addWidget(an); + + QPalette pal = palette(); + pal.setColor(QPalette::Window, QColor(0x29, 0x29, 0x29)); + setAutoFillBackground(true); + setPalette(pal); + + setStyleSheet(R"( + #wifiWidget > QPushButton, #back_btn, #advanced_btn, #scan_btn{ + font-size: 50px; + margin: 0px; + padding: 15px; + border-width: 0; + border-radius: 30px; + color: #dddddd; + background-color: #393939; + } + #back_btn:pressed, #advanced_btn:pressed, #scan_btn:pressed { + background-color: #4a4a4a; + } + )"); + main_layout->setCurrentWidget(wifiScreen); +} + +void NetworkingSP::refresh() { + wifiWidget->refresh(); + an->refresh(); +} + +void NetworkingSP::connectToNetwork(const Network n) { + if (wifi->isKnownConnection(n.ssid)) { + wifi->activateWifiConnection(n.ssid); + } else if (n.security_type == SecurityType::OPEN) { + wifi->connect(n, false); + } else if (n.security_type == SecurityType::WPA) { + QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); + if (!pass.isEmpty()) { + wifi->connect(n, false, pass); + } + } +} + +void NetworkingSP::wrongPassword(const QString &ssid) { + if (wifi->seenNetworks.contains(ssid)) { + const Network &n = wifi->seenNetworks.value(ssid); + QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); + if (!pass.isEmpty()) { + wifi->connect(n, false, pass); + } + } +} + +void NetworkingSP::showEvent(QShowEvent *event) { + wifi->start(); +} + +void NetworkingSP::hideEvent(QHideEvent *event) { + main_layout->setCurrentWidget(wifiScreen); + wifi->stop(); +} + +// AdvancedNetworkingSP functions + +AdvancedNetworkingSP::AdvancedNetworkingSP(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi) { + + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setMargin(40); + main_layout->setSpacing(20); + + // Back button + QPushButton* back = new QPushButton(tr("Back")); + back->setObjectName("back_btn"); + back->setFixedSize(400, 100); + connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); + main_layout->addWidget(back, 0, Qt::AlignLeft); + + ListWidgetSP *list = new ListWidgetSP(this); + // Enable tethering layout + const bool set_hotspot_on_boot = params.getBool("HotspotOnBoot") && params.getBool("HotspotOnBootConfirmed"); + tetheringToggle = new ToggleControlSP(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled() || set_hotspot_on_boot); + list->addItem(tetheringToggle); + QObject::connect(tetheringToggle, &ToggleControlSP::toggleFlipped, this, &AdvancedNetworkingSP::toggleTethering); + + hotspotOnBootToggle = new ToggleControlSP( + tr("Retain hotspot/tethering state"), + tr("Enabling this toggle will retain the hotspot/tethering toggle state across reboots."), + "", + params.getBool("HotspotOnBoot") + ); + hotspotOnBootToggle->setEnabled(wifi->isTetheringEnabled() || set_hotspot_on_boot); + QObject::connect(hotspotOnBootToggle, &ToggleControlSP::toggleFlipped, [=](bool state) { + params.putBool("HotspotOnBoot", state); + }); + list->addItem(hotspotOnBootToggle); + + // Change tethering password + ButtonControlSP *editPasswordButton = new ButtonControlSP(tr("Tethering Password"), tr("EDIT")); + connect(editPasswordButton, &ButtonControlSP::clicked, [=]() { + QString pass = InputDialog::getText(tr("Enter new tethering password"), this, "", true, 8, wifi->getTetheringPassword()); + if (!pass.isEmpty()) { + wifi->changeTetheringPassword(pass); + } + }); + list->addItem(editPasswordButton); + + // IP address + ipLabel = new LabelControlSP(tr("IP Address"), wifi->ipv4_address); + list->addItem(ipLabel); + + // SSH keys + list->addItem(new SshToggle()); + list->addItem(new SshControl()); + + // Roaming toggle + const bool roamingEnabled = params.getBool("GsmRoaming"); + roamingToggle = new ToggleControlSP(tr("Enable Roaming"), "", "", roamingEnabled); + QObject::connect(roamingToggle, &ToggleControlSP::toggleFlipped, [=](bool state) { + params.putBool("GsmRoaming", state); + wifi->updateGsmSettings(state, QString::fromStdString(params.get("GsmApn")), params.getBool("GsmMetered")); + }); + list->addItem(roamingToggle); + + // APN settings + editApnButton = new ButtonControlSP(tr("APN Setting"), tr("EDIT")); + connect(editApnButton, &ButtonControlSP::clicked, [=]() { + const QString cur_apn = QString::fromStdString(params.get("GsmApn")); + QString apn = InputDialog::getText(tr("Enter APN"), this, tr("leave blank for automatic configuration"), false, -1, cur_apn).trimmed(); + + if (apn.isEmpty()) { + params.remove("GsmApn"); + } else { + params.put("GsmApn", apn.toStdString()); + } + wifi->updateGsmSettings(params.getBool("GsmRoaming"), apn, params.getBool("GsmMetered")); + }); + list->addItem(editApnButton); + + // Metered toggle + const bool metered = params.getBool("GsmMetered"); + meteredToggle = new ToggleControlSP(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered connection"), "", metered); + QObject::connect(meteredToggle, &SshToggle::toggleFlipped, [=](bool state) { + params.putBool("GsmMetered", state); + wifi->updateGsmSettings(params.getBool("GsmRoaming"), QString::fromStdString(params.get("GsmApn")), state); + }); + list->addItem(meteredToggle); + + // Hidden Network + hiddenNetworkButton = new ButtonControlSP(tr("Hidden Network"), tr("CONNECT")); + connect(hiddenNetworkButton, &ButtonControlSP::clicked, [=]() { + QString ssid = InputDialog::getText(tr("Enter SSID"), this, "", false, 1); + if (!ssid.isEmpty()) { + QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(ssid), true, -1); + Network hidden_network; + hidden_network.ssid = ssid.toUtf8(); + if (!pass.isEmpty()) { + hidden_network.security_type = SecurityType::WPA; + wifi->connect(hidden_network, true, pass); + } else { + wifi->connect(hidden_network, true); + } + emit requestWifiScreen(); + } + }); + list->addItem(hiddenNetworkButton); + + // Ngrok + QProcess process; + process.start("sudo service ngrok status | grep running"); + process.waitForFinished(); + QString output = QString(process.readAllStandardOutput()); + bool ngrokRunning = !output.isEmpty(); + ToggleControlSP *ngrokToggle = new ToggleControlSP(tr("Ngrok Service"), "", "", ngrokRunning); + connect(ngrokToggle, &ToggleControlSP::toggleFlipped, [=](bool state) { + if (state) std::system("sudo ngrok service start"); + else std::system("sudo ngrok service stop"); + }); + list->addItem(ngrokToggle); + + // Set initial config + wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered); + + connect(uiState(), &UIState::primeTypeChanged, this, [=](PrimeType prime_type) { + bool gsmVisible = prime_type == PrimeType::NONE || prime_type == PrimeType::LITE; + roamingToggle->setVisible(gsmVisible); + editApnButton->setVisible(gsmVisible); + meteredToggle->setVisible(gsmVisible); + }); + + main_layout->addWidget(new ScrollView(list, this)); + main_layout->addStretch(1); +} + +void AdvancedNetworkingSP::refresh() { + ipLabel->setText(wifi->ipv4_address); + tetheringToggle->setEnabled(true); + update(); +} + +void AdvancedNetworkingSP::toggleTethering(bool enabled) { + params.putBool("HotspotOnBootConfirmed", enabled); + wifi->setTetheringEnabled(enabled); + tetheringToggle->setEnabled(false); + + hotspotOnBootToggle->setEnabled(enabled); + if (!enabled) { + params.remove("HotspotOnBoot"); + } +} + +// WifiUISP functions + +WifiUISP::WifiUISP(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + main_layout->setSpacing(0); + + // load imgs + for (const auto &s : {"low", "medium", "high", "full"}) { + QPixmap pix(ASSET_PATH + "/offroad/icon_wifi_strength_" + s + ".svg"); + strengths.push_back(pix.scaledToHeight(68, Qt::SmoothTransformation)); + } + lock = QPixmap(ASSET_PATH + "offroad/icon_lock_closed.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); + checkmark = QPixmap(ASSET_PATH + "offroad/icon_checkmark.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); + circled_slash = QPixmap(ASSET_PATH + "img_circled_slash.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); + + scanningLabel = new QLabel(tr("Scanning for networks...")); + scanningLabel->setStyleSheet("font-size: 65px;"); + main_layout->addWidget(scanningLabel, 0, Qt::AlignCenter); + + wifi_list_widget = new ListWidgetSP(this); + wifi_list_widget->setVisible(false); + main_layout->addWidget(wifi_list_widget); + + setStyleSheet(R"( + QScrollBar::handle:vertical { + min-height: 0px; + border-radius: 4px; + background-color: #8A8A8A; + } + #forgetBtn { + font-size: 32px; + font-weight: 600; + color: #292929; + background-color: #BDBDBD; + border-width: 1px solid #828282; + border-radius: 5px; + padding: 40px; + padding-bottom: 16px; + padding-top: 16px; + } + #forgetBtn:pressed { + background-color: #828282; + } + #connecting { + font-size: 32px; + font-weight: 600; + color: white; + border-radius: 0; + padding: 27px; + padding-left: 43px; + padding-right: 43px; + background-color: black; + } + #ssidLabel { + text-align: left; + border: none; + padding-top: 50px; + padding-bottom: 50px; + } + #ssidLabel:disabled { + color: #696969; + } + )"); +} + +void WifiUISP::refresh() { + bool is_empty = wifi->seenNetworks.isEmpty(); + scanningLabel->setVisible(is_empty); + wifi_list_widget->setVisible(!is_empty); + if (is_empty) return; + + setUpdatesEnabled(false); + + const bool is_tethering_enabled = wifi->isTetheringEnabled(); + QList sortedNetworks = wifi->seenNetworks.values(); + std::sort(sortedNetworks.begin(), sortedNetworks.end(), compare_by_strength); + + int n = 0; + for (Network &network : sortedNetworks) { + QPixmap status_icon; + if (network.connected == ConnectedType::CONNECTED) { + status_icon = checkmark; + } else if (network.security_type == SecurityType::UNSUPPORTED) { + status_icon = circled_slash; + } else if (network.security_type == SecurityType::WPA) { + status_icon = lock; + } + bool show_forget_btn = wifi->isKnownConnection(network.ssid) && !is_tethering_enabled; + QPixmap strength = strengths[strengthLevel(network.strength)]; + + auto item = getItem(n++); + item->setItem(network, status_icon, show_forget_btn, strength); + item->setVisible(true); + } + for (; n < wifi_items.size(); ++n) wifi_items[n]->setVisible(false); + + setUpdatesEnabled(true); +} + +WifiItemSP *WifiUISP::getItem(int n) { + auto item = n < wifi_items.size() ? wifi_items[n] : wifi_items.emplace_back(new WifiItemSP(tr("CONNECTING..."), tr("FORGET"))); + if (!item->parentWidget()) { + QObject::connect(item, &WifiItemSP::connectToNetwork, this, &WifiUISP::connectToNetwork); + QObject::connect(item, &WifiItemSP::forgotNetwork, [this](const Network n) { + if (ConfirmationDialog::confirm(tr("Forget Wi-Fi Network \"%1\"?").arg(QString::fromUtf8(n.ssid)), tr("Forget"), this)) + wifi->forgetConnection(n.ssid); + }); + wifi_list_widget->addItem(item); + } + return item; +} + +// WifiItemSP + +WifiItemSP::WifiItemSP(const QString &connecting_text, const QString &forget_text, QWidget *parent) : QWidget(parent) { + QHBoxLayout *hlayout = new QHBoxLayout(this); + hlayout->setContentsMargins(44, 0, 73, 0); + hlayout->setSpacing(50); + + hlayout->addWidget(ssidLabel = new ElidedLabelSP()); + ssidLabel->setObjectName("ssidLabel"); + ssidLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + hlayout->addWidget(connecting = new QPushButton(connecting_text), 0, Qt::AlignRight); + connecting->setObjectName("connecting"); + hlayout->addWidget(forgetBtn = new QPushButton(forget_text), 0, Qt::AlignRight); + forgetBtn->setObjectName("forgetBtn"); + hlayout->addWidget(iconLabel = new QLabel(), 0, Qt::AlignRight); + hlayout->addWidget(strengthLabel = new QLabel(), 0, Qt::AlignRight); + + iconLabel->setFixedWidth(ICON_WIDTH); + QObject::connect(forgetBtn, &QPushButton::clicked, [this]() { emit forgotNetwork(network); }); + QObject::connect(ssidLabel, &ElidedLabelSP::clicked, [this]() { + if (network.connected == ConnectedType::DISCONNECTED) emit connectToNetwork(network); + }); +} + +void WifiItemSP::setItem(const Network &n, const QPixmap &status_icon, bool show_forget_btn, const QPixmap &strength_icon) { + network = n; + + ssidLabel->setText(n.ssid); + ssidLabel->setEnabled(n.security_type != SecurityType::UNSUPPORTED); + ssidLabel->setFont(InterFont(55, network.connected == ConnectedType::DISCONNECTED ? QFont::Normal : QFont::Bold)); + + connecting->setVisible(n.connected == ConnectedType::CONNECTING); + forgetBtn->setVisible(show_forget_btn); + + iconLabel->setPixmap(status_icon); + strengthLabel->setPixmap(strength_icon); +} diff --git a/selfdrive/ui/sunnypilot/qt/network/networking.h b/selfdrive/ui/sunnypilot/qt/network/networking.h new file mode 100644 index 0000000000..931fe3443e --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/networking.h @@ -0,0 +1,128 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/qt/network/wifi_manager.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class WifiItemSP : public QWidget { + Q_OBJECT +public: + explicit WifiItemSP(const QString &connecting_text, const QString &forget_text, QWidget* parent = nullptr); + void setItem(const Network& n, const QPixmap &icon, bool show_forget_btn, const QPixmap &strength); + +signals: + // Cannot pass Network by reference. it may change after the signal is sent. + void connectToNetwork(const Network n); + void forgotNetwork(const Network n); + +protected: + ElidedLabelSP* ssidLabel; + QPushButton* connecting; + QPushButton* forgetBtn; + QLabel* iconLabel; + QLabel* strengthLabel; + Network network; +}; + +class WifiUISP : public QWidget { + Q_OBJECT + +public: + explicit WifiUISP(QWidget *parent = 0, WifiManager* wifi = 0); + +private: + WifiItemSP *getItem(int n); + + WifiManager *wifi = nullptr; + QLabel *scanningLabel = nullptr; + QPixmap lock; + QPixmap checkmark; + QPixmap circled_slash; + QVector strengths; + ListWidgetSP *wifi_list_widget = nullptr; + std::vector wifi_items; + +signals: + void connectToNetwork(const Network n); + +public slots: + void refresh(); +}; + +class AdvancedNetworkingSP : public QWidget { + Q_OBJECT +public: + explicit AdvancedNetworkingSP(QWidget* parent = 0, WifiManager* wifi = 0); + +private: + LabelControlSP* ipLabel; + ToggleControlSP* tetheringToggle; + ToggleControlSP* roamingToggle; + ButtonControlSP* editApnButton; + ButtonControlSP* hiddenNetworkButton; + ToggleControlSP* meteredToggle; + WifiManager* wifi = nullptr; + Params params; + + ToggleControlSP* hotspotOnBootToggle; + +signals: + void backPress(); + void requestWifiScreen(); + +public slots: + void toggleTethering(bool enabled); + void refresh(); +}; + +class NetworkingSP : public QFrame { + Q_OBJECT + +public: + explicit NetworkingSP(QWidget* parent = 0, bool show_advanced = true); + WifiManager* wifi = nullptr; + +private: + QStackedLayout* main_layout = nullptr; + QWidget* wifiScreen = nullptr; + AdvancedNetworkingSP* an = nullptr; + WifiUISP* wifiWidget; + + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; + +public slots: + void refresh(); + +private slots: + void connectToNetwork(const Network n); + void wrongPassword(const QString &ssid); +}; diff --git a/selfdrive/ui/qt/network/sunnylink/models/role_model.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h similarity index 53% rename from selfdrive/ui/qt/network/sunnylink/models/role_model.h rename to selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h index d7f3a98281..c1e28a5f9a 100644 --- a/selfdrive/ui/qt/network/sunnylink/models/role_model.h +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h @@ -1,5 +1,30 @@ -#ifndef ROLE_MODEL_H -#define ROLE_MODEL_H +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once #include @@ -56,5 +81,3 @@ public: return T(m_raw_json_object); } }; - -#endif diff --git a/selfdrive/ui/qt/network/sunnylink/models/sponsor_role_model.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h similarity index 67% rename from selfdrive/ui/qt/network/sunnylink/models/sponsor_role_model.h rename to selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h index 64aad1ca46..6b6470f0cd 100644 --- a/selfdrive/ui/qt/network/sunnylink/models/sponsor_role_model.h +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h @@ -1,5 +1,30 @@ -#ifndef SPONSORROLE_MODEL_H -#define SPONSORROLE_MODEL_H +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once #include @@ -24,7 +49,7 @@ public: QJsonObject json = RoleModel::toJson(); json["role_tier"] = sponsorTierToString(roleTier); return json; - }; + } static SponsorTier stringToSponsorTier(const QString &sponsorTierString) { const auto sponsorTierStringLower = sponsorTierString.toLower(); @@ -80,5 +105,3 @@ public: } [[nodiscard]] auto getSponsorTierColor() const { return sponsorTierToColor(roleTier); } }; - -#endif diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h new file mode 100644 index 0000000000..259d4b39b9 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h @@ -0,0 +1,56 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +class UserModel { +public: + QString device_id; + QString user_id; + qint64 created_at; + qint64 updated_at; + QString token_hash; + + explicit UserModel(const QJsonObject &json) { + device_id = json["device_id"].toString(); + user_id = json["user_id"].toString(); + created_at = json["created_at"].toInt(); + updated_at = json["updated_at"].toInt(); + token_hash = json["token_hash"].toString(); + } + + [[nodiscard]] QJsonObject toJson() const { + QJsonObject json; + json["device_id"] = device_id; + json["user_id"] = user_id; + json["created_at"] = created_at; + json["updated_at"] = updated_at; + json["token_hash"] = token_hash; + return json; + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc new file mode 100644 index 0000000000..13764c5749 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc @@ -0,0 +1,76 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink//services/base_device_service.h" + +#include "selfdrive/ui/sunnypilot/qt/request_repeater.h" + +#include "common/swaglog.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.h" + +BaseDeviceService::BaseDeviceService(QObject* parent) : QObject(parent), initial_request(nullptr), repeater(nullptr) { + param_watcher = new ParamWatcher(this); + connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { + paramsRefresh(); + }); + param_watcher->addParam("SunnylinkEnabled"); +} + +void BaseDeviceService::paramsRefresh() { +} + +void BaseDeviceService::loadDeviceData(const QString &url, bool poll) { + if (!is_sunnylink_enabled()) { + LOGW("Sunnylink is not enabled, refusing to load data."); + return; + } + + auto sl_dongle_id = getSunnylinkDongleId(); + if (!sl_dongle_id.has_value()) + return; + + QString fullUrl = SUNNYLINK_BASE_URL + "/device/" + *sl_dongle_id + url; + if (poll && !isCurrentyPolling()) { + LOGD("Polling %s", qPrintable(fullUrl)); + LOGD("Cache key: SunnylinkCache_%s", qPrintable(QString(getCacheKey()))); + repeater = new RequestRepeaterSP(this, fullUrl, "SunnylinkCache_" + getCacheKey(), 60, false, true); + connect(repeater, &RequestRepeaterSP::requestDone, this, &BaseDeviceService::handleResponse); + } else if (isCurrentyPolling()) { + repeater->ForceUpdate(); + } else { + LOGD("Sending one-time %s", qPrintable(fullUrl)); + initial_request = new HttpRequestSP(this, true, 10000, true); + connect(initial_request, &HttpRequestSP::requestDone, this, &BaseDeviceService::handleResponse); + } +} + +void BaseDeviceService::stopPolling() { + if (repeater != nullptr) { + repeater->deleteLater(); + repeater = nullptr; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h new file mode 100644 index 0000000000..dced4edb52 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h @@ -0,0 +1,50 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/request_repeater.h" +#include "selfdrive/ui/qt/util.h" + +class BaseDeviceService : public QObject { + Q_OBJECT + +protected: + void paramsRefresh(); + void loadDeviceData(const QString &url, bool poll = false); + virtual void handleResponse(const QString &response, bool success) = 0; + + static bool is_sunnylink_enabled() { return Params().getBool("SunnylinkEnabled");} + ParamWatcher* param_watcher; + HttpRequestSP* initial_request = nullptr; + RequestRepeaterSP* repeater = nullptr; + +public: + explicit BaseDeviceService(QObject* parent = nullptr); + virtual QString getCacheKey() const = 0; + bool isCurrentyPolling() {return repeater != nullptr;} + void stopPolling(); +}; diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc new file mode 100644 index 0000000000..c835bf6f33 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc @@ -0,0 +1,55 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h" + +#include +#include + +RoleService::RoleService(QObject* parent) : BaseDeviceService(parent) {} + +void RoleService::load() { + loadDeviceData(url); +} + +void RoleService::startPolling() { + loadDeviceData(url, true); +} + +void RoleService::handleResponse(const QString &response, bool success) { + if (!success) return; + + QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); + QJsonArray jsonArray = doc.array(); + + std::vector roles; + for (const auto &value : jsonArray) { + roles.emplace_back(value.toObject()); + } + + emit rolesReady(roles); + uiStateSP()->setSunnylinkRoles(roles); +} diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h new file mode 100644 index 0000000000..91d59fe6a9 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h @@ -0,0 +1,51 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h" +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h" + +class RoleService : public BaseDeviceService { + Q_OBJECT + +public: + explicit RoleService(QObject* parent = nullptr); + void load(); + void startPolling(); + [[nodiscard]] QString getCacheKey() const final { return "Roles"; } + +signals: + void rolesReady(const std::vector &roles); + +protected: + void handleResponse(const QString&response, bool success) override; + +private: + QString url = "/roles"; +}; diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc new file mode 100644 index 0000000000..c0ce22ac89 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc @@ -0,0 +1,57 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h" + +#include +#include + +UserService::UserService(QObject* parent) : BaseDeviceService(parent) { + url = "/users"; +} + +void UserService::load() { + loadDeviceData(url); +} + +void UserService::startPolling() { + loadDeviceData(url, true); +} + +void UserService::handleResponse(const QString &response, bool success) { + if (!success) return; + + QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); + QJsonArray jsonArray = doc.array(); + + std::vector users; + for (const auto &value : jsonArray) { + users.emplace_back(value.toObject()); + } + + emit usersReady(users); + uiStateSP()->setSunnylinkDeviceUsers(users); +} diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h new file mode 100644 index 0000000000..aaac6c5b37 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h @@ -0,0 +1,51 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h" +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h" + +class UserService : public BaseDeviceService { + Q_OBJECT + +public: + explicit UserService(QObject* parent = nullptr); + void load(); + void startPolling(); + [[nodiscard]] QString getCacheKey() const final { return "Users"; }; + +signals: + void usersReady(const std::vector&users); + +protected: + void handleResponse(const QString&response, bool success) override; + +private: + QString url = "/users"; +}; diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc new file mode 100644 index 0000000000..3217e44dbd --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc @@ -0,0 +1,33 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h" +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h" + +SunnylinkClient::SunnylinkClient(QObject* parent) : QObject(parent) { + role_service = new RoleService(parent); + user_service = new UserService(parent); +} diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h new file mode 100644 index 0000000000..625f99f1f4 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h @@ -0,0 +1,41 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h" +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h" + +class SunnylinkClient : public QObject { + Q_OBJECT + +public: + explicit SunnylinkClient(QObject* parent); + RoleService* role_service; + UserService* user_service; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc new file mode 100644 index 0000000000..99964c4681 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc @@ -0,0 +1,189 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h" + +#include +#include +#include + +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/widgets/prime.h" + +DevicePanelSP::DevicePanelSP(SettingsWindow *parent) : DevicePanel(parent) { + fleetManagerPin = new ButtonControlSP( + pin_title + pin, tr("TOGGLE"), + tr("Enable or disable PIN requirement for Fleet Manager access.")); + connect(fleetManagerPin, &ButtonControlSP::clicked, [=]() { + if (params.getBool("FleetManagerPin")) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to turn off PIN requirement?"), tr("Turn Off"), this)) { + params.remove("FleetManagerPin"); + refreshPin(); + } + } else { + params.putBool("FleetManagerPin", true); + refreshPin(); + } + }); + AddWidgetAt(2, fleetManagerPin); + + fs_watch = new QFileSystemWatcher(this); + connect(fs_watch, &QFileSystemWatcher::fileChanged, this, &DevicePanelSP::onPinFileChanged); + + QString pin_path = "/data/otp/otp.conf"; + QString pin_require = "/data/params/d/FleetManagerPin"; + fs_watch->addPath(pin_path); + fs_watch->addPath(pin_require); + refreshPin(); + + // Error Troubleshoot + auto errorBtn = new ButtonControlSP( + tr("Error Troubleshoot"), tr("VIEW"), + tr("Display error from the tmux session when an error has occurred from a system process.")); + QFileInfo file("/data/community/crashes/error.txt"); + QDateTime modifiedTime = file.lastModified(); + QString modified_time = modifiedTime.toString("yyyy-MM-dd hh:mm:ss "); + connect(errorBtn, &ButtonControlSP::clicked, [=]() { + const std::string txt = util::read_file("/data/community/crashes/error.txt"); + ConfirmationDialog::rich(modified_time + QString::fromStdString(txt), this); + }); + AddWidgetAt(3, errorBtn); + + + auto resetMapboxTokenBtn = new ButtonControlSP(tr("Reset Access Tokens for Map Services"), tr("RESET"), tr("Reset self-service access tokens for Mapbox, Amap, and Google Maps.")); + connect(resetMapboxTokenBtn, &ButtonControlSP::clicked, [=]() { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reset access tokens for all map services?"), tr("Reset"), this)) { + std::vector tokens = { + "CustomMapboxTokenPk", + "CustomMapboxTokenSk", + "AmapKey1", + "AmapKey2", + "GmapKey" + }; + for (const auto& token : tokens) { + params.remove(token); + } + } + }); + AddWidgetAt(6, resetMapboxTokenBtn); + + auto resetParamsBtn = new ButtonControlSP(tr("Reset sunnypilot Settings"), tr("RESET"), ""); + connect(resetParamsBtn, &ButtonControlSP::clicked, [=]() { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reset all sunnypilot settings?"), tr("Reset"), this)) { + std::system("sudo rm -rf /data/params/d/*"); + Hardware::reboot(); + } + }); + AddWidgetAt(6, resetParamsBtn); + + QObject::connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { + for (auto btn : findChildren()) { + if (btn != pair_device && btn != errorBtn) { + btn->setEnabled(offroad); + } + } + }); + + offroad_btn = new QPushButton(tr("Toggle Onroad/Offroad")); + offroad_btn->setObjectName("offroad_btn"); + QObject::connect(offroad_btn, &QPushButton::clicked, this, &DevicePanelSP::forceoffroad); + + QVBoxLayout *buttons_layout = new QVBoxLayout(); + buttons_layout->setSpacing(24); + buttons_layout->addLayout(power_layout); + buttons_layout->addWidget(offroad_btn); + addItem(buttons_layout); + + updateLabels(); +} + +void DevicePanelSP::onPinFileChanged(const QString &file_path) { + if (file_path == "/data/params/d/FleetManagerPin") { + refreshPin(); + } else if (file_path == "/data/otp/otp.conf") { + refreshPin(); + } +} + +void DevicePanelSP::refreshPin() { + QFile f("/data/otp/otp.conf"); + QFile require("/data/params/d/FleetManagerPin"); + if (!require.exists()) { + setSpacing(50); + fleetManagerPin->setTitle(pin_title + tr("OFF")); + } else if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + pin = f.readAll(); + f.close(); + setSpacing(50); + fleetManagerPin->setTitle(pin_title + pin); + } +} + +void DevicePanelSP::forceoffroad() { + if (!uiStateSP()->engaged()) { + if (params.getBool("ForceOffroad")) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to unforce offroad?"), tr("Unforce"), this)) { + // Check engaged again in case it changed while the dialog was open + if (!uiStateSP()->engaged()) { + params.remove("ForceOffroad"); + } + } + } else { + if (ConfirmationDialog::confirm(tr("Are you sure you want to force offroad?"), tr("Force"), this)) { + // Check engaged again in case it changed while the dialog was open + if (!uiStateSP()->engaged()) { + params.putBool("ForceOffroad", true); + } + } + } + } else { + ConfirmationDialog::alert(tr("Disengage to Force Offroad"), this); + } + + updateLabels(); +} + +void DevicePanelSP::showEvent(QShowEvent *event) { + DevicePanel::showEvent(event); + updateLabels(); +} + +void DevicePanelSP::updateLabels() { + if (!isVisible()) { + return; + } + + bool force_offroad_param = params.getBool("ForceOffroad"); + QString offroad_btn_style = force_offroad_param ? "#393939" : "#E22C2C"; + QString offroad_btn_pressed_style = force_offroad_param ? "#4a4a4a" : "#FF2424"; + QString btn_common_style = QString("QPushButton { height: 120px; border-radius: 15px; background-color: %1; }" + "QPushButton:pressed { background-color: %2; }") + .arg(offroad_btn_style, + offroad_btn_pressed_style); + + offroad_btn->setText(force_offroad_param ? tr("Unforce Offroad") : tr("Force Offroad")); + offroad_btn->setStyleSheet(btn_common_style + offroad_btn_style + offroad_btn_pressed_style); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h new file mode 100644 index 0000000000..14319612b2 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h @@ -0,0 +1,52 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +class DevicePanelSP : public DevicePanel { + Q_OBJECT + +public: + explicit DevicePanelSP(SettingsWindow *parent); + void showEvent(QShowEvent *event) override; + +private slots: + void onPinFileChanged(const QString &file_path); + void refreshPin(); + void forceoffroad(); + + void updateLabels(); + +private: + ButtonControlSP *fleetManagerPin; + QString pin_title = tr("Fleet Manager PIN:") + " "; + QString pin = "OFF"; + QFileSystemWatcher *fs_watch; + + QPushButton *offroad_btn; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.cc similarity index 71% rename from selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.cc index 5d11c8526c..9000c87910 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.cc @@ -1,6 +1,35 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/display_settings.h" +/** +The MIT License -DisplayPanel::DisplayPanel(QWidget *parent) : ListWidget(parent, false) { +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.h" + +#include +#include + +DisplayPanel::DisplayPanel(QWidget *parent) : ListWidgetSP(parent, false) { // param, title, desc, icon std::vector> toggle_defs{ { @@ -18,27 +47,27 @@ DisplayPanel::DisplayPanel(QWidget *parent) : ListWidget(parent, false) { // General: Max Time Offroad (Shutdown timer) auto max_time_offroad = new MaxTimeOffroad(); - connect(max_time_offroad, &SPOptionControl::updateLabels, max_time_offroad, &MaxTimeOffroad::refresh); + connect(max_time_offroad, &OptionControlSP::updateLabels, max_time_offroad, &MaxTimeOffroad::refresh); addItem(max_time_offroad); // General: Onroad Screen Off (Auto Onroad Screen Timer) onroad_screen_off = new OnroadScreenOff(); onroad_screen_off->setUpdateOtherToggles(true); - connect(onroad_screen_off, &SPOptionControl::updateLabels, onroad_screen_off, &OnroadScreenOff::refresh); - connect(onroad_screen_off, &SPOptionControl::updateOtherToggles, this, &DisplayPanel::updateToggles); + connect(onroad_screen_off, &OptionControlSP::updateLabels, onroad_screen_off, &OnroadScreenOff::refresh); + connect(onroad_screen_off, &OptionControlSP::updateOtherToggles, this, &DisplayPanel::updateToggles); addItem(onroad_screen_off); // General: Onroad Screen Off Brightness onroad_screen_off_brightness = new OnroadScreenOffBrightness(); - connect(onroad_screen_off_brightness, &SPOptionControl::updateLabels, onroad_screen_off_brightness, &OnroadScreenOffBrightness::refresh); + connect(onroad_screen_off_brightness, &OptionControlSP::updateLabels, onroad_screen_off_brightness, &OnroadScreenOffBrightness::refresh); addItem(onroad_screen_off_brightness); // General: Brightness Control (Global) auto brightness_control = new BrightnessControl(); - connect(brightness_control, &SPOptionControl::updateLabels, brightness_control, &BrightnessControl::refresh); + connect(brightness_control, &OptionControlSP::updateLabels, brightness_control, &BrightnessControl::refresh); for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); + auto toggle = new ParamControlSP(param, title, desc, icon, this); addItem(toggle); toggles[param.toStdString()] = toggle; @@ -65,7 +94,7 @@ void DisplayPanel::updateToggles() { } // Max Time Offroad (Shutdown timer) -MaxTimeOffroad::MaxTimeOffroad() : SPOptionControl ( +MaxTimeOffroad::MaxTimeOffroad() : OptionControlSP( "MaxTimeOffroad", tr("Max Time Offroad"), tr("Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road)."), @@ -110,7 +139,7 @@ void MaxTimeOffroad::refresh() { } // Onroad Screen Off (Auto Onroad Screen Timer) -OnroadScreenOff::OnroadScreenOff() : SPOptionControl ( +OnroadScreenOff::OnroadScreenOff() : OptionControlSP( "OnroadScreenOff", tr("Driving Screen Off Timer"), tr("Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs."), @@ -136,7 +165,7 @@ void OnroadScreenOff::refresh() { } // Onroad Screen Off Brightness -OnroadScreenOffBrightness::OnroadScreenOffBrightness() : SPOptionControl ( +OnroadScreenOffBrightness::OnroadScreenOffBrightness() : OptionControlSP( "OnroadScreenOffBrightness", tr("Driving Screen Off Brightness (%)"), tr("When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio."), @@ -157,7 +186,7 @@ void OnroadScreenOffBrightness::refresh() { } // Brightness Control (Global) -BrightnessControl::BrightnessControl() : SPOptionControl ( +BrightnessControl::BrightnessControl() : OptionControlSP( "BrightnessControl", tr("Brightness"), tr("Manually adjusts the global brightness of the screen."), diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.h new file mode 100644 index 0000000000..5298b66fd6 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.h @@ -0,0 +1,98 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class OnroadScreenOff : public OptionControlSP { + Q_OBJECT + +public: + OnroadScreenOff(); + + void refresh(); + +private: + Params params; +}; + +class OnroadScreenOffBrightness : public OptionControlSP { + Q_OBJECT + +public: + OnroadScreenOffBrightness(); + + void refresh(); + +private: + Params params; +}; + +class MaxTimeOffroad : public OptionControlSP { + Q_OBJECT + +public: + MaxTimeOffroad(); + + void refresh(); + +private: + Params params; +}; + +class BrightnessControl : public OptionControlSP { + Q_OBJECT + +public: + BrightnessControl(); + + void refresh(); + +private: + Params params; +}; + +class DisplayPanel : public ListWidgetSP { + Q_OBJECT + +public: + explicit DisplayPanel(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + +public slots: + void updateToggles(); + +private: + Params params; + std::map toggles; + + OnroadScreenOff *onroad_screen_off; + OnroadScreenOffBrightness *onroad_screen_off_brightness; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.cc new file mode 100644 index 0000000000..88a69842c7 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.cc @@ -0,0 +1,58 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +MonitoringPanel::MonitoringPanel(QWidget *parent) : QFrame(parent) { + main_layout = new QStackedLayout(this); + + ListWidgetSP *list = new ListWidgetSP(this, false); + // param, title, desc, icon + std::vector> toggle_defs{ + { + "HandsOnWheelMonitoring", + tr("Enable Hands on Wheel Monitoring"), + tr("Monitor and alert when driver is not keeping the hands on the steering wheel."), + "../assets/offroad/icon_blank.png", + } + }; + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControlSP(param, title, desc, icon, this); + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + } + + monitoringScreen = new QWidget(this); + QVBoxLayout* vlayout = new QVBoxLayout(monitoringScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + + vlayout->addWidget(new ScrollViewSP(list, this), 1); + main_layout->addWidget(monitoringScreen); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.h new file mode 100644 index 0000000000..091b2fd9f5 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.h @@ -0,0 +1,46 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class MonitoringPanel : public QFrame { + Q_OBJECT + +public: + explicit MonitoringPanel(QWidget *parent = nullptr); + +private: + QStackedLayout* main_layout = nullptr; + QWidget* monitoringScreen = nullptr; + Params params; + std::map toggles; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.cc new file mode 100644 index 0000000000..dd642054d9 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.cc @@ -0,0 +1,123 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.h" + +#include + +#include +#include +#include + +#include "common/util.h" +#include "common/params.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/input.h" + +void TermsPageSP::showEvent(QShowEvent *event) { + // late init, building QML widget takes 200ms + if (layout()) { + return; + } + + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(45, 35, 45, 45); + main_layout->setSpacing(0); + + QLabel *title = new QLabel(tr("Terms & Conditions")); + title->setStyleSheet("font-size: 90px; font-weight: 600;"); + main_layout->addWidget(title); + + main_layout->addSpacing(30); + + QQuickWidget *text = new QQuickWidget(this); + text->setResizeMode(QQuickWidget::SizeRootObjectToView); + text->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + text->setAttribute(Qt::WA_AlwaysStackOnTop); + text->setClearColor(QColor("#1B1B1B")); + + std::string tc_text = sunnypilot_tc ? "../assets/offroad/sp_tc.html" : "../assets/offroad/tc.html"; + QString text_view = util::read_file(tc_text).c_str(); + text->rootContext()->setContextProperty("text_view", text_view); + + text->setSource(QUrl::fromLocalFile("qt/offroad/text_view.qml")); + + main_layout->addWidget(text, 1); + main_layout->addSpacing(50); + + QObject *obj = (QObject*)text->rootObject(); + QObject::connect(obj, SIGNAL(scroll()), SLOT(enableAccept())); + + QHBoxLayout* buttons = new QHBoxLayout; + buttons->setMargin(0); + buttons->setSpacing(45); + main_layout->addLayout(buttons); + + QPushButton *decline_btn = new QPushButton(tr("Decline")); + buttons->addWidget(decline_btn); + QObject::connect(decline_btn, &QPushButton::clicked, this, &TermsPage::declinedTerms); + + accept_btn = new QPushButton(tr("Scroll to accept")); + accept_btn->setEnabled(false); + accept_btn->setStyleSheet(R"( + QPushButton { + background-color: #465BEA; + } + QPushButton:pressed { + background-color: #3049F4; + } + QPushButton:disabled { + background-color: #4F4F4F; + } + )"); + buttons->addWidget(accept_btn); + QObject::connect(accept_btn, &QPushButton::clicked, this, &TermsPage::acceptedTerms); +} + +void OnboardingWindowSP::updateActiveScreen() { + if(accepted_terms && training_done && !accepted_terms_sp) { + setCurrentIndex(3); + } else { + OnboardingWindow::updateActiveScreen(); + } +} + +OnboardingWindowSP::OnboardingWindowSP(QWidget *parent) : OnboardingWindow(parent) { + std::string current_terms_version_sp = params.get("TermsVersionSunnypilot"); + accepted_terms_sp = params.get("HasAcceptedTermsSP") == current_terms_version_sp; + LOGD("accepted_terms_sp: %s", params.get("HasAcceptedTermsSP").c_str()); + + auto* terms_sp = new TermsPageSP(true, parent); + addWidget(terms_sp); // index = 3 + connect(terms_sp, &TermsPageSP::acceptedTerms, [=]() { + params.put("HasAcceptedTermsSP", current_terms_version_sp); + accepted_terms_sp = true; + updateActiveScreen(); + }); + connect(terms_sp, &TermsPageSP::declinedTerms, [=]() { setCurrentIndex(2); }); + + OnboardingWindowSP::updateActiveScreen(); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.h new file mode 100644 index 0000000000..f7e9bfccde --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/onboarding.h @@ -0,0 +1,57 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/qt_window.h" + +class TermsPageSP : public TermsPage { + Q_OBJECT + + +public: + explicit TermsPageSP(bool sunnypilot = false, QWidget *parent = 0) : TermsPage(parent), sunnypilot_tc(sunnypilot) {} + + +private: + bool sunnypilot_tc = false; + void showEvent(QShowEvent *event) override; +}; + +class OnboardingWindowSP : public OnboardingWindow { + Q_OBJECT + +public: + explicit OnboardingWindowSP(QWidget *parent = 0); + inline bool completed() const override { return accepted_terms && accepted_terms_sp && training_done; } + +private: + bool accepted_terms_sp = false; + void updateActiveScreen() override; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/locations_fetcher.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h similarity index 64% rename from selfdrive/ui/qt/offroad/sunnypilot/locations_fetcher.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h index 1e5b20139f..f3142cfc69 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/locations_fetcher.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h @@ -1,11 +1,40 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once -#include #include // for std::sort #include -#include +#include +#include -#include "selfdrive/ui/qt/offroad/sunnypilot/json_fetcher.h" +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/common/json_fetcher.h" static const std::tuple defaultLocation = std::make_tuple("== None ==", ""); // New class LocationsFetcher that handles web requests and JSON parsing diff --git a/selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc similarity index 80% rename from selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc index 3df9c09fe3..b6736c7424 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc @@ -1,4 +1,31 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h" + #include ModelsFetcher::ModelsFetcher(QObject* parent) : QObject(parent) { @@ -88,7 +115,7 @@ void ModelsFetcher::onFinished(QNetworkReply* reply, const QString& destinationP QFile file(finalPath); //ensure if the path exists and if not create it - if(!QDir().mkpath(destinationPath)) + if (!QDir().mkpath(destinationPath)) { LOGE("Unable to create directory: %s", destinationPath.toStdString().c_str()); emit downloadFailed(filename); diff --git a/selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h similarity index 75% rename from selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h index 22ab81e541..c813deedec 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h @@ -1,18 +1,50 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once #include // for std::sort #include #include +#include + #include #include #include #include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/sunnypilot/ui.h" #include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/json_fetcher.h" +#include "selfdrive/ui/sunnypilot/qt/common/json_fetcher.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif #include "system/hardware/hw.h" static const QString MODELS_PATH = Hardware::PC() ? QDir::homePath() + "/.comma/media/0/models/" : "/data/media/0/models/"; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.cc similarity index 76% rename from selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.cc index 236e4406cf..8420181d16 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.cc @@ -1,18 +1,51 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.h" + +#include +#include +#include + +#include "common/swaglog.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" OsmPanel::OsmPanel(QWidget *parent) : QFrame(parent) { main_layout = new QStackedLayout(this); - const auto list = new ListWidget(this, false); - list->addItem(mapdVersion = new LabelControl(tr("Mapd Version"), "Loading...")); + const auto list = new ListWidgetSP(this, false); + list->addItem(mapdVersion = new LabelControlSP(tr("Mapd Version"), "Loading...")); list->addItem(setupOsmDeleteMapsButton(parent)); - list->addItem(offlineMapsETA = new LabelControl(tr("Offline Maps ETA"), "")); - list->addItem(offlineMapsElapsed = new LabelControl(tr("Time Elapsed"), "")); + list->addItem(offlineMapsETA = new LabelControlSP(tr("Offline Maps ETA"), "")); + list->addItem(offlineMapsElapsed = new LabelControlSP(tr("Time Elapsed"), "")); list->addItem(setupOsmUpdateButton(parent)); list->addItem(setupOsmDownloadButton(parent)); list->addItem(setupUsStatesButton(parent)); - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { updateLabels(); }); @@ -24,13 +57,13 @@ OsmPanel::OsmPanel(QWidget *parent) : QFrame(parent) { osmScreen = new QWidget(this); auto *vlayout = new QVBoxLayout(osmScreen); vlayout->setContentsMargins(50, 20, 50, 20); - vlayout->addWidget(new ScrollView(list, this), 1); + vlayout->addWidget(new ScrollViewSP(list, this), 1); main_layout->addWidget(osmScreen); } -ButtonControl *OsmPanel::setupOsmDeleteMapsButton(QWidget *parent) { - osmDeleteMapsBtn = new ButtonControl(tr("Downloaded Maps"), tr("DELETE")); // Updated on updateLabels() - connect(osmDeleteMapsBtn, &ButtonControl::clicked, [=]() { +ButtonControlSP *OsmPanel::setupOsmDeleteMapsButton(QWidget *parent) { + osmDeleteMapsBtn = new ButtonControlSP(tr("Downloaded Maps"), tr("DELETE")); // Updated on updateLabels() + connect(osmDeleteMapsBtn, &ButtonControlSP::clicked, [=]() { if (showConfirmationDialog(parent, tr("This will delete ALL downloaded maps\n\nAre you sure you want to delete all the maps?"), tr("Yes, delete all the maps."))) { QtConcurrent::run([=]() { QDir dir(MAP_PATH); @@ -47,9 +80,9 @@ ButtonControl *OsmPanel::setupOsmDeleteMapsButton(QWidget *parent) { return osmDeleteMapsBtn; } -ButtonControl *OsmPanel::setupOsmUpdateButton(QWidget *parent) { - osmUpdateBtn = new ButtonControl(tr("Database Update"), tr("CHECK")); // Updated on updateLabels() - connect(osmUpdateBtn, &ButtonControl::clicked, [=]() { +ButtonControlSP *OsmPanel::setupOsmUpdateButton(QWidget *parent) { + osmUpdateBtn = new ButtonControlSP(tr("Database Update"), tr("CHECK")); // Updated on updateLabels() + connect(osmUpdateBtn, &ButtonControlSP::clicked, [=]() { if (osm_download_in_progress && !download_failed_state) { updateLabels(); } else if (showConfirmationDialog(parent)) { @@ -61,9 +94,9 @@ ButtonControl *OsmPanel::setupOsmUpdateButton(QWidget *parent) { return osmUpdateBtn; } -ButtonControl *OsmPanel::setupOsmDownloadButton(QWidget *parent) { - osmDownloadBtn = new ButtonControl(tr("Country"), tr("SELECT")); - connect(osmDownloadBtn, &ButtonControl::clicked, [=]() { +ButtonControlSP *OsmPanel::setupOsmDownloadButton(QWidget *parent) { + osmDownloadBtn = new ButtonControlSP(tr("Country"), tr("SELECT")); + connect(osmDownloadBtn, &ButtonControlSP::clicked, [=]() { osmDownloadBtn->setEnabled(false); osmDownloadBtn->setValue(tr("Fetching Country list...")); const std::vector> locations = getOsmLocations(); @@ -73,7 +106,7 @@ ButtonControl *OsmPanel::setupOsmDownloadButton(QWidget *parent) { const QString currentTitle = ((initTitle == "== None ==") || (initTitle.length() == 0)) ? "== None ==" : initTitle; QStringList locationTitles; - for (auto &loc: locations) { + for (auto &loc : locations) { locationTitles.push_back(std::get<0>(loc)); } @@ -81,7 +114,7 @@ ButtonControl *OsmPanel::setupOsmDownloadButton(QWidget *parent) { if (!selection.isEmpty()) { params.put("OsmLocal", "1"); params.put("OsmLocationTitle", selection.toStdString()); - for (auto &loc: locations) { + for (auto &loc : locations) { if (std::get<0>(loc) == selection) { params.put("OsmLocationName", std::get<1>(loc).toStdString()); break; @@ -103,9 +136,9 @@ ButtonControl *OsmPanel::setupOsmDownloadButton(QWidget *parent) { return osmDownloadBtn; } -ButtonControl *OsmPanel::setupUsStatesButton(QWidget *parent) { - usStatesBtn = new ButtonControl(tr("State"), tr("SELECT")); - connect(usStatesBtn, &ButtonControl::clicked, [=]() { +ButtonControlSP *OsmPanel::setupUsStatesButton(QWidget *parent) { + usStatesBtn = new ButtonControlSP(tr("State"), tr("SELECT")); + connect(usStatesBtn, &ButtonControlSP::clicked, [=]() { const std::tuple allStatesOption = std::make_tuple("All States (~4.8 GB)", "All"); usStatesBtn->setEnabled(false); usStatesBtn->setValue(tr("Fetching State list...")); @@ -116,14 +149,14 @@ ButtonControl *OsmPanel::setupUsStatesButton(QWidget *parent) { const QString currentTitle = ((initTitle == std::get<0>(allStatesOption)) || (initTitle.length() == 0)) ? tr("All") : initTitle; QStringList locationTitles; - for (auto &loc: locations) { + for (auto &loc : locations) { locationTitles.push_back(std::get<0>(loc)); } const QString selection = MultiOptionDialog::getSelection(tr("State"), locationTitles, currentTitle, this); if (!selection.isEmpty()) { params.put("OsmStateTitle", selection.toStdString()); - for (auto &loc: locations) { + for (auto &loc : locations) { if (std::get<0>(loc) == selection) { params.put("OsmStateName", std::get<1>(loc).toStdString()); break; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.h similarity index 77% rename from selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.h index 2f9cf8455f..b250e2cecd 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.h @@ -1,19 +1,47 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once #include #include +#include #include +#include +#include +#include #include #include #include -#include "common/swaglog.h" #include "selfdrive/ui/qt/network/wifi_manager.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/locations_fetcher.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h" #include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/sunnypilot/ui.h" #include "system/hardware/hw.h" constexpr int FAST_REFRESH_INTERVAL = 1000; // ms @@ -31,9 +59,9 @@ private: QWidget* osmScreen = nullptr; Params params; Params mem_params{ Hardware::PC() ? "": "/dev/shm/params"}; - std::map toggles; + std::map toggles; std::optional> mapSizeFuture; - const SubMaster &sm = *uiState()->sm; + const SubMaster &sm = *uiStateSP()->sm; bool is_onroad = false; @@ -45,17 +73,17 @@ private: quint64 mapsDirSize = 0; QLabel *osmUpdateLbl; - ButtonControl *osmDownloadBtn; - ButtonControl *osmUpdateBtn; - ButtonControl *usStatesBtn; - ButtonControl *osmDeleteMapsBtn; - ButtonControl *setupOsmDeleteMapsButton(QWidget *parent);; - ButtonControl* setupOsmUpdateButton(QWidget *parent); - ButtonControl* setupOsmDownloadButton(QWidget *parent); - ButtonControl* setupUsStatesButton(QWidget *parent); + ButtonControlSP *osmDownloadBtn; + ButtonControlSP *osmUpdateBtn; + ButtonControlSP *usStatesBtn; + ButtonControlSP *osmDeleteMapsBtn; + ButtonControlSP *setupOsmDeleteMapsButton(QWidget *parent);; + ButtonControlSP* setupOsmUpdateButton(QWidget *parent); + ButtonControlSP* setupOsmDownloadButton(QWidget *parent); + ButtonControlSP* setupUsStatesButton(QWidget *parent); QTimer *timer; std::string osm_download_locations; -// void updateButtonControl(ButtonControl *btnControl, QWidget *parent, const QString &initTitle, const QString &allStatesOption); +// void updateButtonControlSP(ButtonControlSP *btnControl, QWidget *parent, const QString &initTitle, const QString &allStatesOption); void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent* event) override; @@ -65,10 +93,10 @@ private: QString processUpdateStatus(bool pending_update_check, int total_files, int downloaded_files, const QJsonObject& json, bool failed_state); ConfirmationDialog* confirmationDialog; - LabelControl *mapdVersion; - LabelControl *offlineMapsStatus; - LabelControl *offlineMapsETA; - LabelControl *offlineMapsElapsed; + LabelControlSP *mapdVersion; + LabelControlSP *offlineMapsStatus; + LabelControlSP *offlineMapsETA; + LabelControlSP *offlineMapsElapsed; std::optional lastDownloadedTimePoint; LocationsFetcher locationsFetcher; void updateMapSize(); diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc new file mode 100644 index 0000000000..f99b90fb57 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc @@ -0,0 +1,446 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h" +#include "selfdrive/ui/sunnypilot/qt/network/networking.h" +#include "selfdrive/ui/sunnypilot/sunnypilot_main.h" + +TogglesPanelSP::TogglesPanelSP(SettingsWindow *parent) : TogglesPanel(parent) { + // param, title, desc, icon + std::vector> toggle_defs{ + { + "OpenpilotEnabledToggle", + tr("Enable sunnypilot"), + tr("Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), + "../assets/offroad/icon_blank.png", + }, + { + "ExperimentalLongitudinalEnabled", + tr("openpilot Longitudinal Control (Alpha)"), + QString("%1

%2") + .arg(tr("WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) + .arg(tr("On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " + "Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.")), + "../assets/offroad/icon_blank.png", + }, + { + "CustomStockLong", + tr("Custom Stock Longitudinal Control"), + tr("When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses.\nThis feature must be used along with SLC, and/or V-TSC, and/or M-TSC."), + "../assets/offroad/icon_blank.png", + }, + { + "ExperimentalMode", + tr("Experimental Mode"), + "", + "../assets/offroad/icon_blank.png", + }, + { + "DynamicExperimentalControl", + tr("Enable Dynamic Experimental Control"), + tr("Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal."), + "../assets/offroad/icon_blank.png", + }, + { + "DynamicPersonality", + tr("Enable Dynamic Personality"), + tr("Enable this to allow sunnypilot to dynamically adjust following distance and reaction based on your \"Driving Personality\" setting. " + "Instead of predefined settings for each personality, every personality now adapts dynamically according to your speed and the distance to the lead car."), + "../assets/offroad/icon_blank.png", + }, + { + "DisengageOnAccelerator", + tr("Disengage on Accelerator Pedal"), + tr("When enabled, pressing the accelerator pedal will disengage openpilot."), + "../assets/offroad/icon_blank.png", + }, + { + "IsLdwEnabled", + tr("Enable Lane Departure Warnings"), + tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), + "../assets/offroad/icon_blank.png", + }, + { + "AlwaysOnDM", + tr("Always-On Driver Monitoring"), + tr("Enable driver monitoring even when sunnypilot is not engaged."), + "../assets/offroad/icon_blank.png", + }, + { + "RecordFront", + tr("Record and Upload Driver Camera"), + tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), + "../assets/offroad/icon_blank.png", + }, + { + "DisableOnroadUploads", + tr("Disable Onroad Uploads"), + tr("Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. " + "Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC)."), + "../assets/offroad/icon_blank.png", + }, + { + "IsMetric", + tr("Use Metric System"), + tr("Display speed in km/h instead of mph."), + "../assets/offroad/icon_blank.png", + }, +#ifdef ENABLE_MAPS + { + "NavSettingTime24h", + tr("Show ETA in 24h Format"), + tr("Use 24h format instead of am/pm"), + "../assets/offroad/icon_blank.png", + }, + { + "NavSettingLeftSide", + tr("Show Map on Left Side of UI"), + tr("Show map on left side when in split screen view."), + "../assets/offroad/icon_blank.png", + }, +#endif + }; + + + std::vector longi_button_texts{tr("Aggressive"), tr("Moderate"), tr("Standard"), tr("Relaxed")}; + long_personality_setting = new ButtonParamControlSP("LongitudinalPersonality", tr("Driving Personality"), + tr("Standard is recommended. In moderate/aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. " + "In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " + "your steering wheel distance button."), + "", + longi_button_texts, + 380); + long_personality_setting->showDescription(); + + // accel controller + std::vector accel_personality_texts{tr("Sport"), tr("Normal"), tr("Eco"), tr("Stock")}; + accel_personality_setting = new ButtonParamControlSP("AccelPersonality", tr("Acceleration Personality"), + tr("Normal is recommended. In sport mode, sunnypilot will provide aggressive acceleration for a dynamic driving experience. " + "In eco mode, sunnypilot will apply smoother and more relaxed acceleration. On supported cars, you can cycle through these " + "acceleration personality within Onroad Settings on the driving screen."), + "", + accel_personality_texts); + accel_personality_setting->showDescription(); + + // set up uiState update for personality setting + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &TogglesPanelSP::updateState); + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControlSP(param, title, desc, icon, this); + + bool locked = params.getBool((param + "Lock").toStdString()); + toggle->setEnabled(!locked); + + addItem(toggle); + toggles[param.toStdString()] = toggle; + + // insert longitudinal personality after NDOG toggle + if (param == "DisengageOnAccelerator") { + addItem(long_personality_setting); + addItem(accel_personality_setting); + } + } + + // Toggles with confirmation dialogs + //toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); + toggles["ExperimentalMode"]->setConfirmation(true, true); + toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false); + toggles["CustomStockLong"]->setConfirmation(true, false); + + connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControlSP::toggleFlipped, [=]() { + updateToggles(); + }); + connect(toggles["CustomStockLong"], &ToggleControlSP::toggleFlipped, [=]() { + updateToggles(); + }); + + param_watcher = new ParamWatcher(this); + + QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { + updateToggles(); + }); +} + +void TogglesPanelSP::updateState(const UIStateSP &s) { + const SubMaster &sm = *(s.sm); + + if (sm.updated("controlsState")) { + auto personality = sm["controlsState"].getControlsState().getPersonality(); + if (personality != s.scene.personality && s.scene.started && isVisible()) { + long_personality_setting->setCheckedButton(static_cast(personality)); + } + uiStateSP()->scene.personality = personality; + } + + if (sm.updated("controlsStateSP")) { + auto accel_personality = sm["controlsStateSP"].getControlsStateSP().getAccelPersonality(); + if (accel_personality != s.scene.accel_personality && s.scene.started && isVisible()) { + accel_personality_setting->setCheckedButton(static_cast(accel_personality)); + } + uiStateSP()->scene.accel_personality = accel_personality; + } +} + +void TogglesPanelSP::showEvent(QShowEvent *event) { + updateToggles(); +} + +void TogglesPanelSP::updateToggles() { + param_watcher->addParam("LongitudinalPersonality"); + + if (!isVisible()) return; + + auto experimental_mode_toggle = toggles["ExperimentalMode"]; + auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"]; + auto custom_stock_long_toggle = toggles["CustomStockLong"]; + auto dec_toggle = toggles["DynamicExperimentalControl"]; + auto dynamic_personality_toggle = toggles["DynamicPersonality"]; + const QString e2e_description = QString("%1
" + "

%2


" + "%3
" + "

%4


" + "%5
") + .arg(tr("openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. Experimental features are listed below:")) + .arg(tr("End-to-End Longitudinal Control")) + .arg(tr("Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. " + "Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " + "mistakes should be expected.")) + .arg(tr("New Driving Visualization")) + .arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner.")); + + const bool is_release = params.getBool("IsReleaseBranch"); + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (!CP.getExperimentalLongitudinalAvailable() || is_release) { + params.remove("ExperimentalLongitudinalEnabled"); + } + op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); + + if (!CP.getCustomStockLongAvailable()) { + params.remove("CustomStockLong"); + } + custom_stock_long_toggle->setVisible(CP.getCustomStockLongAvailable()); + + if (hasLongitudinalControl(CP)) { + // normal description and toggle + experimental_mode_toggle->setEnabled(true); + experimental_mode_toggle->setDescription(e2e_description); + long_personality_setting->setEnabled(true); + accel_personality_setting->setEnabled(true); + op_long_toggle->setEnabled(true); + custom_stock_long_toggle->setEnabled(false); + params.remove("CustomStockLong"); + dec_toggle->setEnabled(true); + dynamic_personality_toggle->setEnabled(true); + } else if (custom_stock_long_toggle->isToggled()) { + op_long_toggle->setEnabled(false); + experimental_mode_toggle->setEnabled(false); + long_personality_setting->setEnabled(false); + accel_personality_setting->setEnabled(false); + params.remove("ExperimentalLongitudinalEnabled"); + params.remove("ExperimentalMode"); + } else { + // no long for now + experimental_mode_toggle->setEnabled(false); + long_personality_setting->setEnabled(false); + accel_personality_setting->setEnabled(false); + params.remove("ExperimentalMode"); + + const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control."); + + QString long_desc = unavailable + " " + \ + tr("openpilot longitudinal control may come in a future update."); + if (CP.getExperimentalLongitudinalAvailable()) { + if (is_release) { + long_desc = unavailable + " " + tr("An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches."); + } else { + long_desc = tr("Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode."); + } + } + experimental_mode_toggle->setDescription("" + long_desc + "

" + e2e_description); + + op_long_toggle->setEnabled(CP.getExperimentalLongitudinalAvailable() && !is_release); + custom_stock_long_toggle->setEnabled(CP.getCustomStockLongAvailable()); + dec_toggle->setEnabled(false); + dynamic_personality_toggle->setEnabled(false); + params.remove("DynamicExperimentalControl"); + params.remove("DynamicPersonality"); + } + + experimental_mode_toggle->refresh(); + op_long_toggle->refresh(); + custom_stock_long_toggle->refresh(); + dec_toggle->refresh(); + dynamic_personality_toggle->refresh(); + } else { + experimental_mode_toggle->setDescription(e2e_description); + op_long_toggle->setVisible(false); + custom_stock_long_toggle->setVisible(false); + dec_toggle->setVisible(false); + dynamic_personality_toggle->setVisible(false); + } +} + +SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { + + // setup two main layouts + sidebar_widget = new QWidget; + QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); + panel_widget = new QStackedWidget(); + + // setup layout for close button + QVBoxLayout *close_btn_layout = new QVBoxLayout; + close_btn_layout->setContentsMargins(0, 0, 0, 20); + + // close button + QPushButton *close_btn = new QPushButton(tr("×")); + close_btn->setStyleSheet(R"( + QPushButton { + font-size: 140px; + padding-bottom: 20px; + border-radius: 76px; + background-color: #292929; + font-weight: 400; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + close_btn->setFixedSize(152, 152); + close_btn_layout->addWidget(close_btn, 0, Qt::AlignLeft); + QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings); + + // setup buttons widget + QWidget *buttons_widget = new QWidget; + QVBoxLayout *buttons_layout = new QVBoxLayout(buttons_widget); + buttons_layout->setMargin(0); + buttons_layout->addSpacing(10); + + // setup panels + DevicePanelSP *device = new DevicePanelSP(this); + QObject::connect(device, &DevicePanelSP::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide); + QObject::connect(device, &DevicePanelSP::showDriverView, this, &SettingsWindow::showDriverView); + + TogglesPanelSP *toggles = new TogglesPanelSP(this); + QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); + + QList panels = { + PanelInfo(" " + tr("Device"), device, "../assets/navigation/icon_home.svg"), + PanelInfo(" " + tr("Network"), new NetworkingSP(this), "../assets/offroad/icon_network.png"), + PanelInfo(" " + tr("sunnylink"), new SunnylinkPanel(this), "../assets/offroad/icon_wifi_strength_full.svg"), + PanelInfo(" " + tr("Toggles"), toggles, "../assets/offroad/icon_toggle.png"), + PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../assets/offroad/icon_software.png"), + PanelInfo(" " + tr("sunnypilot"), new SunnypilotPanel(this), "../assets/offroad/icon_openpilot.png"), + PanelInfo(" " + tr("OSM"), new OsmPanel(this), "../assets/offroad/icon_map.png"), + PanelInfo(" " + tr("Monitoring"), new MonitoringPanel(this), "../assets/offroad/icon_monitoring.png"), + PanelInfo(" " + tr("Visuals"), new VisualsPanel(this), "../assets/offroad/icon_visuals.png"), + PanelInfo(" " + tr("Display"), new DisplayPanel(this), "../assets/offroad/icon_display.png"), + PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../assets/offroad/icon_trips.png"), + PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../assets/offroad/icon_vehicle.png"), + }; + + nav_btns = new QButtonGroup(this); + for (auto &[name, panel, icon] : panels) { + QPushButton *btn = new QPushButton(name); + btn->setCheckable(true); + btn->setChecked(nav_btns->buttons().size() == 0); + btn->setIcon(QIcon(QPixmap(icon))); + btn->setIconSize(QSize(70, 70)); + btn->setStyleSheet(R"( + QPushButton { + border-radius: 20px; + width: 400px; + height: 98px; + color: #bdbdbd; + border: none; + background: none; + font-size: 50px; + font-weight: 500; + text-align: left; + padding-left: 22px; + } + QPushButton:checked { + background-color: #696868; + color: white; + } + QPushButton:pressed { + color: #ADADAD; + } + )"); + btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + nav_btns->addButton(btn); + buttons_layout->addWidget(btn, 0, Qt::AlignLeft | Qt::AlignBottom); + + const int lr_margin = (name != (" " + tr("Network")) || (name != (" " + tr("sunnypilot")))) ? 50 : 0; // Network and sunnypilot panel handles its own margins + panel->setContentsMargins(lr_margin, 25, lr_margin, 25); + + ScrollViewSP *panel_frame = new ScrollViewSP(panel, this); + panel_widget->addWidget(panel_frame); + + QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { + btn->setChecked(true); + panel_widget->setCurrentWidget(w); + }); + } + sidebar_layout->setContentsMargins(50, 50, 25, 50); + + // main settings layout, sidebar + main panel + QHBoxLayout *main_layout = new QHBoxLayout(this); + + // add layout for close button + sidebar_layout->addLayout(close_btn_layout); + + // add layout for buttons scrolling + ScrollViewSP *buttons_scrollview = new ScrollViewSP(buttons_widget, this); + sidebar_layout->addWidget(buttons_scrollview); + + sidebar_widget->setFixedWidth(500); + main_layout->addWidget(sidebar_widget); + main_layout->addWidget(panel_widget); + + setStyleSheet(R"( + * { + color: white; + font-size: 50px; + } + SettingsWindow { + background-color: black; + } + QStackedWidget, ScrollViewSP { + background-color: black; + border-radius: 30px; + } + )"); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h new file mode 100644 index 0000000000..297a092400 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h @@ -0,0 +1,66 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/qt/offroad/settings.h" + +class TogglesPanelSP : public TogglesPanel { + Q_OBJECT + +public: + explicit TogglesPanelSP(SettingsWindow *parent); + void showEvent(QShowEvent *event) override; + +private slots: + void updateState(const UIStateSP &s); + +private: + ButtonParamControlSP *long_personality_setting; + ButtonParamControlSP *accel_personality_setting; + + ParamWatcher *param_watcher; + void updateToggles() override; +}; + +class SettingsWindowSP : public SettingsWindow { + Q_OBJECT + +public: + explicit SettingsWindowSP(QWidget* parent = nullptr); + +protected: + struct PanelInfo { + QString name; + QWidget *widget; + QString icon; + + PanelInfo(const QString &name, QWidget *widget, const QString &icon) : name(name), widget(widget), icon(icon) {} + }; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.cc similarity index 86% rename from selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.cc index bbe8572797..ffe047ba4d 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.cc @@ -1,9 +1,39 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.h" + +#include + +#include "common/model.h" SoftwarePanelSP::SoftwarePanelSP(QWidget *parent) : SoftwarePanel(parent) { - // Get current model name and create new ButtonControl + // Get current model name and create new ButtonControlSP const auto current_model = GetModelName(); - currentModelLblBtn = new ButtonControl(tr("Driving Model"), tr("SELECT"), current_model); + currentModelLblBtn = new ButtonControlSP(tr("Driving Model"), tr("SELECT"), current_model); currentModelLblBtn->setValue(current_model); connect(&models_fetcher, &ModelsFetcher::downloadProgress, this, [this](const double progress) { @@ -61,9 +91,9 @@ SoftwarePanelSP::SoftwarePanelSP(QWidget *parent) : SoftwarePanel(parent) { // Connect click event from currentModelLblBtn to local slot - connect(currentModelLblBtn, &ButtonControl::clicked, this, &SoftwarePanelSP::handleCurrentModelLblBtnClicked); + connect(currentModelLblBtn, &ButtonControlSP::clicked, this, &SoftwarePanelSP::handleCurrentModelLblBtnClicked); - ReplaceOrAddWidget(currentModelLbl, currentModelLblBtn); + AddWidgetAt(0, currentModelLblBtn); } void SoftwarePanelSP::handleDownloadFailed(const QString &modelType) { @@ -185,7 +215,7 @@ void SoftwarePanelSP::handleCurrentModelLblBtnClicked() { QMap index_to_model; // Collecting indices with display names - for (const auto &model: models) { + for (const auto &model : models) { if ((is_release_sp && model.environment == "release") || !is_release_sp) { index_to_model.insert(model.index, model.displayName); } @@ -197,7 +227,7 @@ void SoftwarePanelSP::handleCurrentModelLblBtnClicked() { return index1.toInt() > index2.toInt(); }); - for (const QString &index: indices) { + for (const QString &index : indices) { modelNames.push_back(index_to_model[index]); } @@ -213,7 +243,7 @@ void SoftwarePanelSP::handleCurrentModelLblBtnClicked() { } // Finding and setting the selected model - for (auto &model: models) { + for (auto &model : models) { if (model.displayName == selectedModelName) { selectedModelToDownload = model; selectedNavModelToDownload = model; @@ -254,7 +284,7 @@ void SoftwarePanelSP::handleCurrentModelLblBtnClicked() { } void SoftwarePanelSP::checkNetwork() { - const SubMaster &sm = *(uiState()->sm); + const SubMaster &sm = *(uiStateSP()->sm); const auto device_state = sm["deviceState"].getDeviceState(); const auto network_type = device_state.getNetworkType(); is_wifi = network_type == cereal::DeviceState::NetworkType::WIFI; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.h similarity index 64% rename from selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.h index 384a592de4..17b6b0bb9d 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/software_settings_sp.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.h @@ -1,9 +1,34 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once -#include "common/model.h" -#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/sunnypilot/ui.h" #include "selfdrive/ui/qt/offroad/settings.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/models_fetcher.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h" class SoftwarePanelSP final : public SoftwarePanel { Q_OBJECT @@ -19,17 +44,17 @@ private: void checkNetwork(); bool isDownloadingModel() const { - LOGD("isDownloadingModel: selectedModelToDownload.has_value() [%s] && modelDownloadProgress [%f]",selectedModelToDownload.has_value() ?"true": "false", modelDownloadProgress.value_or(0.0)); + LOGD("isDownloadingModel: selectedModelToDownload.has_value() [%s] && modelDownloadProgress [%f]", selectedModelToDownload.has_value() ? "true": "false", modelDownloadProgress.value_or(0.0)); return selectedModelToDownload.has_value() && modelDownloadProgress.value_or(0.0) > 0.0 && modelDownloadProgress.value_or(0.0) < 100.0; } bool isDownloadingNavModel() const { - LOGD("isDownloadingNavModel: selectedNavModelToDownload.has_value() [%s] && navModelDownloadProgress [%f]",selectedNavModelToDownload.has_value() ?"true": "false", navModelDownloadProgress.value_or(0.0)); + LOGD("isDownloadingNavModel: selectedNavModelToDownload.has_value() [%s] && navModelDownloadProgress [%f]", selectedNavModelToDownload.has_value() ? "true": "false", navModelDownloadProgress.value_or(0.0)); return selectedNavModelToDownload.has_value() && navModelDownloadProgress.value_or(0.0) > 0.0 && navModelDownloadProgress.value_or(0.0) < 100.0; } bool isDownloadingMetadata() const { - LOGD("isDownloadingMetadata: selectedMetadataToDownload.has_value() [%s] && metadataDownloadProgress [%f]",selectedMetadataToDownload.has_value() ?"true": "false", metadataDownloadProgress.value_or(0.0)); + LOGD("isDownloadingMetadata: selectedMetadataToDownload.has_value() [%s] && metadataDownloadProgress [%f]", selectedMetadataToDownload.has_value() ? "true": "false", metadataDownloadProgress.value_or(0.0)); return selectedMetadataToDownload.has_value() && metadataDownloadProgress.value_or(0.0) > 0.0 && metadataDownloadProgress.value_or(0.0) < 100.0; } @@ -69,7 +94,7 @@ private: std::optional selectedModelToDownload; std::optional selectedNavModelToDownload; std::optional selectedMetadataToDownload; - ButtonControl *currentModelLblBtn; + ButtonControlSP *currentModelLblBtn; ModelsFetcher models_fetcher; ModelsFetcher nav_models_fetcher; ModelsFetcher metadata_fetcher; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.cc similarity index 85% rename from selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.cc index 1ebaf4f14e..cf5ec4a4da 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.cc @@ -1,7 +1,38 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.h" +#include "selfdrive/ui/sunnypilot/qt/api.h" + +#include +#include + +#include #include -#include SunnylinkPanel::SunnylinkPanel(QWidget* parent) : QFrame(parent) { main_layout = new QStackedLayout(this); @@ -13,28 +44,27 @@ SunnylinkPanel::SunnylinkPanel(QWidget* parent) : QFrame(parent) { }); is_sunnylink_enabled = Params().getBool("SunnylinkEnabled"); - connect(uiState(), &UIState::sunnylinkRolesChanged, this, &SunnylinkPanel::updateLabels); - connect(uiState(), &UIState::sunnylinkDeviceUsersChanged, this, &SunnylinkPanel::updateLabels); + connect(uiStateSP(), &UIStateSP::sunnylinkRolesChanged, this, &SunnylinkPanel::updateLabels); + connect(uiStateSP(), &UIStateSP::sunnylinkDeviceUsersChanged, this, &SunnylinkPanel::updateLabels); - auto list = new ListWidget(this, false); - sunnylinkEnabledBtn = new ParamControl( + auto list = new ListWidgetSP(this, false); + sunnylinkEnabledBtn = new ParamControlSP( "SunnylinkEnabled", tr("Enable sunnylink"), sunnylinkBtnDescription, - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); sunnylinkEnabledBtn->setValue(tr("Device ID ")+ getSunnylinkDongleId().value_or(tr("N/A"))); list->addItem(sunnylinkEnabledBtn); list->addItem(horizontal_line()); sunnylinkBtnDescription = tr("This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that."); - connect(sunnylinkEnabledBtn, &ParamControl::showDescriptionEvent, [=]() { + connect(sunnylinkEnabledBtn, &ParamControlSP::showDescriptionEvent, [=]() { //resets the description to the default one for the easter egg sunnylinkEnabledBtn->setDescription(sunnylinkBtnDescription); }); - connect(sunnylinkEnabledBtn, &ParamControl::toggleFlipped, [=](bool enabled) { + connect(sunnylinkEnabledBtn, &ParamControlSP::toggleFlipped, [=](bool enabled) { if (enabled) { auto proud_description = ""+ tr("🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀")+ ""; sunnylinkEnabledBtn->showDescription(); @@ -49,23 +79,21 @@ SunnylinkPanel::SunnylinkPanel(QWidget* parent) : QFrame(parent) { }); status_popup = new SunnylinkSponsorPopup(false, this); - sponsorBtn = new ButtonControl( + sponsorBtn = new ButtonControlSP( tr("Sponsor Status"), tr("SPONSOR"), - tr("Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.") - ); + tr("Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.")); list->addItem(sponsorBtn); - connect(sponsorBtn, &ButtonControl::clicked, [=]() { + connect(sponsorBtn, &ButtonControlSP::clicked, [=]() { status_popup->exec(); }); list->addItem(horizontal_line()); pair_popup = new SunnylinkSponsorPopup(true, this); - pairSponsorBtn = new ButtonControl( + pairSponsorBtn = new ButtonControlSP( tr("Pair GitHub Account"), tr("PAIR"), - tr("Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.") + "🌟" - ); + tr("Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.") + "🌟"); list->addItem(pairSponsorBtn); - connect(pairSponsorBtn, &ButtonControl::clicked, [=]() { + connect(pairSponsorBtn, &ButtonControlSP::clicked, [=]() { if (getSunnylinkDongleId().value_or(tr("N/A")) == "N/A") { ConfirmationDialog::alert(tr("sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again."), this); } else { @@ -74,7 +102,7 @@ SunnylinkPanel::SunnylinkPanel(QWidget* parent) : QFrame(parent) { }); list->addItem(horizontal_line()); - list->addItem(new LabelControl(tr("Manage Settings"))); + list->addItem(new LabelControlSP(tr("Manage Settings"))); backup_settings = new BackupSettings; @@ -125,7 +153,7 @@ SunnylinkPanel::SunnylinkPanel(QWidget* parent) : QFrame(parent) { settings_layout->setAlignment(Qt::AlignLeft); list->addItem(settings_layout); - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { is_onroad = !offroad; updateLabels(); }); @@ -134,7 +162,7 @@ SunnylinkPanel::SunnylinkPanel(QWidget* parent) : QFrame(parent) { auto vlayout = new QVBoxLayout(sunnylinkScreen); vlayout->setContentsMargins(50, 20, 50, 20); - vlayout->addWidget(new ScrollView(list, this), 1); + vlayout->addWidget(new ScrollViewSP(list, this), 1); main_layout->addWidget(sunnylinkScreen); if (is_sunnylink_enabled) { startSunnylink(); @@ -186,17 +214,12 @@ void SunnylinkPanel::updateLabels() { is_sunnylink_enabled = Params().getBool("SunnylinkEnabled"); const auto sunnylinkDongleId = getSunnylinkDongleId().value_or(tr("N/A")); - bool is_sub = uiState()->isSunnylinkSponsor() && is_sunnylink_enabled; - auto max_current_sponsor_rule = uiState()->sunnylinkSponsorRole(); + bool is_sub = uiStateSP()->isSunnylinkSponsor() && is_sunnylink_enabled; + auto max_current_sponsor_rule = uiStateSP()->sunnylinkSponsorRole(); auto role_name = max_current_sponsor_rule.getSponsorTierString(); std::optional role_color = max_current_sponsor_rule.getSponsorTierColor(); - bool is_paired = uiState()->isSunnylinkPaired(); - auto paired_users = uiState()->sunnylinkDeviceUsers(); - - //little easter egg for Panda :D - if(sunnylinkDongleId == "d689627422cefcbc") { - role_name = "Panda 🐼"; - } + bool is_paired = uiStateSP()->isSunnylinkPaired(); + auto paired_users = uiStateSP()->sunnylinkDeviceUsers(); sunnylinkEnabledBtn->setEnabled(!is_onroad); sunnylinkEnabledBtn->setValue(tr("Device ID ")+ sunnylinkDongleId); @@ -252,7 +275,7 @@ QByteArray BackupSettings::backupParams(const bool encrypt) { // Compress the QByteArray. QByteArray compressedData = qCompress(jsonData); - QByteArray processed_data = encrypt ? CommaApi::rsa_encrypt(compressedData) : compressedData; + QByteArray processed_data = encrypt ? SunnylinkApi::rsa_encrypt(compressedData) : compressedData; // Encode the compressed QByteArray in Base64. auto converted_params_processed_base64_data = QString(processed_data.toBase64()); @@ -278,8 +301,8 @@ QByteArray BackupSettings::backupParams(const bool encrypt) { void BackupSettings::sendParams(const QByteArray &payload) { if (auto sl_dongle_id = getSunnylinkDongleId()) { QString url = SUNNYLINK_BASE_URL + "/backup/" + *sl_dongle_id; - auto request = new HttpRequest(this, true, 10000, true); - connect(request, &HttpRequest::requestDone, [=](const QString &resp, bool success) { + auto request = new HttpRequestSP(this, true, 10000, true); + connect(request, &HttpRequestSP::requestDone, [=](const QString &resp, bool success) { if (success && resp != "[]") { ConfirmationDialog::alert(tr("Settings backed up for sunnylink Device ID:") + " " + *sl_dongle_id, this); } else if (resp == "[]") { @@ -318,7 +341,7 @@ void BackupSettings::restoreParams(const QString &resp) { if (configValue.isString()) { // Decode the Base64-encoded "config" value const QByteArray configJsonDataDecoded = QByteArray::fromBase64(configValue.toString().toUtf8()); - const QByteArray configJsonDataCompressed = isEncrypted ? CommaApi::rsa_decrypt(configJsonDataDecoded) : configJsonDataDecoded; + const QByteArray configJsonDataCompressed = isEncrypted ? SunnylinkApi::rsa_decrypt(configJsonDataDecoded) : configJsonDataDecoded; const QByteArray configJsonData = qUncompress(configJsonDataCompressed); // Convert the decompressed JSON data to a QJsonDocument @@ -346,8 +369,8 @@ void BackupSettings::restoreParams(const QString &resp) { void BackupSettings::getParams() { if (auto sl_dongle_id = getSunnylinkDongleId()) { QString url = SUNNYLINK_BASE_URL + "/backup/" + *sl_dongle_id; - auto request = new HttpRequest(this, true, 10000, true); - connect(request, &HttpRequest::requestDone, [=](const QString &resp, bool success) { + auto request = new HttpRequestSP(this, true, 10000, true); + connect(request, &HttpRequestSP::requestDone, [=](const QString &resp, bool success) { bool restart_ui = false; if (success && resp != "[]") { restoreParams(resp); @@ -408,7 +431,7 @@ void SunnylinkSponsorQRWidget::refresh() { QString qrString; if (sponsor_pair) { - QString token = CommaApi::create_jwt({}, 3600, true); + QString token = SunnylinkApi::create_jwt({}, 3600, true); auto sl_dongle_id = getSunnylinkDongleId(); QByteArray payload = QString("1|" + sl_dongle_id.value_or("") + "|" + token).toUtf8().toBase64(); qrString = SUNNYLINK_BASE_URL + "/sso?state=" + payload; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.h similarity index 58% rename from selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.h index fdca62d1c5..2f0885107f 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/sunnylink_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.h @@ -1,14 +1,38 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once #include #include -#include "selfdrive/ui/qt/network/sunnylink/sunnylink_client.h" +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" const QString SUNNYLINK_BASE_URL = util::getenv("SUNNYLINK_API_HOST", "https://stg.api.sunnypilot.ai").c_str(); // sponsor QR code @@ -78,18 +102,18 @@ public slots: private: - ParamControl* sunnylinkEnabledBtn; + ParamControlSP* sunnylinkEnabledBtn; QStackedLayout* main_layout = nullptr; QWidget* sunnylinkScreen = nullptr; - ScrollView *scrollView = nullptr; + ScrollViewSP *scrollView = nullptr; BackupSettings *backup_settings; SubPanelButton *restoreSettings; SubPanelButton *backupSettings; SunnylinkSponsorPopup *status_popup; SunnylinkSponsorPopup *pair_popup; - ButtonControl* sponsorBtn; - ButtonControl* pairSponsorBtn; + ButtonControlSP* sponsorBtn; + ButtonControlSP* pairSponsorBtn; SunnylinkClient* sunnylink_client; bool is_onroad = false; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.cc similarity index 54% rename from selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.cc index 405eca50ce..ed1561be1d 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.cc @@ -1,4 +1,30 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.h" CustomOffsetsSettings::CustomOffsetsSettings(QWidget* parent) : QWidget(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); @@ -10,25 +36,25 @@ CustomOffsetsSettings::CustomOffsetsSettings(QWidget* parent) : QWidget(parent) connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); - ListWidget *list = new ListWidget(this, false); + ListWidgetSP *list = new ListWidgetSP(this, false); // Controls: Camera Offset (cm) camera_offset = new CameraOffset(); - connect(camera_offset, &SPOptionControl::updateLabels, camera_offset, &CameraOffset::refresh); + connect(camera_offset, &OptionControlSP::updateLabels, camera_offset, &CameraOffset::refresh); camera_offset->showDescription(); list->addItem(camera_offset); // Controls: Path Offset (cm) path_offset = new PathOffset(); - connect(path_offset, &SPOptionControl::updateLabels, path_offset, &PathOffset::refresh); + connect(path_offset, &OptionControlSP::updateLabels, path_offset, &PathOffset::refresh); path_offset->showDescription(); list->addItem(path_offset); - main_layout->addWidget(new ScrollView(list, this)); + main_layout->addWidget(new ScrollViewSP(list, this)); } // Camera Offset Value -CameraOffset::CameraOffset() : SPOptionControl ( +CameraOffset::CameraOffset() : OptionControlSP( "CameraOffset", tr("Camera Offset - Laneful Only"), tr("Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm"), @@ -48,7 +74,7 @@ void CameraOffset::refresh() { } // Path Offset Value -PathOffset::PathOffset() : SPOptionControl ( +PathOffset::PathOffset() : OptionControlSP( "PathOffset", tr("Path Offset"), tr("Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately."), diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.h new file mode 100644 index 0000000000..780f5fc233 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.h @@ -0,0 +1,74 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +class CameraOffset : public OptionControlSP { + Q_OBJECT + +public: + CameraOffset(); + + void refresh(); + +private: + Params params; +}; + +class PathOffset : public OptionControlSP { + Q_OBJECT + +public: + PathOffset(); + + void refresh(); + +private: + Params params; +}; + +class CustomOffsetsSettings : public QWidget { + Q_OBJECT + +public: + explicit CustomOffsetsSettings(QWidget* parent = nullptr); + +signals: + void backPress(); + +private: + Params params; + std::map toggles; + + CameraOffset *camera_offset; + PathOffset *path_offset; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.cc similarity index 68% rename from selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.cc index 8f16091460..5d47349837 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.cc @@ -1,4 +1,35 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.h" + +#include +#include +#include +#include LaneChangeSettings::LaneChangeSettings(QWidget* parent) : QWidget(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); @@ -10,7 +41,7 @@ LaneChangeSettings::LaneChangeSettings(QWidget* parent) : QWidget(parent) { connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); - ListWidget *list = new ListWidget(this, false); + ListWidgetSP *list = new ListWidgetSP(this, false); // param, title, desc, icon std::vector> toggle_defs{ { @@ -37,17 +68,17 @@ LaneChangeSettings::LaneChangeSettings(QWidget* parent) : QWidget(parent) { auto_lane_change_timer = new AutoLaneChangeTimer(); auto_lane_change_timer->setUpdateOtherToggles(true); auto_lane_change_timer->showDescription(); - connect(auto_lane_change_timer, &SPOptionControl::updateLabels, auto_lane_change_timer, &AutoLaneChangeTimer::refresh); + connect(auto_lane_change_timer, &OptionControlSP::updateLabels, auto_lane_change_timer, &AutoLaneChangeTimer::refresh); connect(auto_lane_change_timer, &AutoLaneChangeTimer::updateOtherToggles, this, &LaneChangeSettings::updateToggles); list->addItem(auto_lane_change_timer); // Pause Lateral Below Speed w/ Blinker pause_lateral_speed = new PauseLateralSpeed(); pause_lateral_speed->showDescription(); - connect(pause_lateral_speed, &SPOptionControl::updateLabels, pause_lateral_speed, &PauseLateralSpeed::refresh); + connect(pause_lateral_speed, &OptionControlSP::updateLabels, pause_lateral_speed, &PauseLateralSpeed::refresh); for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); + auto toggle = new ParamControlSP(param, title, desc, icon, this); list->addItem(toggle); toggles[param.toStdString()] = toggle; @@ -57,14 +88,14 @@ LaneChangeSettings::LaneChangeSettings(QWidget* parent) : QWidget(parent) { } } - connect(toggles["BelowSpeedPause"], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles["BelowSpeedPause"], &ToggleControlSP::toggleFlipped, [=](bool state) { pause_lateral_speed->setEnabled(state); pause_lateral_speed->setVisible(state); }); pause_lateral_speed->setEnabled(toggles["BelowSpeedPause"]->isToggled()); pause_lateral_speed->setVisible(toggles["BelowSpeedPause"]->isToggled()); - main_layout->addWidget(new ScrollView(list, this)); + main_layout->addWidget(new ScrollViewSP(list, this)); } void LaneChangeSettings::showEvent(QShowEvent *event) { @@ -97,10 +128,12 @@ void LaneChangeSettings::updateToggles() { } // Auto Lane Change Timer (ALCT) -AutoLaneChangeTimer::AutoLaneChangeTimer() : SPOptionControl ( +AutoLaneChangeTimer::AutoLaneChangeTimer() : OptionControlSP( "AutoLaneChangeTimer", tr("Auto Lane Change by Blinker"), - tr("Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge.\nPlease use caution when using this feature. Only use the blinker when traffic and road conditions permit."), + tr("Set a timer to delay the auto lane change operation when the blinker is used. " + "No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge.\n" + "Please use caution when using this feature. Only use the blinker when traffic and road conditions permit."), "../assets/offroad/icon_blank.png", {-1, 5}) { @@ -127,7 +160,7 @@ void AutoLaneChangeTimer::refresh() { } } -PauseLateralSpeed::PauseLateralSpeed() : SPOptionControl ( +PauseLateralSpeed::PauseLateralSpeed() : OptionControlSP( "PauseLateralSpeed", "", tr("Pause lateral actuation with blinker when traveling below the desired speed selected. Default is 20 MPH or 32 km/h."), diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.h new file mode 100644 index 0000000000..f3cf0be588 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.h @@ -0,0 +1,86 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +class AutoLaneChangeTimer : public OptionControlSP { + Q_OBJECT + +public: + AutoLaneChangeTimer(); + + void refresh(); + +signals: + void toggleUpdated(); + +private: + Params params; +}; + +class PauseLateralSpeed : public OptionControlSP { + Q_OBJECT + +public: + PauseLateralSpeed(); + + void refresh(); + + signals: + void ToggleUpdated(); + +private: + Params params; +}; + + +class LaneChangeSettings : public QWidget { + Q_OBJECT + +public: + explicit LaneChangeSettings(QWidget* parent = nullptr); + void showEvent(QShowEvent *event) override; + +signals: + void backPress(); + +public slots: + void updateToggles(); + +private: + Params params; + std::map toggles; + + AutoLaneChangeTimer *auto_lane_change_timer; + PauseLateralSpeed *pause_lateral_speed; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc similarity index 52% rename from selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc index e213cc19a7..610ee160bc 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc @@ -1,4 +1,33 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h" + +#include +#include MadsSettings::MadsSettings(QWidget* parent) : QWidget(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); @@ -10,7 +39,7 @@ MadsSettings::MadsSettings(QWidget* parent) : QWidget(parent) { connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); - ListWidget *list = new ListWidget(this, false); + ListWidgetSP *list = new ListWidgetSP(this, false); // param, title, desc, icon std::vector> toggle_defs{ { @@ -32,31 +61,31 @@ MadsSettings::MadsSettings(QWidget* parent) : QWidget(parent) { // Disengage Lateral on Brake (DLOB) std::vector dlob_settings_texts{tr("Remain Active"), tr("Pause Steering")}; - dlob_settings = new ButtonParamControl( + dlob_settings = new ButtonParamControlSP( "DisengageLateralOnBrake", tr("Steering Mode After Braking"), - tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\nRemain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed."), - "../assets/offroad/icon_blank.png", + tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\n" + "Remain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed."), + "", dlob_settings_texts, - 500 - ); + 500); dlob_settings->showDescription(); list->addItem(dlob_settings); for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); + auto toggle = new ParamControlSP(param, title, desc, icon, this); list->addItem(toggle); toggles[param.toStdString()] = toggle; // trigger offroadTransition when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, toggle, &ParamControl::setEnabled); + connect(uiStateSP(), &UIStateSP::offroadTransition, toggle, &ParamControlSP::setEnabled); } // trigger offroadTransition when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, dlob_settings, &ButtonParamControl::setEnabled); + connect(uiStateSP(), &UIStateSP::offroadTransition, dlob_settings, &ButtonParamControlSP::setEnabled); - main_layout->addWidget(new ScrollView(list, this)); + main_layout->addWidget(new ScrollViewSP(list, this)); } void MadsSettings::showEvent(QShowEvent *event) { @@ -68,7 +97,7 @@ void MadsSettings::updateToggles() { return; } - const bool is_offroad = !uiState()->scene.started; + const bool is_offroad = !uiStateSP()->scene.started; const bool enable_mads = params.getBool("EnableMads"); const bool enabled = is_offroad && enable_mads; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h new file mode 100644 index 0000000000..e88bf6a90d --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h @@ -0,0 +1,54 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +class MadsSettings : public QWidget { + Q_OBJECT + +public: + explicit MadsSettings(QWidget* parent = nullptr); + void showEvent(QShowEvent *event) override; + +signals: + void backPress(); + +public slots: + void updateToggles(); + +private: + Params params; + std::map toggles; + + ButtonParamControlSP *dlob_settings; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.cc similarity index 68% rename from selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.cc index 7686f0a873..29c1191998 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.cc @@ -1,4 +1,32 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.h" + +#include SlcSettings::SlcSettings(QWidget* parent) : QWidget(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); @@ -10,34 +38,32 @@ SlcSettings::SlcSettings(QWidget* parent) : QWidget(parent) { connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); - ListWidget *list = new ListWidget(this, false); + ListWidgetSP *list = new ListWidgetSP(this, false); std::vector speed_limit_engage_texts{tr("Auto"), tr("User Confirm")}; - speed_limit_engage_settings = new ButtonParamControl( + speed_limit_engage_settings = new ButtonParamControlSP( "SpeedLimitEngageType", tr("Engage Mode"), "", - "../assets/offroad/icon_blank.png", + "", speed_limit_engage_texts, - 440 - ); + 440); speed_limit_engage_settings->showDescription(); list->addItem(speed_limit_engage_settings); std::vector speed_limit_offset_settings_texts{tr("Default"), tr("Fixed"), tr("Percentage")}; - speed_limit_offset_settings = new ButtonParamControl( + speed_limit_offset_settings = new ButtonParamControlSP( "SpeedLimitOffsetType", tr("Limit Offset"), tr("Set speed limit slightly higher than actual speed limit for a more natural drive."), - "../assets/offroad/icon_blank.png", + "", speed_limit_offset_settings_texts, - 380 - ); + 380); list->addItem(speed_limit_offset_settings); slvo = new SpeedLimitValueOffset(); - connect(slvo, &SPOptionControl::updateLabels, slvo, &SpeedLimitValueOffset::refresh); + connect(slvo, &OptionControlSP::updateLabels, slvo, &SpeedLimitValueOffset::refresh); list->addItem(slvo); - connect(speed_limit_offset_settings, &ButtonParamControl::buttonToggled, this, &SlcSettings::updateToggles); - connect(speed_limit_offset_settings, &ButtonParamControl::buttonToggled, slvo, &SpeedLimitValueOffset::refresh); + connect(speed_limit_offset_settings, &ButtonParamControlSP::buttonToggled, this, &SlcSettings::updateToggles); + connect(speed_limit_offset_settings, &ButtonParamControlSP::buttonToggled, slvo, &SpeedLimitValueOffset::refresh); param_watcher = new ParamWatcher(this); @@ -45,7 +71,7 @@ SlcSettings::SlcSettings(QWidget* parent) : QWidget(parent) { updateToggles(); }); - main_layout->addWidget(new ScrollView(list, this)); + main_layout->addWidget(new ScrollViewSP(list, this)); } void SlcSettings::showEvent(QShowEvent *event) { @@ -101,7 +127,7 @@ void SlcSettings::updateToggles() { } // Speed Limit Control Custom Offset -SpeedLimitValueOffset::SpeedLimitValueOffset() : SPOptionControl ( +SpeedLimitValueOffset::SpeedLimitValueOffset() : OptionControlSP( "SpeedLimitValueOffset", "", "", diff --git a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.h similarity index 50% rename from selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.h index 877064e577..a03de75b45 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_control_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.h @@ -1,11 +1,41 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" +#include +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/util.h" -class SpeedLimitValueOffset : public SPOptionControl { +class SpeedLimitValueOffset : public OptionControlSP { Q_OBJECT public: @@ -33,11 +63,11 @@ public slots: private: Params params; - std::map toggles; + std::map toggles; SpeedLimitValueOffset *slvo; - ButtonParamControl *speed_limit_offset_settings; - ButtonParamControl *speed_limit_engage_settings; + ButtonParamControlSP *speed_limit_offset_settings; + ButtonParamControlSP *speed_limit_engage_settings; ParamWatcher *param_watcher; QString slcDescriptionBuilder(QString param, std::vector descriptions) { diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.cc new file mode 100644 index 0000000000..d3e3eda41c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.cc @@ -0,0 +1,76 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.h" + +SpeedLimitPolicySettings::SpeedLimitPolicySettings(QWidget* parent) : QWidget(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 20, 50, 20); + main_layout->setSpacing(20); + + // Back button + PanelBackButton* back = new PanelBackButton(); + connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); + main_layout->addWidget(back, 0, Qt::AlignLeft); + + ListWidgetSP *list = new ListWidgetSP(this, false); + + speed_limit_policy = new ButtonParamControlSP( + "SpeedLimitControlPolicy", + tr("Speed Limit Source Policy"), + "", + "", + speed_limit_policy_texts, + 250); + speed_limit_policy->showDescription(); + connect(speed_limit_policy, &ButtonParamControlSP::buttonToggled, this, &SpeedLimitPolicySettings::updateToggles); + list->addItem(speed_limit_policy); + + param_watcher = new ParamWatcher(this); + + QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { + updateToggles(); + }); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void SpeedLimitPolicySettings::showEvent(QShowEvent *event) { + updateToggles(); +} + +void SpeedLimitPolicySettings::updateToggles() { + param_watcher->addParam("SpeedLimitControlPolicy"); + + if (!isVisible()) { + return; + } + + // TODO: SP: use upstream's setCheckedButton + speed_limit_policy->setButton("SpeedLimitControlPolicy"); + + speed_limit_policy->setDescription(speedLimitPolicyDescriptionBuilder("SpeedLimitControlPolicy", speed_limit_policy_descriptions)); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.h similarity index 59% rename from selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.h index 5b7ef66a68..68337af596 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.h @@ -1,8 +1,37 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/util.h" class SpeedLimitPolicySettings : public QWidget { @@ -23,7 +52,7 @@ public slots: private: Params params; - ButtonParamControl *speed_limit_policy; + ButtonParamControlSP *speed_limit_policy; ParamWatcher *param_watcher; QString speedLimitPolicyDescriptionBuilder(QString param, std::vector descriptions) { diff --git a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.cc similarity index 64% rename from selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.cc index 6b355e2740..a521b3458b 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.cc @@ -1,4 +1,30 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "../../settings/sunnypilot/speed_limit_warning_settings.h" SpeedLimitWarningSettings::SpeedLimitWarningSettings(QWidget* parent) : QWidget(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); @@ -10,45 +36,42 @@ SpeedLimitWarningSettings::SpeedLimitWarningSettings(QWidget* parent) : QWidget( connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); - ListWidget *list = new ListWidget(this, false); + ListWidgetSP *list = new ListWidgetSP(this, false); std::vector speed_limit_warning_texts{tr("Off"), tr("Display"), tr("Chime")}; - speed_limit_warning_settings = new ButtonParamControl( + speed_limit_warning_settings = new ButtonParamControlSP( "SpeedLimitWarningType", tr("Speed Limit Warning"), "", - "../assets/offroad/icon_blank.png", + "", speed_limit_warning_texts, - 380 - ); + 380); speed_limit_warning_settings->showDescription(); list->addItem(speed_limit_warning_settings); - speed_limit_warning_flash = new ParamControl( + speed_limit_warning_flash = new ParamControlSP( "SpeedLimitWarningFlash", tr("Warning with speed limit flash"), tr("When Speed Limit Warning is enabled, the speed limit sign will alert the driver when the cruising speed is faster than then speed limit plus the offset."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); list->addItem(speed_limit_warning_flash); std::vector speed_limit_warning_offset_settings_texts{tr("Default"), tr("Fixed"), tr("Percentage")}; - speed_limit_warning_offset_settings = new ButtonParamControl( + speed_limit_warning_offset_settings = new ButtonParamControlSP( "SpeedLimitWarningOffsetType", tr("Warning Offset"), tr("Select the desired offset to warn the driver when the vehicle is driving faster than the speed limit."), - "../assets/offroad/icon_blank.png", + "", speed_limit_warning_offset_settings_texts, - 380 - ); + 380); list->addItem(speed_limit_warning_offset_settings); slwvo = new SpeedLimitWarningValueOffset(); - connect(slwvo, &SPOptionControl::updateLabels, slwvo, &SpeedLimitWarningValueOffset::refresh); + connect(slwvo, &OptionControlSP::updateLabels, slwvo, &SpeedLimitWarningValueOffset::refresh); list->addItem(slwvo); - connect(speed_limit_warning_settings, &ButtonParamControl::buttonToggled, this, &SpeedLimitWarningSettings::updateToggles); + connect(speed_limit_warning_settings, &ButtonParamControlSP::buttonToggled, this, &SpeedLimitWarningSettings::updateToggles); - connect(speed_limit_warning_offset_settings, &ButtonParamControl::buttonToggled, this, &SpeedLimitWarningSettings::updateToggles); - connect(speed_limit_warning_offset_settings, &ButtonParamControl::buttonToggled, slwvo, &SpeedLimitWarningValueOffset::refresh); + connect(speed_limit_warning_offset_settings, &ButtonParamControlSP::buttonToggled, this, &SpeedLimitWarningSettings::updateToggles); + connect(speed_limit_warning_offset_settings, &ButtonParamControlSP::buttonToggled, slwvo, &SpeedLimitWarningValueOffset::refresh); param_watcher = new ParamWatcher(this); @@ -56,7 +79,7 @@ SpeedLimitWarningSettings::SpeedLimitWarningSettings(QWidget* parent) : QWidget( updateToggles(); }); - main_layout->addWidget(new ScrollView(list, this)); + main_layout->addWidget(new ScrollViewSP(list, this)); } void SpeedLimitWarningSettings::showEvent(QShowEvent *event) { @@ -86,7 +109,7 @@ void SpeedLimitWarningSettings::updateToggles() { } // Speed Limit Control Custom Offset -SpeedLimitWarningValueOffset::SpeedLimitWarningValueOffset() : SPOptionControl ( +SpeedLimitWarningValueOffset::SpeedLimitWarningValueOffset() : OptionControlSP( "SpeedLimitWarningValueOffset", "", "", diff --git a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.h similarity index 50% rename from selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.h rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.h index ce3c48ac3f..95510ad0ab 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/speed_limit_warning_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.h @@ -1,11 +1,41 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" +#include +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/util.h" -class SpeedLimitWarningValueOffset : public SPOptionControl { +class SpeedLimitWarningValueOffset : public OptionControlSP { Q_OBJECT public: @@ -33,12 +63,12 @@ public slots: private: Params params; - std::map toggles; + std::map toggles; SpeedLimitWarningValueOffset *slwvo; - ButtonParamControl *speed_limit_warning_offset_settings; - ParamControl *speed_limit_warning_flash; - ButtonParamControl *speed_limit_warning_settings; + ButtonParamControlSP *speed_limit_warning_offset_settings; + ParamControlSP *speed_limit_warning_flash; + ButtonParamControlSP *speed_limit_warning_settings; ParamWatcher *param_watcher; QString speedLimitWarningDescriptionBuilder(QString param, std::vector descriptions) { diff --git a/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.cc similarity index 85% rename from selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.cc index 8db23500b4..c363e78844 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.cc @@ -1,9 +1,40 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.h" + +#include +#include + +#include "common/model.h" SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { main_layout = new QStackedLayout(this); - ListWidget *list = new ListWidget(this, false); + ListWidgetSP *list = new ListWidgetSP(this, false); // param, title, desc, icon std::vector> toggle_defs{ { @@ -21,7 +52,8 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { { "EnableSlc", tr("Speed Limit Control (SLC)"), - tr("When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. The maximum cruising speed will always be the MAX set speed."), + tr("When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. " + "The maximum cruising speed will always be the MAX set speed."), "../assets/offroad/icon_blank.png", }, { @@ -80,7 +112,8 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { { "CustomTorqueLateral", tr("Enable Custom Tuning"), - tr("Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within \"selfdrive/torque_data\". The values will also be used live when \"Override Self-Tune\" toggle is enabled."), + tr("Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within \"selfdrive/torque_data\". " + "The values will also be used live when \"Override Self-Tune\" toggle is enabled."), "../assets/offroad/icon_blank.png", }, { @@ -100,7 +133,8 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { tr("Green Traffic Light Chime (Beta)"), QString("%1
" "

%2


") - .arg(tr("A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged.")) + .arg(tr("A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. " + "If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged.")) .arg(tr("Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly.")), "../assets/offroad/icon_blank.png", }, @@ -235,23 +269,22 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { // Controls: Torque - FRICTION friction = new TorqueFriction(); - connect(friction, &SPOptionControl::updateLabels, friction, &TorqueFriction::refresh); + connect(friction, &OptionControlSP::updateLabels, friction, &TorqueFriction::refresh); // Controls: Torque - LAT_ACCEL_FACTOR lat_accel_factor = new TorqueMaxLatAccel(); - connect(lat_accel_factor, &SPOptionControl::updateLabels, lat_accel_factor, &TorqueMaxLatAccel::refresh); + connect(lat_accel_factor, &OptionControlSP::updateLabels, lat_accel_factor, &TorqueMaxLatAccel::refresh); std::vector dlp_settings_texts{tr("Laneful"), tr("Laneless"), tr("Auto")}; - dlp_settings = new ButtonParamControl( + dlp_settings = new ButtonParamControlSP( "DynamicLaneProfile", tr("Dynamic Lane Profile"), "", - "../assets/offroad/icon_blank.png", + "", dlp_settings_texts, - 340 - ); + 340); dlp_settings->showDescription(); for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); + auto toggle = new ParamControlSP(param, title, desc, icon, this); list->addItem(toggle); toggles[param.toStdString()] = toggle; @@ -269,7 +302,7 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { list->addItem(laneChangeSettingsLayout); list->addItem(horizontal_line()); - list->addItem(new LabelControl(tr("Speed Limit Assist"))); + list->addItem(new LabelControlSP(tr("Speed Limit Assist"))); } if (param == "EnableSlc") { @@ -299,7 +332,7 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { } } - connect(toggles["NNFF"], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles["NNFF"], &ToggleControlSP::toggleFlipped, [=](bool state) { if (state) { toggles["EnforceTorqueLateral"]->setEnabled(false); params.putBool("EnforceTorqueLateral", false); @@ -315,7 +348,7 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { // trigger updateToggles() when toggleFlipped for (const auto& updateToggleName : updateTogglesNames) { if (toggles.find(updateToggleName) != toggles.end()) { - connect(toggles[updateToggleName], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles[updateToggleName], &ToggleControlSP::toggleFlipped, [=](bool state) { updateToggles(); }); } @@ -324,7 +357,7 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { // trigger offroadTransition when going onroad/offroad for (const auto& offroadName : toggleOffroad) { if (toggles.find(offroadName) != toggles.end()) { - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { toggles[offroadName]->setEnabled(offroad); }); } @@ -334,27 +367,27 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { toggles["EndToEndLongAlertLight"]->setConfirmation(true, false); toggles["CustomOffsets"]->showDescription(); - connect(toggles["EnableMads"], &ToggleControl::toggleFlipped, mads_settings, &MadsSettings::updateToggles); - connect(toggles["EnableMads"], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles["EnableMads"], &ToggleControlSP::toggleFlipped, mads_settings, &MadsSettings::updateToggles); + connect(toggles["EnableMads"], &ToggleControlSP::toggleFlipped, [=](bool state) { madsSettings->setEnabled(state); }); madsSettings->setEnabled(toggles["EnableMads"]->isToggled()); - connect(toggles["EnableSlc"], &ToggleControl::toggleFlipped, slc_settings, &SlcSettings::updateToggles); - connect(toggles["EnableSlc"], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles["EnableSlc"], &ToggleControlSP::toggleFlipped, slc_settings, &SlcSettings::updateToggles); + connect(toggles["EnableSlc"], &ToggleControlSP::toggleFlipped, [=](bool state) { slcSettings->setEnabled(state); slcSettings->setVisible(state); }); slcSettings->setEnabled(toggles["EnableSlc"]->isToggled()); slcSettings->setVisible(toggles["EnableSlc"]->isToggled()); - connect(toggles["CustomOffsets"], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles["CustomOffsets"], &ToggleControlSP::toggleFlipped, [=](bool state) { customOffsetsSettings->setEnabled(state); }); customOffsetsSettings->setEnabled(toggles["CustomOffsets"]->isToggled()); // update "FRICTION" and "LAT_ACCEL_FACTOR" titles when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { friction->setEnabled(offroad || toggles["TorquedOverride"]->isToggled()); lat_accel_factor->setEnabled(offroad || toggles["TorquedOverride"]->isToggled()); @@ -363,7 +396,7 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { }); // update "FRICTION" and "LAT_ACCEL_FACTOR" titles when TorquedOverride is flipped - connect(toggles["TorquedOverride"], &ToggleControl::toggleFlipped, [=](bool state) { + connect(toggles["TorquedOverride"], &ToggleControlSP::toggleFlipped, [=](bool state) { friction->setEnabled(params.getBool("IsOffroad") || state); lat_accel_factor->setEnabled(params.getBool("IsOffroad") || state); @@ -384,7 +417,7 @@ SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { QVBoxLayout* vlayout = new QVBoxLayout(sunnypilotScreen); vlayout->setContentsMargins(50, 20, 50, 20); - scrollView = new ScrollView(list, this); + scrollView = new ScrollViewSP(list, this); vlayout->addWidget(scrollView, 1); main_layout->addWidget(sunnypilotScreen); main_layout->addWidget(mads_settings); @@ -467,7 +500,8 @@ void SunnypilotPanel::updateToggles() { // NNLC/NNFF QString nnff_available_desc = tr("NNLC is currently not available on this platform."); - QString nnff_fuzzy_desc = tr("Match: \"Exact\" is ideal, but \"Fuzzy\" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: ") + "#tuning-nnlc"; + QString nnff_fuzzy_desc = tr("Match: \"Exact\" is ideal, but \"Fuzzy\" is fine too. Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server if there are any issues: ") + + "#tuning-nnlc"; QString nnff_status_init = "⚠️ " + tr("Start the car to check car compatibility") + ""; QString nnff_not_loaded = "⚠️ " + tr("NNLC Not Loaded") + ""; QString nnff_loaded = "✅ " + tr("NNLC Loaded") + ""; @@ -493,9 +527,11 @@ void SunnypilotPanel::updateToggles() { QString nn_model_name = QString::fromStdString(CP.getLateralTuning().getTorque().getNnModelName()); QString nn_fuzzy = CP.getLateralTuning().getTorque().getNnModelFuzzyMatch() ? tr("Fuzzy") : tr("Exact"); - nnff_toggle->setDescription(nnffDescriptionBuilder((nn_model_name == "") ? nnff_status_init : - (nn_model_name == "mock") ? (nnff_not_loaded + "
" + tr("Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: ") + "#tuning-nnlc") : - (nnff_loaded + " | " + tr("Match") + " = " + nn_fuzzy + " | " + _car_model + "

" + nnff_fuzzy_desc))); + nnff_toggle->setDescription(nnffDescriptionBuilder( + (nn_model_name == "") ? nnff_status_init : + (nn_model_name == "mock") ? (nnff_not_loaded + "
" + tr("Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server and donate logs to get NNLC loaded for your car: ") + + "#tuning-nnlc") : + (nnff_loaded + " | " + tr("Match") + " = " + nn_fuzzy + " | " + _car_model + "

" + nnff_fuzzy_desc))); enforce_torque_lateral->setEnabled(false); } else { nnff_toggle->setDescription(nnffDescriptionBuilder(nnff_status_init)); @@ -567,7 +603,7 @@ void SunnypilotPanel::updateToggles() { } // toggle names to update when CustomTorqueLateral is flipped - std::vector customTorqueGroup{friction, lat_accel_factor}; + std::vector customTorqueGroup{friction, lat_accel_factor}; for (const auto& customTorqueControl : customTorqueGroup) { customTorqueControl->setVisible(!(nnff_toggle->isToggled() || !custom_torque_lateral->isToggled())); customTorqueControl->setEnabled(!(nnff_toggle->isToggled() || !custom_torque_lateral->isToggled())); @@ -581,7 +617,7 @@ void SunnypilotPanel::updateToggles() { dlp_settings->setDescription((model_use_lateral_planner ? "" : dlp_incompatible_desc + "

") + dlp_description); } -TorqueFriction::TorqueFriction() : SPOptionControl ( +TorqueFriction::TorqueFriction() : OptionControlSP( "TorqueFriction", tr("FRICTION"), tr("Adjust Friction for the Torque Lateral Controller. Live: Override self-tune values; Offline: Override self-tune offline values at car restart."), @@ -597,7 +633,7 @@ void TorqueFriction::refresh() { setLabel(QString::number(torqueFrictionVal)); } -TorqueMaxLatAccel::TorqueMaxLatAccel() : SPOptionControl ( +TorqueMaxLatAccel::TorqueMaxLatAccel() : OptionControlSP( "TorqueMaxLatAccel", tr("LAT_ACCEL_FACTOR"), tr("Adjust Max Lateral Acceleration for the Torque Lateral Controller. Live: Override self-tune values; Offline: Override self-tune offline values at car restart."), diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.h new file mode 100644 index 0000000000..b87854492c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.h @@ -0,0 +1,115 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/custom_offsets_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/lane_change_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_control_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_warning_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +class TorqueFriction : public OptionControlSP { + Q_OBJECT + +public: + TorqueFriction(); + + void refresh(); + +private: + Params params; +}; + +class TorqueMaxLatAccel : public OptionControlSP { + Q_OBJECT + +public: + TorqueMaxLatAccel(); + + void refresh(); + +private: + Params params; +}; + +class SunnypilotPanel : public QFrame { + Q_OBJECT + +public: + explicit SunnypilotPanel(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent* event) override; + +public slots: + void updateToggles(); + +private: + QStackedLayout* main_layout = nullptr; + QWidget* sunnypilotScreen = nullptr; + MadsSettings* mads_settings = nullptr; + SubPanelButton *slcSettings = nullptr; + SlcSettings* slc_settings = nullptr; + SubPanelButton *slwSettings = nullptr; + SpeedLimitWarningSettings* slw_settings = nullptr; + SubPanelButton *slpSettings = nullptr; + SpeedLimitPolicySettings* slp_settings = nullptr; + LaneChangeSettings* lane_change_settings = nullptr; + CustomOffsetsSettings* custom_offsets_settings = nullptr; + Params params; + std::map toggles; + ParamWatcher *param_watcher; + + TorqueFriction *friction; + TorqueMaxLatAccel *lat_accel_factor; + ButtonParamControlSP *dlp_settings; + + ScrollViewSP *scrollView = nullptr; + + const QString nnff_description = QString("%1

" + "%2") + .arg(tr("Formerly known as \"NNFF\", this replaces the lateral \"torque\" controller, " + "with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy.")) + .arg(tr("Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server with feedback, " + "or to provide log data for your car if your car is currently unsupported: ") + "#tuning-nnlc"); + + QString nnffDescriptionBuilder(const QString &custom_description) { + QString description = "" + custom_description + "

" + nnff_description; + return description; + } + + const QString custom_offsets_description = QString(tr("Add custom offsets to Camera and Path in sunnypilot.")); + const QString dlp_description = QString( + tr("Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions.")); +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.cc new file mode 100644 index 0000000000..b69be20ded --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.cc @@ -0,0 +1,55 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.h" + +TripsPanel::TripsPanel(QWidget* parent) : QFrame(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setMargin(0); + + // main content + main_layout->addSpacing(25); + center_layout = new QStackedLayout(); + + driveStatsWidget = new DriveStats; + driveStatsWidget->setStyleSheet(R"( + QLabel[type="title"] { font-size: 51px; font-weight: 500; } + QLabel[type="number"] { font-size: 78px; font-weight: 500; } + QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; } + )"); + center_layout->addWidget(driveStatsWidget); + + main_layout->addLayout(center_layout, 1); + + setStyleSheet(R"( + * { + color: white; + } + TripsPanel > QLabel { + font-size: 55px; + } + )"); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.h new file mode 100644 index 0000000000..11c0798364 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.h @@ -0,0 +1,47 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#endif +#include "selfdrive/ui/sunnypilot/qt/widgets/drive_stats.h" + +class TripsPanel : public QFrame { + Q_OBJECT + +public: + explicit TripsPanel(QWidget* parent = 0); + +private: + Params params; + + QStackedLayout* center_layout; + DriveStats *driveStatsWidget; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.cc similarity index 75% rename from selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.cc index dd5d457926..10ff22c028 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.cc @@ -1,4 +1,33 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/util.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" VehiclePanel::VehiclePanel(QWidget *parent) : QWidget(parent) { main_layout = new QStackedLayout(this); @@ -29,7 +58,7 @@ VehiclePanel::VehiclePanel(QWidget *parent) : QWidget(parent) { QVBoxLayout* toggle_layout = new QVBoxLayout(home_widget); home_widget->setObjectName("homeWidget"); - ScrollView *scroller = new ScrollView(home_widget, this); + ScrollViewSP *scroller = new ScrollViewSP(home_widget, this); scroller->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); fcr_layout->addWidget(scroller, 1); @@ -67,60 +96,55 @@ void VehiclePanel::updateToggles() { setCarBtn->setText(((set == "=== Not Selected ===") || (set.length() == 0)) ? prompt_select : set); } -SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(VehiclePanel *parent) : ListWidget(parent, false) { +SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(VehiclePanel *parent) : ListWidgetSP(parent, false) { setSpacing(50); // Hyundai/Kia/Genesis - addItem(new LabelControl(tr("Hyundai/Kia/Genesis"))); - auto hkgSmoothStop = new ParamControl( + addItem(new LabelControlSP(tr("Hyundai/Kia/Genesis"))); + auto hkgSmoothStop = new ParamControlSP( "HkgSmoothStop", tr("HKG CAN: Smoother Stopping Performance (Beta)"), tr("Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); hkgSmoothStop->setConfirmation(true, false); addItem(hkgSmoothStop); // Subaru - addItem(new LabelControl(tr("Subaru"))); - auto subaruManualParkingBrakeSng = new ParamControl( + addItem(new LabelControlSP(tr("Subaru"))); + auto subaruManualParkingBrakeSng = new ParamControlSP( "SubaruManualParkingBrakeSng", tr("Manual Parking Brake: Stop and Go (Beta)"), tr("Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation!"), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); subaruManualParkingBrakeSng->setConfirmation(true, false); addItem(subaruManualParkingBrakeSng); // Toyota/Lexus - addItem(new LabelControl(tr("Toyota/Lexus"))); - stockLongToyota = new ParamControl( + addItem(new LabelControlSP(tr("Toyota/Lexus"))); + stockLongToyota = new ParamControlSP( "StockLongToyota", tr("Enable Stock Toyota Longitudinal Control"), tr("sunnypilot will not take over control of gas and brakes. Stock Toyota longitudinal control will be used."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); stockLongToyota->setConfirmation(true, false); addItem(stockLongToyota); - auto lkasToggle = new ParamControl( + auto lkasToggle = new ParamControlSP( "LkasToggle", tr("Allow M.A.D.S. toggling w/ LKAS Button (Beta)"), QString("%1
" "

%2


") .arg(tr("Allows M.A.D.S. engagement/disengagement with \"LKAS\" button from the steering wheel.")) .arg(tr("Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly.")), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); lkasToggle->setConfirmation(true, false); addItem(lkasToggle); - auto toyotaTss2LongTune = new ParamControl( + auto toyotaTss2LongTune = new ParamControlSP( "ToyotaTSS2Long", tr("Toyota TSS2 Longitudinal: Custom Tuning"), tr("Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); toyotaTss2LongTune->setConfirmation(true, false); addItem(toyotaTss2LongTune); @@ -128,51 +152,46 @@ SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(VehiclePanel *parent) : ListWidge "ToyotaEnhancedBsm", tr("Enable Enhanced Blind Spot Monitor"), "", - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); toyotaEnhancedBsm->setConfirmation(true, false); addItem(toyotaEnhancedBsm); - auto toyotaSngHack = new ParamControl( + auto toyotaSngHack = new ParamControlSP( "ToyotaSnG", tr("Enable Toyota Stop and Go Hack"), tr("sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); toyotaSngHack->setConfirmation(true, false); addItem(toyotaSngHack); - auto toyotaAutoLock = new ParamControl( + auto toyotaAutoLock = new ParamControlSP( "ToyotaAutoLock", tr("Enable Toyota Door Auto Locking"), tr("sunnypilot will attempt to lock the doors when drive above 10 km/h (6.2 mph).\nReboot Required."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); toyotaAutoLock->setConfirmation(true, false); addItem(toyotaAutoLock); - auto toyotaAutoUnlock = new ParamControl( + auto toyotaAutoUnlock = new ParamControlSP( "ToyotaAutoUnlockByShifter", tr("Enable Toyota Door Auto Unlocking"), tr("sunnypilot will attempt to unlock the doors when shift to gear P.\nReboot Required."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); toyotaAutoUnlock->setConfirmation(true, false); addItem(toyotaAutoUnlock); // Volkswagen - addItem(new LabelControl(tr("Volkswagen"))); - auto volkswagenCCOnly = new ParamControl( + addItem(new LabelControlSP(tr("Volkswagen"))); + auto volkswagenCCOnly = new ParamControlSP( "VwCCOnly", tr("Enable CC Only support"), tr("sunnypilot supports Volkswagen MQB CC only platforms with this toggle enabled. Only enable this toggle if your car does not have ACC from the factory."), - "../assets/offroad/icon_blank.png" - ); + "../assets/offroad/icon_blank.png"); volkswagenCCOnly->setConfirmation(true, false); addItem(volkswagenCCOnly); // trigger offroadTransition when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { is_onroad = !offroad; hkgSmoothStop->setEnabled(offroad); toyotaTss2LongTune->setEnabled(offroad); diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.h new file mode 100644 index 0000000000..ddf61f6e76 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.h @@ -0,0 +1,82 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class VehiclePanel : public QWidget { + Q_OBJECT + +public: + explicit VehiclePanel(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + +public slots: + void updateToggles(); + +private: + Params params; + + QStackedLayout* main_layout = nullptr; + QWidget* home = nullptr; + + QPushButton* setCarBtn; + QString set; + + QWidget* home_widget; + QString prompt_select = tr("Select your car"); +}; + +class SPVehiclesTogglesPanel : public ListWidgetSP { + Q_OBJECT +public: + explicit SPVehiclesTogglesPanel(VehiclePanel *parent); + void showEvent(QShowEvent *event) override; + +public slots: + void updateToggles(); + +private: + Params params; + bool is_onroad = false; + + ParamControlSP *stockLongToyota; + ParamControlSP *toyotaEnhancedBsm; + + const QString toyotaEnhancedBsmDescription = QString("%1

" + "%2") + .arg(tr("sunnypilot will use debugging CAN messages to receive unfiltered BSM signals, allowing detection of more objects.")) + .arg(tr("Tested on RAV4 TSS1, Lexus LSS1, Toyota TSS1/1.5, and Prius TSS2.")); + + QString toyotaEnhancedBsmDesciptionBuilder(const QString &custom_description) { + QString description = "" + custom_description + "

" + toyotaEnhancedBsmDescription; + return description; + } +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.cc similarity index 68% rename from selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc rename to selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.cc index 742725e8ab..a444b546cb 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.cc @@ -1,6 +1,35 @@ -#include "selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h" +/** +The MIT License -VisualsPanel::VisualsPanel(QWidget *parent) : ListWidget(parent) { +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.h" + +#include +#include + +VisualsPanel::VisualsPanel(QWidget *parent) : ListWidgetSP(parent) { // param, title, desc, icon std::vector> toggle_defs{ { @@ -73,26 +102,24 @@ VisualsPanel::VisualsPanel(QWidget *parent) : ListWidget(parent) { // Visuals: Developer UI Info (Dev UI) std::vector dev_ui_settings_texts{tr("Off"), tr("5 Metrics"), tr("10 Metrics")}; - dev_ui_settings = new ButtonParamControl( + dev_ui_settings = new ButtonParamControlSP( "DevUIInfo", tr("Developer UI"), tr("Display real-time parameters and metrics from various sources."), - "../assets/offroad/icon_blank.png", + "", dev_ui_settings_texts, - 380 - ); + 380); dev_ui_settings->showDescription(); // Visuals: Display Metrics above Chevron std::vector chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Time"), tr("All")}; - chevron_info_settings = new ButtonParamControl( + chevron_info_settings = new ButtonParamControlSP( "ChevronInfo", tr("Display Metrics Below Chevron"), tr("Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control)."), - "../assets/offroad/icon_blank.png", + "", chevron_info_settings_texts, - 300 - ); + 300); chevron_info_settings->showDescription(); for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); + auto toggle = new ParamControlSP(param, title, desc, icon, this); addItem(toggle); toggles[param.toStdString()] = toggle; @@ -107,20 +134,19 @@ VisualsPanel::VisualsPanel(QWidget *parent) : ListWidget(parent) { } std::vector sidebar_temp_texts{tr("Off"), tr("RAM"), tr("CPU"), tr("GPU"), tr("Max")}; - sidebar_temp_setting = new ButtonParamControl( + sidebar_temp_setting = new ButtonParamControlSP( "SidebarTemperatureOptions", tr("Display Temperature on Sidebar"), "", - "../assets/offroad/icon_blank.png", + "", sidebar_temp_texts, - 255 - ); + 255); sidebar_temp_setting->showDescription(); addItem(sidebar_temp_setting); // trigger offroadTransition when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { }); - QObject::connect(toggles["MapboxFullScreen"], &ToggleControl::toggleFlipped, [=](bool state) { + QObject::connect(toggles["MapboxFullScreen"], &ToggleControlSP::toggleFlipped, [=](bool state) { toggles["MapboxFullScreen"]->showDescription(); }); } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.h new file mode 100644 index 0000000000..d90b36e21d --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.h @@ -0,0 +1,48 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class VisualsPanel : public ListWidgetSP { + Q_OBJECT + +public: + explicit VisualsPanel(QWidget *parent = nullptr); + +private: + Params params; + std::map toggles; + + ButtonParamControlSP *dev_ui_settings; + ButtonParamControlSP *chevron_info_settings; + ButtonParamControlSP *sidebar_temp_setting; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad_home.cc b/selfdrive/ui/sunnypilot/qt/offroad_home.cc new file mode 100644 index 0000000000..e0791bd11e --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad_home.cc @@ -0,0 +1,58 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/offroad_home.h" + +#include + +#include "selfdrive/ui/qt/offroad/experimental_mode.h" +#include "selfdrive/ui/qt/widgets/prime.h" +#include "selfdrive/ui/sunnypilot/sunnypilot_main.h" + +#ifdef ENABLE_MAPS +#define LEFT_WIDGET MapSettings +#else +#define LEFT_WIDGET QWidget +#endif + +void OffroadHomeSP::replaceLeftWidget(){ + auto* new_left_widget = new QStackedWidget(left_widget->parentWidget()); + new_left_widget->addWidget(new LEFT_WIDGET); + if (!custom_mapbox) + new_left_widget->addWidget(new PrimeAdWidget); + + new_left_widget->setStyleSheet("border-radius: 10px;"); + new_left_widget->setCurrentIndex((uiStateSP()->hasPrime() || custom_mapbox) ? 0 : 1); + connect(uiStateSP(), &UIStateSP::primeChanged, [=](bool prime) { + new_left_widget->setCurrentIndex((prime || custom_mapbox) ? 0 : 1); + }); + ReplaceWidget(left_widget, new_left_widget); +} + +OffroadHomeSP::OffroadHomeSP(QWidget* parent) : OffroadHome(parent){ + custom_mapbox = QString::fromStdString(params.get("CustomMapboxTokenSk")) != ""; + replaceLeftWidget(); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad_home.h b/selfdrive/ui/sunnypilot/qt/offroad_home.h new file mode 100644 index 0000000000..a7c1a36ef2 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad_home.h @@ -0,0 +1,45 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once +#include "selfdrive/ui/qt/offroad_home.h" + +#ifdef ENABLE_MAPS +#include "selfdrive/ui/qt/maps/map_settings.h" +#endif + +class OffroadHomeSP : public OffroadHome { + Q_OBJECT + +public: + void replaceLeftWidget(); + explicit OffroadHomeSP(QWidget* parent = 0); + +private: + // static void replaceWidget(QWidget* old_widget, QWidget* new_widget); + Params params; + bool custom_mapbox; +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc new file mode 100644 index 0000000000..bebac31d90 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc @@ -0,0 +1,1535 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h" + +#include +#include +#include +#include + +#include "common/swaglog.h" +#include "selfdrive/ui/qt/onroad/buttons.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/qt/util.h" + +static std::pair getFeatureStatus(int value, QStringList text_list, QStringList color_list, + bool condition, QString off_text) { + + QString text("Error"); + QColor color("#ffffff"); + + for (int i = 0; i < text_list.size() && i < color_list.size(); ++i) { + if (value == i) { + text = condition ? text_list[i] : off_text; + color = condition ? QColor(color_list[i]) : QColor("#ffffff"); + break; // Exit the loop once a match is found + } + } + + return {text, color}; +} + + +// Window that shows camera view and variety of info drawn on top +AnnotatedCameraWidgetSP::AnnotatedCameraWidgetSP(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) { + pm = std::make_unique>({"uiDebug"}); + e2e_state = std::make_unique>({"e2eLongStateSP"}); + + main_layout = new QVBoxLayout(this); + main_layout->setMargin(UI_BORDER_SIZE); + main_layout->setSpacing(0); + + experimental_btn = new ExperimentalButtonSP(this); + main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight); + + onroad_settings_btn = new OnroadSettingsButton(this); + + map_settings_btn = new MapSettingsButton(this); + + dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5}); + map_img = loadPixmap("../assets/img_world_icon.png", {subsign_img_size, subsign_img_size}); + left_img = loadPixmap("../assets/img_turn_left_icon.png", {subsign_img_size, subsign_img_size}); + right_img = loadPixmap("../assets/img_turn_right_icon.png", {subsign_img_size, subsign_img_size}); + + buttons_layout = new QHBoxLayout(); + buttons_layout->setContentsMargins(0, 0, 10, 20); + main_layout->addLayout(buttons_layout); + updateButtonsLayout(false); +} + +void AnnotatedCameraWidgetSP::mousePressEvent(QMouseEvent* e) { + bool propagate_event = true; + + UIStateSP *s = uiStateSP(); + UISceneSP &scene = s->scene; + const SubMaster &sm = *(s->sm); + const auto longitudinal_plan_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP(); + + if (longitudinal_plan_sp.getSpeedLimit() > 0.0 && sl_sign_rect.contains(e->x(), e->y())) { + // If touching the speed limit sign area when visible + scene.last_speed_limit_sign_tap = seconds_since_boot(); + params.putBool("LastSpeedLimitSignTap", true); + scene.speed_limit_control_enabled = !scene.speed_limit_control_enabled; + params.putBool("EnableSlc", scene.speed_limit_control_enabled); + propagate_event = false; + } + + if (propagate_event) { + QWidget::mousePressEvent(e); + } +} + +void AnnotatedCameraWidgetSP::updateButtonsLayout(bool is_rhd) { + QLayoutItem *item; + while ((item = buttons_layout->takeAt(0)) != nullptr) { + delete item; + } + + buttons_layout->setContentsMargins(0, 0, 10, rn_offset != 0 ? rn_offset + 10 : 20); + + if (is_rhd) { + buttons_layout->addSpacing(map_settings_btn->isVisible() ? 30 : 0); + buttons_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignLeft); + + buttons_layout->addStretch(1); + + buttons_layout->addWidget(onroad_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight); + buttons_layout->addSpacing(onroad_settings_btn->isVisible() ? 216 : 0); + } else { + buttons_layout->addSpacing(onroad_settings_btn->isVisible() ? 216 : 0); + buttons_layout->addWidget(onroad_settings_btn, 0, Qt::AlignBottom | Qt::AlignLeft); + + buttons_layout->addStretch(1); + + buttons_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight); + buttons_layout->addSpacing(map_settings_btn->isVisible() ? 30 : 0); // Add spacing to the right + } +} + +void AnnotatedCameraWidgetSP::updateState(const UIStateSP &s) { + const int SET_SPEED_NA = 255; + const SubMaster &sm = *(s.sm); + + const bool cs_alive = sm.alive("controlsState"); + const bool nav_alive = sm.alive("navInstruction") && sm["navInstruction"].getValid(); + const auto cs = sm["controlsState"].getControlsState(); + const auto cs_sp = sm["controlsStateSP"].getControlsStateSP(); + const auto car_state = sm["carState"].getCarState(); + const auto nav_instruction = sm["navInstruction"].getNavInstruction(); + const auto car_control = sm["carControl"].getCarControl(); + const auto radar_state = sm["radarState"].getRadarState(); + const auto is_gps_location_external = sm.rcv_frame("gpsLocationExternal") > 1; + const auto gpsLocation = is_gps_location_external ? sm["gpsLocationExternal"].getGpsLocationExternal() : sm["gpsLocation"].getGpsLocation(); + const auto ltp = sm["liveTorqueParameters"].getLiveTorqueParameters(); + const auto lateral_plan_sp = sm["lateralPlanSPDEPRECATED"].getLateralPlanSPDEPRECATED(); + car_params = sm["carParams"].getCarParams(); + + // Handle older routes where vCruiseCluster is not set + float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster(); + setSpeed = cs_alive ? v_cruise : SET_SPEED_NA; + is_cruise_set = setSpeed > 0 && (int)setSpeed != SET_SPEED_NA; + if (is_cruise_set && !s.scene.is_metric) { + setSpeed *= KM_TO_MILE; + } + + // Handle older routes where vEgoCluster is not set + v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0; + float v_ego = v_ego_cluster_seen ? car_state.getVEgoCluster() : car_state.getVEgo(); + v_ego = s.scene.true_vego_ui ? car_state.getVEgo() : v_ego; + speed = cs_alive ? std::max(0.0, v_ego) : 0.0; + speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH; + + auto speed_limit_sign = nav_instruction.getSpeedLimitSign(); + speedLimit = nav_alive ? nav_instruction.getSpeedLimit() : 0.0; + speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + + has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD); + has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA); + is_metric = s.scene.is_metric; + speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph"); + hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE); + status = s.status; + + // TODO: Add minimum speed? + left_blindspot = cs_alive && car_state.getLeftBlindspot(); + right_blindspot = cs_alive && car_state.getRightBlindspot(); + + steerOverride = car_state.getSteeringPressed(); + gasOverride = car_state.getGasPressed(); + latActive = car_control.getLatActive(); + madsEnabled = car_state.getMadsEnabled(); + + brakeLights = car_state.getBrakeLightsDEPRECATED() && s.scene.visual_brake_lights; + + standStillTimer = s.scene.stand_still_timer; + standStill = car_state.getStandstill(); + standstillElapsedTime = lateral_plan_sp.getStandstillElapsed(); + + hideVEgoUi = s.scene.hide_vego_ui; + + splitPanelVisible = s.scene.map_visible || s.scene.onroad_settings_visible; + + // ############################## DEV UI START ############################## + lead_d_rel = radar_state.getLeadOne().getDRel(); + lead_v_rel = radar_state.getLeadOne().getVRel(); + lead_status = radar_state.getLeadOne().getStatus(); + lateralState = QString::fromStdString(cs_sp.getLateralState()); + angleSteers = car_state.getSteeringAngleDeg(); + steerAngleDesired = cs.getLateralControlState().getPidState().getSteeringAngleDesiredDeg(); + curvature = cs.getCurvature(); + roll = sm["liveParameters"].getLiveParameters().getRoll(); + memoryUsagePercent = sm["deviceState"].getDeviceState().getMemoryUsagePercent(); + devUiInfo = s.scene.dev_ui_info; + gpsAccuracy = is_gps_location_external ? gpsLocation.getHorizontalAccuracy() : 1.0; //External reports accuracy, internal does not. + altitude = gpsLocation.getAltitude(); + vEgo = car_state.getVEgo(); + aEgo = car_state.getAEgo(); + steeringTorqueEps = car_state.getSteeringTorqueEps(); + bearingAccuracyDeg = gpsLocation.getBearingAccuracyDeg(); + bearingDeg = gpsLocation.getBearingDeg(); + torquedUseParams = (ltp.getUseParams() || s.scene.live_torque_toggle) && !s.scene.torqued_override; + latAccelFactorFiltered = ltp.getLatAccelFactorFiltered(); + frictionCoefficientFiltered = ltp.getFrictionCoefficientFiltered(); + liveValid = ltp.getLiveValid(); + // ############################## DEV UI END ############################## + + btnPerc = s.scene.sleep_btn_opacity * 0.05; + + left_blinker = car_state.getLeftBlinker(); + right_blinker = car_state.getRightBlinker(); + lane_change_edge_block = lateral_plan_sp.getLaneChangeEdgeBlockDEPRECATED(); + + // update engageability/experimental mode button + experimental_btn->updateState(s); + + // update onroad settings button state + onroad_settings_btn->updateState(s); + + // update DM icon + auto dm_state = sm["driverMonitoringState"].getDriverMonitoringState(); + dmActive = dm_state.getIsActiveMode(); + rightHandDM = dm_state.getIsRHD(); + // DM icon transition + dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0); + + // update buttons layout + updateButtonsLayout(rightHandDM); + + // hide map settings button for alerts and flip for right hand DM + if (map_settings_btn->isEnabled()) { + map_settings_btn->setVisible(!hideBottomIcons); + buttons_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); + } + + // hide onroad settings button for alerts and flip for right hand DM + if (onroad_settings_btn->isEnabled()) { + onroad_settings_btn->setVisible(!hideBottomIcons); + buttons_layout->setAlignment(onroad_settings_btn, (rightHandDM ? Qt::AlignRight : Qt::AlignLeft) | Qt::AlignBottom); + } + + const auto lp_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP(); + slcState = lp_sp.getSpeedLimitControlState(); + + speedLimitControlToggle = s.scene.speed_limit_control_enabled; + + const auto vtcState = lp_sp.getVisionTurnControllerState(); + const float vtc_speed = lp_sp.getVisionTurnSpeed() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + const auto lpSoruce = lp_sp.getLongitudinalPlanSource(); + QColor vtc_color = tcs_colors[int(vtcState)]; + vtc_color.setAlpha(lpSoruce == cereal::LongitudinalPlanSP::LongitudinalPlanSource::TURN ? 255 : 100); + + showVTC = vtcState > cereal::LongitudinalPlanSP::VisionTurnControllerState::DISABLED; + vtcSpeed = QString::number(std::nearbyint(vtc_speed)); + vtcColor = vtc_color; + showDebugUI = s.scene.show_debug_ui; + + const auto lmd_sp = sm["liveMapDataSP"].getLiveMapDataSP(); + + const auto data_type = int(lmd_sp.getDataType()); + const QString data_type_draw(data_type == 2 ? "🌐 " : ""); + roadName = QString::fromStdString(lmd_sp.getCurrentRoadName()); + roadName = !roadName.isEmpty() ? data_type_draw + roadName : ""; + + float speed_limit_slc = lp_sp.getSpeedLimit() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + const float speed_limit_offset = lp_sp.getSpeedLimitOffset() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + const bool sl_force_active = speedLimitControlToggle && + seconds_since_boot() < s.scene.last_speed_limit_sign_tap + 2.0; + const bool sl_inactive = !sl_force_active && (!speedLimitControlToggle || + slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::INACTIVE); + const bool sl_temp_inactive = !sl_force_active && (speedLimitControlToggle && + slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::TEMP_INACTIVE); + const bool sl_pre_active = !sl_force_active && (speedLimitControlToggle && + slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::PRE_ACTIVE); + const int sl_distance = int(lp_sp.getDistToSpeedLimit() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH) / 10.0) * 10; + const QString sl_distance_str(QString::number(sl_distance) + (s.scene.is_metric ? "m" : "f")); + const QString sl_offset_str(speed_limit_offset > 0.0 ? speed_limit_offset < 0.0 ? + "-" + QString::number(std::nearbyint(std::abs(speed_limit_offset))) : + "+" + QString::number(std::nearbyint(speed_limit_offset)) : ""); + const QString sl_inactive_str(sl_temp_inactive && s.scene.speed_limit_control_engage_type == 0 ? "TEMP" : ""); + const QString sl_substring(sl_inactive || sl_temp_inactive || sl_pre_active ? sl_inactive_str : + sl_distance > 0 ? sl_distance_str : sl_offset_str); + + showSpeedLimit = speed_limit_slc > 0.0; + speedLimitSLC = speed_limit_slc; + speedLimitSLCOffset = speed_limit_offset; + slcSubText = sl_substring; + slcSubTextSize = sl_inactive || sl_temp_inactive || sl_distance > 0 ? 25.0 : 27.0; + mapSourcedSpeedLimit = lp_sp.getIsMapSpeedLimit(); + slcActive = !sl_inactive && !sl_temp_inactive; + overSpeedLimit = showSpeedLimit && s.scene.speed_limit_warning_type != 0 && + (std::nearbyint(speed_limit_slc + s.scene.speed_limit_warning_value_offset) < std::nearbyint(speed)); + plus_arrow_up_img = loadPixmap("../assets/img_plus_arrow_up", {105, 105}); + minus_arrow_down_img = loadPixmap("../assets/img_minus_arrow_down", {105, 105}); + + const float tsc_speed = lp_sp.getTurnSpeed() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + const auto tscState = lp_sp.getTurnSpeedControlState(); + const int t_distance = int(lp_sp.getDistToTurn() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH) / 10.0) * 10; + const QString t_distance_str(QString::number(t_distance) + (s.scene.is_metric ? "m" : "f")); + + showTurnSpeedLimit = tsc_speed > 0.0 && std::round(tsc_speed) < 224 && (tsc_speed < speed || s.scene.show_debug_ui); + turnSpeedLimit = QString::number(std::nearbyint(tsc_speed)); + tscSubText = t_distance > 0 ? t_distance_str : QString(""); + tscActive = tscState > cereal::LongitudinalPlanSP::SpeedLimitControlState::TEMP_INACTIVE; + curveSign = lp_sp.getTurnSign(); + + // TODO: Add toggle variables to cereal, and parse from cereal + longitudinalPersonality = s.scene.longitudinal_personality; + dynamicLaneProfile = s.scene.dynamic_lane_profile; + mpcMode = QString::fromStdString(lp_sp.getE2eBlended()); + mpcMode = (mpcMode == "blended") ? mpcMode.replace(0, 1, mpcMode[0].toUpper()) : mpcMode.toUpper(); + + static int reverse_delay = 0; + bool reverse_allowed = false; + if (int(car_state.getGearShifter()) != 4) { + reverse_delay = 0; + reverse_allowed = false; + } else { + reverse_delay += 50; + if (reverse_delay >= 1000) { + reverse_allowed = true; + } + } + + reversing = reverse_allowed; + + cruiseStateEnabled = car_state.getCruiseState().getEnabled(); + + int e2eLStatus = 0; + static bool chime_sent = false; + static int chime_count = 0; + int chime_prompt = 0; + static float last_lead_distance = -1; + const float lead_distance = radar_state.getLeadOne().getDRel(); + + if (s.scene.e2eX[12] > 30 && car_state.getVEgo() < 1.0) { + e2eLStatus = 2; + } else if ((s.scene.e2eX[12] > 0 && s.scene.e2eX[12] < 80) || s.scene.e2eX[12] < 0) { + e2eLStatus = 1; + } else { + e2eLStatus = 0; + } + + if (!car_state.getStandstill()) { + chime_prompt = 0; + chime_sent = false; + chime_count = 0; + + if (last_lead_distance != -1) { + last_lead_distance = -1; + } + } + + if ((cruiseStateEnabled || car_state.getBrakeLightsDEPRECATED()) && !car_state.getGasPressed() && car_state.getStandstill()) { + if (e2eLStatus == 2 && !radar_state.getLeadOne().getStatus()) { + if (chime_sent) { + chime_count = 0; + } else { + chime_count += 1; + } + if (s.scene.e2e_long_alert_light && chime_count >= 2 && !chime_sent) { + chime_prompt = 1; + chime_sent = true; + } else { + chime_prompt = 0; + } + } else if (radar_state.getLeadOne().getStatus()) { + if ((last_lead_distance == -1) || (lead_distance < last_lead_distance)) { + last_lead_distance = lead_distance; + } + if (s.scene.e2e_long_alert_lead && (lead_distance - last_lead_distance > 1.0) && !chime_sent) { + chime_prompt = 2; + chime_sent = true; + } else { + chime_prompt = 0; + } + } else { + chime_prompt = 0; + } + } else { + } + + e2eStatus = chime_prompt; + e2eState = e2eLStatus; + e2eLongAlertUi = s.scene.e2e_long_alert_ui; + dynamicExperimentalControlToggle = s.scene.dynamic_experimental_control; + speedLimitWarningFlash = s.scene.speed_limit_warning_flash; + experimentalMode = cs.getExperimentalMode(); + + featureStatusToggle = s.scene.feature_status_toggle; + + experimental_btn->setVisible(!(showDebugUI && showVTC)); + drivingModelGen = s.scene.driving_model_generation; +} + +void AnnotatedCameraWidgetSP::drawHud(QPainter &p) { + p.save(); + + // Header gradient + QLinearGradient bg(0, UI_HEADER_HEIGHT - (UI_HEADER_HEIGHT / 2.5), 0, UI_HEADER_HEIGHT); + bg.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.45)); + bg.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0)); + p.fillRect(0, 0, width(), UI_HEADER_HEIGHT, bg); + + QString speedLimitStr = (speedLimit > 1) ? QString::number(std::nearbyint(speedLimit)) : "–"; + QString speedLimitStrSlc = showSpeedLimit ? QString::number(std::nearbyint(speedLimitSLC)) : "–"; + QString speedStr = QString::number(std::nearbyint(speed)); + QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : "–"; + const bool isNavSpeedLimit = has_us_speed_limit || has_eu_speed_limit; + + // Draw outer box + border to contain set speed and speed limit + const int sign_margin = 12; + const int us_sign_height = slcSubText == "" ? 186 : 216; + const int eu_sign_size = 176; + + const QSize default_size = {172, 204}; + QSize set_speed_size = default_size; + if (is_metric || has_eu_speed_limit) set_speed_size.rwidth() = 200; + if ((mapSourcedSpeedLimit && !is_metric && speedLimitStrSlc.size() >= 3) || + (has_us_speed_limit && speedLimitStr.size() >= 3)) set_speed_size.rwidth() = 223; + + if ((mapSourcedSpeedLimit && !is_metric) || has_us_speed_limit) set_speed_size.rheight() += us_sign_height + sign_margin; + else if ((mapSourcedSpeedLimit && is_metric) || has_eu_speed_limit) set_speed_size.rheight() += eu_sign_size + sign_margin; + + int top_radius = 32; + int bottom_radius = ((mapSourcedSpeedLimit && is_metric) || has_eu_speed_limit) ? 100 : 32; + + QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size); + p.setPen(QPen(whiteColor(75), 6)); + p.setBrush(blackColor(166)); + drawRoundedRect(p, set_speed_rect, top_radius, top_radius, bottom_radius, bottom_radius); + + // Draw MAX + QColor max_color = QColor(0x80, 0xd8, 0xa6, 0xff); + QColor set_speed_color = whiteColor(); + if (is_cruise_set) { + if (status == STATUS_DISENGAGED) { + max_color = whiteColor(); + } else if (status == STATUS_OVERRIDE && gasOverride) { + max_color = QColor(0x91, 0x9b, 0x95, 0xff); + } else if (speedLimitSLC > 0) { + auto interp_color = [=](QColor c1, QColor c2, QColor c3) { + return speedLimitSLC > 0 ? interpColor(setSpeed, {speedLimitSLC + 5, speedLimitSLC + 15, speedLimitSLC + 25}, {c1, c2, c3}) : c1; + }; + max_color = interp_color(max_color, QColor(0xff, 0xe4, 0xbf), QColor(0xff, 0xbf, 0xbf)); + set_speed_color = interp_color(set_speed_color, QColor(0xff, 0x95, 0x00), QColor(0xff, 0x00, 0x00)); + } else if (speedLimit > 0) { + auto interp_color = [=](QColor c1, QColor c2, QColor c3) { + return speedLimit > 0 ? interpColor(setSpeed, {speedLimit + 5, speedLimit + 15, speedLimit + 25}, {c1, c2, c3}) : c1; + }; + max_color = interp_color(max_color, QColor(0xff, 0xe4, 0xbf), QColor(0xff, 0xbf, 0xbf)); + set_speed_color = interp_color(set_speed_color, QColor(0xff, 0x95, 0x00), QColor(0xff, 0x00, 0x00)); + } + } else { + max_color = QColor(0xa6, 0xa6, 0xa6, 0xff); + set_speed_color = QColor(0x72, 0x72, 0x72, 0xff); + } + p.setFont(InterFont(40, QFont::DemiBold)); + p.setPen(max_color); + p.drawText(set_speed_rect.adjusted(0, 27, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("MAX")); + p.setFont(InterFont(90, QFont::Bold)); + p.setPen(set_speed_color); + p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr); + + const QRect sign_rect = set_speed_rect.adjusted(sign_margin, default_size.height(), -sign_margin, -sign_margin); + sl_sign_rect = sign_rect; + + speedLimitWarning(p, sign_rect, sign_margin); + + // US/Canada (MUTCD style) sign + if (((mapSourcedSpeedLimit && !is_metric && !isNavSpeedLimit) || has_us_speed_limit) && slcShowSign) { + p.setPen(Qt::NoPen); + p.setBrush(whiteColor()); + p.drawRoundedRect(sign_rect, 24, 24); + p.setPen(QPen(blackColor(), 6)); + p.drawRoundedRect(sign_rect.adjusted(9, 9, -9, -9), 16, 16); + + p.setFont(InterFont(28, QFont::DemiBold)); + p.drawText(sign_rect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("SPEED")); + p.drawText(sign_rect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT")); + p.setFont(InterFont(70, QFont::Bold)); + if (overSpeedLimit) p.setPen(QColor(255, 0, 0, 255)); + else p.setPen(blackColor()); + p.drawText(sign_rect.adjusted(0, 85, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitStrSlc); + + // Speed limit offset value + p.setFont(InterFont(32, QFont::Bold)); + p.setPen(blackColor()); + p.drawText(sign_rect.adjusted(0, 85 + 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, slcSubText); + } + + // EU (Vienna style) sign + if (((mapSourcedSpeedLimit && is_metric && !isNavSpeedLimit) || has_eu_speed_limit) && slcShowSign) { + p.setPen(Qt::NoPen); + p.setBrush(whiteColor()); + p.drawEllipse(sign_rect); + p.setPen(QPen(Qt::red, 20)); + p.drawEllipse(sign_rect.adjusted(16, 16, -16, -16)); + + p.setFont(InterFont((speedLimitStrSlc.size() >= 3) ? 60 : 70, QFont::Bold)); + if (overSpeedLimit) p.setPen(QColor(255, 0, 0, 255)); + else p.setPen(blackColor()); + p.drawText(sign_rect, Qt::AlignCenter, speedLimitStrSlc); + + // Speed limit offset value + p.setFont(InterFont(slcSubTextSize, QFont::Bold)); + p.setPen(blackColor()); + p.drawText(sign_rect.adjusted(0, 27, 0, 0), Qt::AlignTop | Qt::AlignHCenter, slcSubText); + } + + // current speed + if (!hideVEgoUi) { + p.setFont(InterFont(176, QFont::Bold)); + drawColoredText(p, rect().center().x(), 210, speedStr, brakeLights ? QColor(0xff, 0, 0, 255) : QColor(0xff, 0xff, 0xff, 255)); + p.setFont(InterFont(66)); + drawText(p, rect().center().x(), 290, speedUnit, 200); + } + + if (!reversing) { + // ####### 1 ROW ####### + QRect bar_rect1(rect().left(), rect().bottom() - 60, rect().width(), 61); + if (!splitPanelVisible && devUiInfo == 2) { + p.setPen(Qt::NoPen); + p.setBrush(QColor(0, 0, 0, 100)); + p.drawRect(bar_rect1); + drawNewDevUi2(p, bar_rect1.left(), bar_rect1.center().y()); + } + + // ####### 1 COLUMN ######## + QRect rc2(rect().right() - (UI_BORDER_SIZE * 2), UI_BORDER_SIZE * 1.5, 184, 152); + if (devUiInfo != 0) { + drawRightDevUi(p, rect().right() - 184 - UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2 + rc2.height()); + } + + int rn_btn = 0; + rn_btn = !splitPanelVisible && devUiInfo == 2 ? 35 : 0; + rn_offset = rn_btn; + + // Stand Still Timer + if (standStillTimer && standStill && !splitPanelVisible) { + drawStandstillTimer(p, rect().right() - 650, 30 + 160 + 250); + } + + // V-TSC + if (showDebugUI && showVTC) { + drawVisionTurnControllerUI(p, rect().right() - 184 - (UI_BORDER_SIZE * 1.5), int(UI_BORDER_SIZE * 1.5), 184, vtcColor, vtcSpeed, 100); + } + + // Bottom bar road name + if (showDebugUI && !roadName.isEmpty()) { + int font_size = splitPanelVisible ? 38 : 50; + int h = splitPanelVisible ? 18 : 26; + p.setFont(InterFont(font_size, QFont::Bold)); + drawRoadNameText(p, rect().center().x(), h, roadName, QColor(255, 255, 255, 255)); + } + + // Turn Speed Sign + if (showTurnSpeedLimit) { + QRect rc = sign_rect; + rc.moveTop(sign_rect.bottom() + UI_BORDER_SIZE); + drawTrunSpeedSign(p, rc, turnSpeedLimit, tscSubText, curveSign, tscActive); + } + } + + // E2E Status + if (e2eLongAlertUi && e2eState != 0) { + drawE2eStatus(p, UI_BORDER_SIZE * 2 + 190, 45, 150, 150, e2eState); + } + + if (!hideBottomIcons && featureStatusToggle) { + int x = UI_BORDER_SIZE * 2 + (rightHandDM ? 600 : 370); + int feature_status_text_x = rightHandDM ? rect().right() - x : x; + drawFeatureStatusText(p, feature_status_text_x, rect().bottom() - 160 - rn_offset); + } + + p.restore(); +} + +void AnnotatedCameraWidgetSP::drawText(QPainter &p, int x, int y, const QString &text, int alpha) { + QRect real_rect = p.fontMetrics().boundingRect(text); + real_rect.moveCenter({x, y - real_rect.height() / 2}); + + p.setPen(QColor(0xff, 0xff, 0xff, alpha)); + p.drawText(real_rect.x(), real_rect.bottom(), text); +} + +void AnnotatedCameraWidgetSP::drawColoredText(QPainter &p, int x, int y, const QString &text, QColor color) { + QRect real_rect = p.fontMetrics().boundingRect(text); + real_rect.moveCenter({x, y - real_rect.height() / 2}); + + p.setPen(color); + p.drawText(real_rect.x(), real_rect.bottom(), text); +} + +void AnnotatedCameraWidgetSP::drawCenteredText(QPainter &p, int x, int y, const QString &text, QColor color) { + QRect real_rect = p.fontMetrics().boundingRect(text); + real_rect.moveCenter({x, y}); + + p.setPen(color); + p.drawText(real_rect, Qt::AlignCenter, text); +} + +void AnnotatedCameraWidgetSP::drawRoadNameText(QPainter &p, int x, int y, const QString &text, QColor color) { + QRect real_rect = p.fontMetrics().boundingRect(text); + real_rect.moveCenter({x, y}); + + QRect real_rect_adjusted(real_rect); + real_rect_adjusted.adjust(-UI_ROAD_NAME_MARGIN_X, 5, UI_ROAD_NAME_MARGIN_X, 0); + QPainterPath path; + path.addRoundedRect(real_rect_adjusted, 10, 10); + p.setPen(Qt::NoPen); + p.setBrush(QColor(0, 0, 0, 100)); + p.drawPath(path); + + p.setPen(color); + p.drawText(real_rect, Qt::AlignCenter, text); +} + +void AnnotatedCameraWidgetSP::drawVisionTurnControllerUI(QPainter &p, int x, int y, int size, const QColor &color, + const QString &vision_speed, int alpha) { + QRect rvtc(x, y, size, size); + p.setPen(QPen(color, 10)); + p.setBrush(QColor(0, 0, 0, alpha)); + p.drawRoundedRect(rvtc, 20, 20); + p.setPen(Qt::NoPen); + + p.setFont(InterFont(56, QFont::DemiBold)); + drawCenteredText(p, rvtc.center().x(), rvtc.center().y(), vision_speed, color); +} + +void AnnotatedCameraWidgetSP::drawStandstillTimer(QPainter &p, int x, int y) { + char lab_str[16]; + char val_str[16]; + int minute = (int)(standstillElapsedTime / 60); + int second = (int)((standstillElapsedTime) - (minute * 60)); + + if (standStill) { + snprintf(lab_str, sizeof(lab_str), "STOP"); + snprintf(val_str, sizeof(val_str), "%01d:%02d", minute, second); + } + + p.setFont(InterFont(125, QFont::DemiBold)); + drawColoredText(p, x, y, QString(lab_str), QColor(255, 175, 3, 240)); + p.setFont(InterFont(150, QFont::DemiBold)); + drawColoredText(p, x, y + 150, QString(val_str), QColor(255, 255, 255, 240)); +} + +void AnnotatedCameraWidgetSP::drawCircle(QPainter &p, int x, int y, int r, QBrush bg) { + p.setPen(Qt::NoPen); + p.setBrush(bg); + p.drawEllipse(x - r, y - r, 2 * r, 2 * r); +} + +void AnnotatedCameraWidgetSP::drawSpeedSign(QPainter &p, QRect rc, const QString &speed_limit, const QString &sub_text, + int subtext_size, bool is_map_sourced, bool is_active) { + const QColor ring_color = is_active ? QColor(255, 0, 0, 255) : QColor(0, 0, 0, 50); + const QColor inner_color = QColor(255, 255, 255, is_active ? 255 : 85); + const QColor text_color = QColor(0, 0, 0, is_active ? 255 : 85); + + const int x = rc.center().x(); + const int y = rc.center().y(); + const int r = rc.width() / 2.0f; + + drawCircle(p, x, y, r, ring_color); + drawCircle(p, x, y, int(r * 0.8f), inner_color); + + p.setFont(InterFont(89, QFont::Bold)); + drawCenteredText(p, x, y, speed_limit, text_color); + p.setFont(InterFont(subtext_size, QFont::Bold)); + drawCenteredText(p, x, y + 55, sub_text, text_color); + + if (is_map_sourced) { + p.setPen(Qt::NoPen); + p.setOpacity(is_active ? 1.0 : 0.3); + p.drawPixmap(x - subsign_img_size / 2, y - 55 - subsign_img_size / 2, map_img); + p.setOpacity(1.0); + } +} + +void AnnotatedCameraWidgetSP::drawTrunSpeedSign(QPainter &p, QRect rc, const QString &turn_speed, const QString &sub_text, + int curv_sign, bool is_active) { + const QColor border_color = is_active ? QColor(255, 0, 0, 255) : QColor(0, 0, 0, 50); + const QColor inner_color = QColor(255, 255, 255, is_active ? 255 : 85); + const QColor text_color = QColor(0, 0, 0, is_active ? 255 : 85); + + const int x = rc.center().x(); + const int y = 184 * 2 + UI_BORDER_SIZE + 202; + const int width = 184; + + const float stroke_w = 15.0; + const float cS = stroke_w / 2.0 + 4.5; // half width of the stroke on the corners of the triangle + const float R = width / 2.0 - stroke_w / 2.0; + const float A = 0.73205; + const float h2 = 2.0 * R / (1.0 + A); + const float h1 = A * h2; + const float L = 4.0 * R / sqrt(3.0); + + // Draw the internal triangle, compensate for stroke width. Needed to improve rendering when in inactive + // state due to stroke transparency being different from inner transparency. + QPainterPath path; + path.moveTo(x, y - R + cS); + path.lineTo(x - L / 2.0 + cS, y + h1 + h2 - R - stroke_w / 2.0); + path.lineTo(x + L / 2.0 - cS, y + h1 + h2 - R - stroke_w / 2.0); + path.lineTo(x, y - R + cS); + p.setPen(Qt::NoPen); + p.setBrush(inner_color); + p.drawPath(path); + + // Draw the stroke + QPainterPath stroke_path; + stroke_path.moveTo(x, y - R); + stroke_path.lineTo(x - L / 2.0, y + h1 + h2 - R); + stroke_path.lineTo(x + L / 2.0, y + h1 + h2 - R); + stroke_path.lineTo(x, y - R); + p.setPen(QPen(border_color, stroke_w, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + p.setBrush(Qt::NoBrush); + p.drawPath(stroke_path); + + // Draw the turn sign + if (curv_sign != 0) { + p.setPen(Qt::NoPen); + p.setOpacity(is_active ? 1.0 : 0.3); + p.drawPixmap(int(x - (subsign_img_size / 2)), int(y - R + stroke_w + 30), curv_sign > 0 ? left_img : right_img); + p.setOpacity(1.0); + } + + // Draw the texts. + p.setFont(InterFont(67, QFont::Bold)); + drawCenteredText(p, x, y + 25, turn_speed, text_color); + p.setFont(InterFont(22, QFont::Bold)); + drawCenteredText(p, x, y + 65, sub_text, text_color); +} + +// ############################## DEV UI START ############################## +void AnnotatedCameraWidgetSP::drawCenteredLeftText(QPainter &p, int x, int y, const QString &text1, QColor color1, const QString &text2, const QString &text3, QColor color2) { + QFontMetrics fm(p.font()); + QRect init_rect = fm.boundingRect(text1 + " "); + QRect real_rect = fm.boundingRect(init_rect, 0, text1 + " "); + real_rect.moveCenter({x, y}); + + QRect init_rect3 = fm.boundingRect(text3); + QRect real_rect3 = fm.boundingRect(init_rect3, 0, text3); + real_rect3.moveTop(real_rect.top()); + real_rect3.moveLeft(real_rect.right() + 135); + + QRect init_rect2 = fm.boundingRect(text2); + QRect real_rect2 = fm.boundingRect(init_rect2, 0, text2); + real_rect2.moveTop(real_rect.top()); + real_rect2.moveRight(real_rect.right() + 125); + + p.setPen(color1); + p.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, text1); + + p.setPen(color2); + p.drawText(real_rect2, Qt::AlignRight | Qt::AlignVCenter, text2); + p.drawText(real_rect3, Qt::AlignLeft | Qt::AlignVCenter, text3); +} + +int AnnotatedCameraWidgetSP::drawDevUiRight(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) { + p.setFont(InterFont(30 * 2, QFont::Bold)); + drawColoredText(p, x + 92, y + 80, value, color); + + p.setFont(InterFont(28, QFont::Bold)); + drawText(p, x + 92, y + 80 + 42, label, 255); + + if (units.length() > 0) { + p.save(); + p.translate(x + 54 + 30 - 3 + 92 + 30, y + 37 + 25); + p.rotate(-90); + drawText(p, 0, 0, units, 255); + p.restore(); + } + + return 110; +} + +void AnnotatedCameraWidgetSP::drawRightDevUi(QPainter &p, int x, int y) { + int rh = 5; + int ry = y; + + UiElement dRelElement = DeveloperUi::getDRel(lead_status, lead_d_rel); + rh += drawDevUiRight(p, x, ry, dRelElement.value, dRelElement.label, dRelElement.units, dRelElement.color); + ry = y + rh; + + UiElement vRelElement = DeveloperUi::getVRel(lead_status, lead_v_rel, is_metric, speedUnit); + rh += drawDevUiRight(p, x, ry, vRelElement.value, vRelElement.label, vRelElement.units, vRelElement.color); + ry = y + rh; + + UiElement steeringAngleDegElement = DeveloperUi::getSteeringAngleDeg(angleSteers, madsEnabled, latActive); + rh += drawDevUiRight(p, x, ry, steeringAngleDegElement.value, steeringAngleDegElement.label, steeringAngleDegElement.units, steeringAngleDegElement.color); + ry = y + rh; + + if (lateralState == "torque") { + UiElement actualLateralAccelElement = DeveloperUi::getActualLateralAccel(curvature, vEgo, roll, madsEnabled, latActive); + rh += drawDevUiRight(p, x, ry, actualLateralAccelElement.value, actualLateralAccelElement.label, actualLateralAccelElement.units, actualLateralAccelElement.color); + } else { + UiElement steeringAngleDesiredDegElement = DeveloperUi::getSteeringAngleDesiredDeg(madsEnabled, latActive, steerAngleDesired, angleSteers); + rh += drawDevUiRight(p, x, ry, steeringAngleDesiredDegElement.value, steeringAngleDesiredDegElement.label, steeringAngleDesiredDegElement.units, steeringAngleDesiredDegElement.color); + } + ry = y + rh; + + UiElement memoryUsagePercentElement = DeveloperUi::getMemoryUsagePercent(memoryUsagePercent); + rh += drawDevUiRight(p, x, ry, memoryUsagePercentElement.value, memoryUsagePercentElement.label, memoryUsagePercentElement.units, memoryUsagePercentElement.color); + ry = y + rh; + + rh += 25; + p.setBrush(QColor(0, 0, 0, 0)); + QRect ldu(x, y, 184, rh); +} + +int AnnotatedCameraWidgetSP::drawNewDevUi(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) { + p.setFont(InterFont(38, QFont::Bold)); + drawCenteredLeftText(p, x, y, label, whiteColor(), value, units, color); + + return 430; +} + +void AnnotatedCameraWidgetSP::drawNewDevUi2(QPainter &p, int x, int y) { + int rw = 90; + + UiElement aEgoElement = DeveloperUi::getAEgo(aEgo); + rw += drawNewDevUi(p, rw, y, aEgoElement.value, aEgoElement.label, aEgoElement.units, aEgoElement.color); + + UiElement vEgoLeadElement = DeveloperUi::getVEgoLead(lead_status, lead_v_rel, vEgo, is_metric, speedUnit); + rw += drawNewDevUi(p, rw, y, vEgoLeadElement.value, vEgoLeadElement.label, vEgoLeadElement.units, vEgoLeadElement.color); + + if (torquedUseParams) { + UiElement frictionCoefficientFilteredElement = DeveloperUi::getFrictionCoefficientFiltered(frictionCoefficientFiltered, liveValid); + rw += drawNewDevUi(p, rw, y, frictionCoefficientFilteredElement.value, frictionCoefficientFilteredElement.label, frictionCoefficientFilteredElement.units, frictionCoefficientFilteredElement.color); + + UiElement latAccelFactorFilteredElement = DeveloperUi::getLatAccelFactorFiltered(latAccelFactorFiltered, liveValid); + rw += drawNewDevUi(p, rw, y, latAccelFactorFilteredElement.value, latAccelFactorFilteredElement.label, latAccelFactorFilteredElement.units, latAccelFactorFilteredElement.color); + } else { + UiElement steeringTorqueEpsElement = DeveloperUi::getSteeringTorqueEps(steeringTorqueEps); + rw += drawNewDevUi(p, rw, y, steeringTorqueEpsElement.value, steeringTorqueEpsElement.label, steeringTorqueEpsElement.units, steeringTorqueEpsElement.color); + + UiElement bearingDegElement = DeveloperUi::getBearingDeg(bearingAccuracyDeg, bearingDeg); + rw += drawNewDevUi(p, rw, y, bearingDegElement.value, bearingDegElement.label, bearingDegElement.units, bearingDegElement.color); + } + + UiElement altitudeElement = DeveloperUi::getAltitude(gpsAccuracy, altitude); + rw += drawNewDevUi(p, rw, y, altitudeElement.value, altitudeElement.label, altitudeElement.units, altitudeElement.color); +} + +// ############################## DEV UI END ############################## + +void AnnotatedCameraWidgetSP::drawE2eStatus(QPainter &p, int x, int y, int w, int h, int e2e_long_status) { + QColor status_color; + QRect e2eStatusIcon(x, y, w, h); + p.setPen(Qt::NoPen); + p.setBrush(QBrush(blackColor(70))); + p.drawEllipse(e2eStatusIcon); + e2eStatusIcon -= QMargins(25, 25, 25, 25); + p.setPen(Qt::NoPen); + if (e2e_long_status == 2) { + status_color = QColor::fromRgbF(0.0, 1.0, 0.0, 0.9); + } else if (e2e_long_status == 1) { + status_color = QColor::fromRgbF(1.0, 0.0, 0.0, 0.9); + } + p.setBrush(QBrush(status_color)); + p.drawEllipse(e2eStatusIcon); +} + +void AnnotatedCameraWidgetSP::drawLeftTurnSignal(QPainter &painter, int x, int y, int circle_size, int state) { + painter.setRenderHint(QPainter::Antialiasing, true); + + QColor circle_color, circle_color_0, circle_color_1; + QColor arrow_color, arrow_color_0, arrow_color_1; + if ((left_blindspot || lane_change_edge_block) && !(left_blinker && right_blinker)) { + circle_color_0 = QColor(164, 0, 1); + circle_color_1 = QColor(204, 0, 1); + arrow_color_0 = QColor(72, 1, 1); + arrow_color_1 = QColor(255, 255, 255); + } else { + circle_color_0 = QColor(22, 156, 69); + circle_color_1 = QColor(30, 215, 96); + arrow_color_0 = QColor(9, 56, 27); + arrow_color_1 = QColor(255, 255, 255); + } + + if (state == 1) { + circle_color = circle_color_1; + arrow_color = arrow_color_1; + } else if (state == 0) { + circle_color = circle_color_0; + arrow_color = arrow_color_0; + } + + // Draw the circle + int circleX = x; + int circleY = y; + painter.setPen(Qt::NoPen); + painter.setBrush(circle_color); + painter.drawEllipse(circleX, circleY, circle_size, circle_size); + + // Draw the arrow + int arrowSize = 50; + int arrowX = circleX + (circle_size - arrowSize) / 4; + int arrowY = circleY + (circle_size - arrowSize) / 2; + painter.setPen(Qt::NoPen); + painter.setBrush(arrow_color); + + // Draw the arrow shape + QPolygon arrowPolygon; + arrowPolygon << QPoint(arrowX + 10, arrowY + arrowSize / 2) + << QPoint(arrowX + arrowSize - 3, arrowY) + << QPoint(arrowX + arrowSize, arrowY) + << QPoint(arrowX + arrowSize, arrowY + arrowSize) + << QPoint(arrowX + arrowSize - 3, arrowY + arrowSize) + << QPoint(arrowX + 10, arrowY + arrowSize / 2); + painter.drawPolygon(arrowPolygon); + + // Draw the tail rectangle + int tailWidth = arrowSize / 2.25; + int tailHeight = arrowSize / 2; + QRect tailRect(arrowX + arrowSize - 3, arrowY + arrowSize / 4, tailWidth, tailHeight); + painter.fillRect(tailRect, arrow_color); +} + +void AnnotatedCameraWidgetSP::drawRightTurnSignal(QPainter &painter, int x, int y, int circle_size, int state) { + painter.setRenderHint(QPainter::Antialiasing, true); + + QColor circle_color, circle_color_0, circle_color_1; + QColor arrow_color, arrow_color_0, arrow_color_1; + if ((right_blindspot || lane_change_edge_block) && !(left_blinker && right_blinker)) { + circle_color_0 = QColor(164, 0, 1); + circle_color_1 = QColor(204, 0, 1); + arrow_color_0 = QColor(72, 1, 1); + arrow_color_1 = QColor(255, 255, 255); + } else { + circle_color_0 = QColor(22, 156, 69); + circle_color_1 = QColor(30, 215, 96); + arrow_color_0 = QColor(9, 56, 27); + arrow_color_1 = QColor(255, 255, 255); + } + + if (state == 1) { + circle_color = circle_color_1; + arrow_color = arrow_color_1; + } else if (state == 0) { + circle_color = circle_color_0; + arrow_color = arrow_color_0; + } + + + // Draw the circle + int circleX = x; + int circleY = y; + painter.setPen(Qt::NoPen); + painter.setBrush(circle_color); + painter.drawEllipse(circleX, circleY, circle_size, circle_size); + + // Draw the arrow + int arrowSize = 50; + int arrowX = circleX + (circle_size - arrowSize) / 2 + (arrowSize / 2.5) - 3; + int arrowY = circleY + (circle_size - arrowSize) / 2; + painter.setPen(Qt::NoPen); + painter.setBrush(arrow_color); + + // Draw the arrow shape + QPolygon arrowPolygon; + arrowPolygon << QPoint(arrowX + arrowSize - 10, arrowY + arrowSize / 2) + << QPoint(arrowX + 3, arrowY) + << QPoint(arrowX, arrowY) + << QPoint(arrowX, arrowY + arrowSize) + << QPoint(arrowX + 3, arrowY + arrowSize) + << QPoint(arrowX + arrowSize - 10, arrowY + arrowSize / 2); + painter.drawPolygon(arrowPolygon); + + // Draw the tail rectangle + int tailWidth = arrowSize / 2.25; + int tailHeight = arrowSize / 2; + QRect tailRect(arrowX - tailWidth + 3, arrowY + arrowSize / 4, tailWidth, tailHeight); + painter.fillRect(tailRect, arrow_color); +} + +int AnnotatedCameraWidgetSP::blinkerPulse(int frame) { + if (frame % UI_FREQ < (UI_FREQ / 2)) { + blinker_state = 1; + } else { + blinker_state = 0; + } + + return blinker_state; +} + +void AnnotatedCameraWidgetSP::speedLimitSignPulse(int frame) { + if (frame % UI_FREQ < (UI_FREQ / 2.5)) { + slcShowSign = false; + } else { + slcShowSign = true; + } +} + +void AnnotatedCameraWidgetSP::drawFeatureStatusText(QPainter &p, int x, int y) { + const FeatureStatusText feature_text; + const FeatureStatusColor feature_color; + const QColor text_color = whiteColor(); + const QColor shadow_color = blackColor(38); + const int text_height = 34; + const int drop_shadow_size = 2; + const int eclipse_x_offset = 25; + const int eclipse_y_offset = 20; + const int w = 16; + const int h = 16; + + const bool longitudinal = hasLongitudinalControl(car_params); + + p.setFont(InterFont(32, QFont::Bold)); + + // Define a function to draw a feature status button + auto drawFeatureStatusElement = [&](int value, const QStringList& text_list, const QStringList& color_list, bool condition, const QString& off_text, const QString& label) { + std::pair feature_status = getFeatureStatus(value, text_list, color_list, condition, off_text); + QRect btn(x - eclipse_x_offset, y - eclipse_y_offset, w, h); + QRect btn_shadow(x - eclipse_x_offset + drop_shadow_size, y - eclipse_y_offset + drop_shadow_size, w, h); + p.setPen(Qt::NoPen); + p.setBrush(shadow_color); + p.drawEllipse(btn_shadow); + p.setBrush(feature_status.second); + p.drawEllipse(btn); + QString status_text; + status_text.sprintf("%s: %s", label.toStdString().c_str(), (feature_status.first).toStdString().c_str()); + p.setPen(QPen(shadow_color, 2)); + p.drawText(x + drop_shadow_size, y + drop_shadow_size, status_text); + p.setPen(QPen(text_color, 2)); + p.drawText(x, y, status_text); + y += text_height; + }; + + // Driving Personality / Gap Adjust Cruise + if (longitudinal) { + drawFeatureStatusElement(longitudinalPersonality, feature_text.gac_list_text, feature_color.gac_list_color, longitudinal, "N/A", "GAP"); + } + + // Dynamic Lane Profile + if (drivingModelGen == cereal::ModelGeneration::ONE) { + drawFeatureStatusElement(dynamicLaneProfile, feature_text.dlp_list_text, feature_color.dlp_list_color, true, "OFF", "DLP"); + } + + // TODO: Add toggle variables to cereal, and parse from cereal + if (longitudinal) { + QColor dec_color((cruiseStateEnabled && dynamicExperimentalControlToggle) ? "#4bff66" : "#ffffff"); + QRect dec_btn(x - eclipse_x_offset, y - eclipse_y_offset, w, h); + QRect dec_btn_shadow(x - eclipse_x_offset + drop_shadow_size, y - eclipse_y_offset + drop_shadow_size, w, h); + p.setPen(Qt::NoPen); + p.setBrush(shadow_color); + p.drawEllipse(dec_btn_shadow); + p.setBrush(dec_color); + p.drawEllipse(dec_btn); + QString dec_status_text; + dec_status_text.sprintf("DEC: %s\n", dynamicExperimentalControlToggle ? (experimentalMode ? QString(mpcMode).toStdString().c_str() : QString("Inactive").toStdString().c_str()) : "OFF"); + p.setPen(QPen(shadow_color, 2)); + p.drawText(x + drop_shadow_size, y + drop_shadow_size, dec_status_text); + p.setPen(QPen(text_color, 2)); + p.drawText(x, y, dec_status_text); + y += text_height; + } + + // TODO: Add toggle variables to cereal, and parse from cereal + // Speed Limit Control + if (longitudinal || !car_params.getPcmCruiseSpeed()) { + drawFeatureStatusElement(int(slcState), feature_text.slc_list_text, feature_color.slc_list_color, speedLimitControlToggle, "OFF", "SLC"); + } +} + +void AnnotatedCameraWidgetSP::speedLimitWarning(QPainter &p, QRect sign_rect, const int sign_margin) { + // PRE ACTIVE + if (slcState == cereal::LongitudinalPlanSP::SpeedLimitControlState::PRE_ACTIVE) { + int set_speed = std::nearbyint(setSpeed); + int speed_limit_offsetted = std::nearbyint(speedLimitSLC + speedLimitSLCOffset); + + // Calculate the vertical offset using a sinusoidal function for smooth bouncing + double bounce_frequency = 2.0 * M_PI / 20.0; // 20 frames for one full oscillation + int bounce_offset = 20 * sin(speed_limit_frame * bounce_frequency); // Adjust the amplitude (20 pixels) as needed + + if (set_speed < speed_limit_offsetted) { + QPoint iconPosition(sign_rect.right() + sign_margin * 3, sign_rect.center().y() - plus_arrow_up_img.height() / 2 + bounce_offset); + p.drawPixmap(iconPosition, plus_arrow_up_img); + } else if (set_speed > speed_limit_offsetted) { + QPoint iconPosition(sign_rect.right() + sign_margin * 3, sign_rect.center().y() - minus_arrow_down_img.height() / 2 - bounce_offset); + p.drawPixmap(iconPosition, minus_arrow_down_img); + } + + speed_limit_frame++; + speedLimitSignPulse(speed_limit_frame); + } + + // current speed over speed limit + else if (overSpeedLimit && speedLimitWarningFlash) { + speed_limit_frame++; + speedLimitSignPulse(speed_limit_frame); + } + + else { + speed_limit_frame = 0; + slcShowSign = true; + } +} + + +void AnnotatedCameraWidgetSP::initializeGL() { + CameraWidget::initializeGL(); + qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION)); + qInfo() << "OpenGL vendor:" << QString((const char*)glGetString(GL_VENDOR)); + qInfo() << "OpenGL renderer:" << QString((const char*)glGetString(GL_RENDERER)); + qInfo() << "OpenGL language version:" << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); + + prev_draw_t = millis_since_boot(); + setBackgroundColor(bg_colors[STATUS_DISENGAGED]); +} + +void AnnotatedCameraWidgetSP::updateFrameMat() { + CameraWidget::updateFrameMat(); + UIStateSP *s = uiStateSP(); + int w = width(), h = height(); + + s->fb_w = w; + s->fb_h = h; + + // Apply transformation such that video pixel coordinates match video + // 1) Put (0, 0) in the middle of the video + // 2) Apply same scaling as video + // 3) Put (0, 0) in top left corner of video + s->car_space_transform.reset(); + s->car_space_transform.translate(w / 2 - x_offset, h / 2 - y_offset) + .scale(zoom, zoom) + .translate(-intrinsic_matrix.v[2], -intrinsic_matrix.v[5]); +} + +void AnnotatedCameraWidgetSP::drawLaneLines(QPainter &painter, const UIStateSP *s) { + painter.save(); + + const UISceneSP &scene = s->scene; + SubMaster &sm = *(s->sm); + + const auto car_state = sm["carState"].getCarState(); + + // Shane's colored lanelines + for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) { + if (i == 1 || i == 2) { + // TODO: can we just use the projected vertices somehow? + const cereal::XYZTData::Reader &line = (*s->sm)["modelV2"].getModelV2().getLaneLines()[i]; + const float default_pos = 1.4; // when lane poly isn't available + const float lane_pos = line.getY().size() > 0 ? std::abs(line.getY()[5]) : default_pos; // get redder when line is closer to car + float hue = 332.5 * lane_pos - 332.5; // equivalent to {1.4, 1.0}: {133, 0} (green to red) + hue = std::fmin(133, fmax(0, hue)) / 360.; // clip and normalize + painter.setBrush(QColor::fromHslF(hue, 1.0, 0.50, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); + } else { + painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); + } + painter.drawPolygon(scene.lane_line_vertices[i]); + } + + // TODO: Fix empty spaces when curiving back on itself + painter.setBrush(QColor::fromRgbF(1.0, 0.0, 0.0, 0.2)); + if (left_blindspot) painter.drawPolygon(scene.lane_barrier_vertices[0]); + if (right_blindspot) painter.drawPolygon(scene.lane_barrier_vertices[1]); + + // road edges + for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) { + painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); + painter.drawPolygon(scene.road_edge_vertices[i]); + } + + // paint path + QLinearGradient bg(0, height(), 0, 0); + if (madsEnabled || car_state.getCruiseState().getEnabled()) { + if (steerOverride && latActive) { + bg.setColorAt(0.0, QColor::fromHslF(20 / 360., 0.94, 0.51, 0.17)); + bg.setColorAt(0.5, QColor::fromHslF(20 / 360., 1.0, 0.68, 0.17)); + bg.setColorAt(1.0, QColor::fromHslF(20 / 360., 1.0, 0.68, 0.0)); + } else if (!(latActive || car_state.getCruiseState().getEnabled())) { + bg.setColorAt(0, whiteColor()); + bg.setColorAt(1, whiteColor(0)); + } else if (sm["controlsState"].getControlsState().getExperimentalMode()) { + // The first half of track_vertices are the points for the right side of the path + const auto &acceleration = sm["modelV2"].getModelV2().getAcceleration().getX(); + const int max_len = std::min(scene.track_vertices.length() / 2, acceleration.size()); + + for (int i = 0; i < max_len; ++i) { + // Some points are out of frame + if (scene.track_vertices[i].y() < 0 || scene.track_vertices[i].y() > height()) continue; + + // Flip so 0 is bottom of frame + float lin_grad_point = (height() - scene.track_vertices[i].y()) / height(); + + // speed up: 120, slow down: 0 + float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0); + // FIXME: painter.drawPolygon can be slow if hue is not rounded + path_hue = int(path_hue * 100 + 0.5) / 100; + + float saturation = fmin(fabs(acceleration[i] * 1.5), 1); + float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey + float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade + bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha)); + + // Skip a point, unless next is last + i += (i + 2) < max_len ? 1 : 0; + } + + } else { + bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4)); + bg.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.35)); + bg.setColorAt(1.0, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.0)); + } + } else { + bg.setColorAt(0.0, whiteColor(102)); + bg.setColorAt(0.5, whiteColor(89)); + bg.setColorAt(1.0, whiteColor(0)); + } + + painter.setBrush(bg); + painter.drawPolygon(scene.track_vertices); + + // create path combining track vertices and track edge vertices + QPainterPath path; + path.addPolygon(scene.track_vertices); + path.addPolygon(scene.track_edge_vertices); + + // paint path edges + QLinearGradient pe(0, height(), 0, height() / 4); + if (!scene.dynamic_lane_profile_status) { + pe.setColorAt(0.0, QColor::fromHslF(240 / 360., 0.94, 0.51, 1.0)); + pe.setColorAt(0.5, QColor::fromHslF(204 / 360., 1.0, 0.68, 0.5)); + pe.setColorAt(1.0, QColor::fromHslF(204 / 360., 1.0, 0.68, 0.0)); + + painter.setBrush(pe); + painter.drawPath(path); + } + + painter.restore(); +} + +void AnnotatedCameraWidgetSP::drawDriverState(QPainter &painter, const UIStateSP *s) { + const UIScene &scene = s->scene; + + painter.save(); + + // base icon + int offset = UI_BORDER_SIZE + btn_size / 2; + int x = rightHandDM ? width() - offset : offset; + int y = height() - offset - rn_offset; + float opacity = dmActive ? 0.65 : 0.2; + drawIcon(painter, QPoint(x, y), dm_img, blackColor(70), opacity); + + // face + QPointF face_kpts_draw[std::size(default_face_kpts_3d)]; + float kp; + for (int i = 0; i < std::size(default_face_kpts_3d); ++i) { + kp = (scene.face_kpts_draw[i].v[2] - 8) / 120 + 1.0; + face_kpts_draw[i] = QPointF(scene.face_kpts_draw[i].v[0] * kp + x, scene.face_kpts_draw[i].v[1] * kp + y); + } + + painter.setPen(QPen(QColor::fromRgbF(1.0, 1.0, 1.0, opacity), 5.2, Qt::SolidLine, Qt::RoundCap)); + painter.drawPolyline(face_kpts_draw, std::size(default_face_kpts_3d)); + + // tracking arcs + const int arc_l = 133; + const float arc_t_default = 6.7; + const float arc_t_extend = 12.0; + QColor arc_color = QColor::fromRgbF(0.545 - 0.445 * s->engaged(), + 0.545 + 0.4 * s->engaged(), + 0.545 - 0.285 * s->engaged(), + 0.4 * (1.0 - dm_fade_state)); + float delta_x = -scene.driver_pose_sins[1] * arc_l / 2; + float delta_y = -scene.driver_pose_sins[0] * arc_l / 2; + painter.setPen(QPen(arc_color, arc_t_default+arc_t_extend*fmin(1.0, scene.driver_pose_diff[1] * 5.0), Qt::SolidLine, Qt::RoundCap)); + painter.drawArc(QRectF(std::fmin(x + delta_x, x), y - arc_l / 2, fabs(delta_x), arc_l), (scene.driver_pose_sins[1]>0 ? 90 : -90) * 16, 180 * 16); + painter.setPen(QPen(arc_color, arc_t_default+arc_t_extend*fmin(1.0, scene.driver_pose_diff[0] * 5.0), Qt::SolidLine, Qt::RoundCap)); + painter.drawArc(QRectF(x - arc_l / 2, std::fmin(y + delta_y, y), arc_l, fabs(delta_y)), (scene.driver_pose_sins[0]>0 ? 0 : 180) * 16, 180 * 16); + + painter.restore(); +} + +void AnnotatedCameraWidgetSP::rocketFuel(QPainter &p) { + + static const int ct_n = 1; + static float ct; + + int rect_w = rect().width(); + int rect_h = rect().height(); + + const int n = 15 + 1; //Add one off screen due to timing issues + static float t[n]; + int dim_n = (sin(ct / 5) + 1) * (n - 0.01); + t[dim_n] = 1.0; + t[(int)(ct/ct_n)] = 1.0; + + UIStateSP *s = uiStateSP(); + float vc_accel0 = (*s->sm)["carState"].getCarState().getAEgo(); + static float vc_accel; + vc_accel = vc_accel + (vc_accel0 - vc_accel) / 5; + float hha = 0; + if (vc_accel > 0) { + hha = 0.85 - 0.1 / vc_accel; // only extend up to 85% + p.setBrush(QColor(0, 245, 0, 200)); + } + if (vc_accel < 0) { + hha = 0.85 + 0.1 / vc_accel; // only extend up to 85% + p.setBrush(QColor(245, 0, 0, 200)); + } + if (hha < 0) { + hha = 0; + } + hha = hha * rect_h; + float wp = 28; + if (vc_accel > 0) { + QRect ra = QRect(rect_w - wp, rect_h / 2 - hha / 2, wp, hha / 2); + p.drawRect(ra); + } else { + QRect ra = QRect(rect_w - wp, rect_h / 2, wp, hha / 2); + p.drawRect(ra); + } +} + +void AnnotatedCameraWidgetSP::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, + int num, const cereal::CarState::Reader &car_data, int chevron_data) { + painter.save(); + + const float speedBuff = 10.; + const float leadBuff = 40.; + const float d_rel = lead_data.getDRel(); + const float v_rel = lead_data.getVRel(); + const float v_ego = car_data.getVEgo(); + + float fillAlpha = 0; + if (d_rel < leadBuff) { + fillAlpha = 255 * (1.0 - (d_rel / leadBuff)); + if (v_rel < 0) { + fillAlpha += 255 * (-1 * (v_rel / speedBuff)); + } + fillAlpha = (int)(fmin(fillAlpha, 255)); + } + + float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * 2.35; + float x = std::clamp((float)vd.x(), 0.f, width() - sz / 2); + float y = std::fmin(height() - sz * .6, (float)vd.y()); + + float g_xo = sz / 5; + float g_yo = sz / 10; + + QPointF glow[] = {{x + (sz * 1.35) + g_xo, y + sz + g_yo}, {x, y - g_yo}, {x - (sz * 1.35) - g_xo, y + sz + g_yo}}; + painter.setBrush(QColor(218, 202, 37, 255)); + painter.drawPolygon(glow, std::size(glow)); + + // chevron + QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}}; + painter.setBrush(redColor(fillAlpha)); + painter.drawPolygon(chevron, std::size(chevron)); + + if (num == 0) { // Display metrics to the 0th lead car + const int chevron_types = 3; + const int chevron_all = chevron_types + 1; // All metrics + QStringList chevron_text[chevron_types]; + int position; + float val; + if (chevron_data == 1 || chevron_data == chevron_all) { + position = 0; + val = std::max(0.0f, d_rel); + chevron_text[position].append(QString::number(val,'f', 0) + " " + "m"); + } + if (chevron_data == 2 || chevron_data == chevron_all) { + position = (chevron_data == 2) ? 0 : 1; + val = std::max(0.0f, (v_rel + v_ego) * (is_metric ? static_cast(MS_TO_KPH) : static_cast(MS_TO_MPH))); + chevron_text[position].append(QString::number(val,'f', 0) + " " + (is_metric ? "km/h" : "mph")); + } + if (chevron_data == 3 || chevron_data == chevron_all) { + position = (chevron_data == 3) ? 0 : 2; + val = (d_rel > 0 && v_ego > 0) ? std::max(0.0f, d_rel / v_ego) : 0.0f; + + QString ttc_str = (val > 0 && val < 200) ? QString::number(val, 'f', 1) + "s" : "---"; + chevron_text[position].append(ttc_str); + } + + float str_w = 200; // Width of the text box, might need adjustment + float str_h = 50; // Height of the text box, adjust as necessary + painter.setFont(InterFont(45, QFont::Bold)); + // Calculate the center of the chevron and adjust the text box position + float text_y = y + sz + 12; // Position the text at the bottom of the chevron + QRect textRect(x - str_w / 2, text_y, str_w, str_h); // Adjust the rectangle to center the text horizontally at the chevron's bottom + QPoint shadow_offset(2, 2); + for (int i = 0; i < chevron_types; ++i) { + if (!chevron_text[i].isEmpty()) { + painter.setPen(QColor(0x0, 0x0, 0x0, 200)); // Draw shadow + painter.drawText(textRect.translated(shadow_offset.x(), shadow_offset.y() + i * str_h), Qt::AlignBottom | Qt::AlignHCenter, chevron_text[i].at(0)); + painter.setPen(QColor(0xff, 0xff, 0xff)); // Draw text + painter.drawText(textRect.translated(0, i * str_h), Qt::AlignBottom | Qt::AlignHCenter, chevron_text[i].at(0)); + painter.setPen(Qt::NoPen); // Reset pen to default + } + } + } + + painter.restore(); +} + +void AnnotatedCameraWidgetSP::paintGL() { + UIStateSP *s = uiStateSP(); + SubMaster &sm = *(s->sm); + const double start_draw_t = millis_since_boot(); + const cereal::ModelDataV2::Reader &model = sm["modelV2"].getModelV2(); + + // draw camera frame + { + std::lock_guard lk(frame_lock); + + if (frames.empty()) { + if (skip_frame_count > 0) { + skip_frame_count--; + qDebug() << "skipping frame, not ready"; + return; + } + } else { + // skip drawing up to this many frames if we're + // missing camera frames. this smooths out the + // transitions from the narrow and wide cameras + skip_frame_count = 5; + } + + // Wide or narrow cam dependent on speed + bool has_wide_cam = available_streams.count(VISION_STREAM_WIDE_ROAD); + if (has_wide_cam) { + float v_ego = sm["carState"].getCarState().getVEgo(); + float steer_angle = sm["carState"].getCarState().getSteeringAngleDeg(); + if ((v_ego < 10) || available_streams.size() == 1 || (std::fabs(steer_angle) > 45)) { + wide_cam_requested = true; + } else if ((v_ego > 15) && (std::fabs(steer_angle) < 30)) { + wide_cam_requested = false; + } + wide_cam_requested = wide_cam_requested && sm["controlsState"].getControlsState().getExperimentalMode(); + // for replay of old routes, never go to widecam + wide_cam_requested = wide_cam_requested && s->scene.calibration_wide_valid; + } + CameraWidget::setStreamType(wide_cam_requested ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD); + + if (reversing && s->scene.reverse_dm_cam) { + CameraWidget::setStreamType(VISION_STREAM_DRIVER, s->scene.reverse_dm_cam); + } + + s->scene.wide_cam = CameraWidget::getStreamType() == VISION_STREAM_WIDE_ROAD; + if (s->scene.calibration_valid) { + auto calib = s->scene.wide_cam ? s->scene.view_from_wide_calib : s->scene.view_from_calib; + CameraWidget::updateCalibration(calib); + } else { + CameraWidget::updateCalibration(DEFAULT_CALIBRATION); + } + CameraWidget::setFrameId(model.getFrameId()); + CameraWidget::paintGL(); + } + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + + if (s->scene.world_objects_visible) { + sp_update_model(s, model); + drawLaneLines(painter, s); + + if (s->scene.longitudinal_control && sm.rcv_frame("radarState") > s->scene.started_frame) { + auto radar_state = sm["radarState"].getRadarState(); + auto car_state = sm["carState"].getCarState(); + update_leads(s, radar_state, model.getPosition()); + auto lead_one = radar_state.getLeadOne(); + auto lead_two = radar_state.getLeadTwo(); + if (lead_one.getStatus()) { + drawLead(painter, lead_one, s->scene.lead_vertices[0], 0, car_state, s->scene.chevron_data); + } + if (lead_two.getStatus() && (std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0)) { + drawLead(painter, lead_two, s->scene.lead_vertices[1], 1, car_state, s->scene.chevron_data); + } + + rocketFuel(painter); + } + } + + // DMoji + if (!hideBottomIcons && (sm.rcv_frame("driverStateV2") > s->scene.started_frame)) { + update_dmonitoring(s, sm["driverStateV2"].getDriverStateV2(), dm_fade_state, rightHandDM); + drawDriverState(painter, s); + } + + drawHud(painter); + + if (left_blinker || right_blinker) { + blinker_frame++; + int state = blinkerPulse(blinker_frame); + int blinker_x = splitPanelVisible ? 150 : 180; + int blinker_y = splitPanelVisible ? 220 : 90; + if (left_blinker) { + drawLeftTurnSignal(painter, rect().center().x() - (blinker_x + blinker_size), blinker_y, blinker_size, state); + } + if (right_blinker) { + drawRightTurnSignal(painter, rect().center().x() + blinker_x, blinker_y, blinker_size, state); + } + } else { + blinker_frame = 0; + } + + double cur_draw_t = millis_since_boot(); + double dt = cur_draw_t - prev_draw_t; + double fps = fps_filter.update(1. / dt * 1000); + if (fps < 15) { + LOGW("slow frame rate: %.2f fps", fps); + } + prev_draw_t = cur_draw_t; + + // publish debug msg + MessageBuilder msg; + auto m = msg.initEvent().initUiDebug(); + m.setDrawTimeMillis(cur_draw_t - start_draw_t); + pm->send("uiDebug", msg); + + MessageBuilder e2e_long_msg; + auto e2eLongStatus = e2e_long_msg.initEvent().initE2eLongStateSP(); + e2eLongStatus.setStatus(e2eStatus); + e2e_state->send("e2eLongStateSP", e2e_long_msg); +} + +void AnnotatedCameraWidgetSP::showEvent(QShowEvent *event) { + CameraWidget::showEvent(event); + + sp_ui_update_params(uiStateSP()); + prev_draw_t = millis_since_boot(); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h new file mode 100644 index 0000000000..92bf5056b3 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h @@ -0,0 +1,230 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include +#include + +#include "selfdrive/ui/qt/onroad/buttons.h" +#include "selfdrive/ui/qt/widgets/cameraview.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/buttons.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h" + +const int subsign_img_size = 35; +const int blinker_size = 120; + +class AnnotatedCameraWidgetSP : public CameraWidget { + Q_OBJECT + +public: + explicit AnnotatedCameraWidgetSP(VisionStreamType type, QWidget* parent = 0); + void updateState(const UIStateSP &s); + + MapSettingsButton *map_settings_btn; + OnroadSettingsButton *onroad_settings_btn; + +private: + void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255); + void drawCenteredText(QPainter &p, int x, int y, const QString &text, QColor color); + void drawVisionTurnControllerUI(QPainter &p, int x, int y, int size, const QColor &color, const QString &speed, + int alpha); + void drawCircle(QPainter &p, int x, int y, int r, QBrush bg); + void drawSpeedSign(QPainter &p, QRect rc, const QString &speed, const QString &sub_text, int subtext_size, + bool is_map_sourced, bool is_active); + void drawTrunSpeedSign(QPainter &p, QRect rc, const QString &speed, const QString &sub_text, int curv_sign, + bool is_active); + + void drawColoredText(QPainter &p, int x, int y, const QString &text, QColor color); + void drawStandstillTimer(QPainter &p, int x, int y); + + // ############################## DEV UI START ############################## + void drawRightDevUi(QPainter &p, int x, int y); + int drawDevUiRight(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color); + int drawNewDevUi(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color); + void drawNewDevUi2(QPainter &p, int x, int y); + void drawCenteredLeftText(QPainter &p, int x, int y, const QString &text1, QColor color1, const QString &text2, const QString &text3, QColor color2); + + // ############################## DEV UI END ############################## + + void drawE2eStatus(QPainter &p, int x, int y, int w, int h, int e2e_long_status); + + void drawLeftTurnSignal(QPainter &painter, int x, int y, int circle_size, int state); + void drawRightTurnSignal(QPainter &painter, int x, int y, int circle_size, int state); + int blinkerPulse(int frame); + void updateButtonsLayout(bool is_rhd); + + void drawFeatureStatusText(QPainter &p, int x, int y); + void speedLimitSignPulse(int frame); + void speedLimitWarning(QPainter &p, QRect sign_rect, const int sign_margin); + void mousePressEvent(QMouseEvent* e) override; + void drawRoadNameText(QPainter &p, int x, int y, const QString &text, QColor color); + + QVBoxLayout *main_layout; + ExperimentalButtonSP *experimental_btn; + QPixmap dm_img; + float speed; + QString speedUnit; + float setSpeed; + float speedLimit; + bool is_cruise_set = false; + bool is_metric = false; + bool dmActive = false; + bool hideBottomIcons = false; + bool rightHandDM = false; + float dm_fade_state = 1.0; + bool has_us_speed_limit = false; + bool has_eu_speed_limit = false; + bool v_ego_cluster_seen = false; + int status = STATUS_DISENGAGED; + std::unique_ptr pm; + + int skip_frame_count = 0; + bool wide_cam_requested = false; + + Params params; + QHBoxLayout *buttons_layout; + QPixmap map_img; + QPixmap left_img; + QPixmap right_img; + bool left_blindspot = false; + bool right_blindspot = false; + std::unique_ptr e2e_state; + + bool steerOverride = false; + bool gasOverride = false; + bool latActive = false; + bool madsEnabled = false; + + bool brakeLights; + + bool standStillTimer; + bool standStill; + float standstillElapsedTime; + + bool showVTC = false; + QString vtcSpeed; + QColor vtcColor; + + bool showDebugUI = false; + + QString roadName = ""; + + bool showSpeedLimit = false; + float speedLimitSLC; + float speedLimitSLCOffset; + float speedLimitWarningOffset; + QString slcSubText; + float slcSubTextSize = 0.0; + bool overSpeedLimit; + bool mapSourcedSpeedLimit = false; + bool slcActive = false; + + bool showTurnSpeedLimit = false; + QString turnSpeedLimit; + QString tscSubText; + bool tscActive = false; + int curveSign = 0; + + bool hideVEgoUi; + + bool splitPanelVisible; + + // ############################## DEV UI START ############################## + bool lead_status; + float lead_d_rel = 0; + float lead_v_rel = 0; + QString lateralState; + float angleSteers = 0; + float steerAngleDesired = 0; + float curvature; + float roll; + int memoryUsagePercent; + int devUiInfo; + float gpsAccuracy; + float altitude; + float vEgo; + float aEgo; + float steeringTorqueEps; + float bearingAccuracyDeg; + float bearingDeg; + bool torquedUseParams; + float latAccelFactorFiltered; + float frictionCoefficientFiltered; + bool liveValid; + // ############################## DEV UI END ############################## + + float btnPerc; + + bool reversing; + + int e2eState; + int e2eStatus; + + bool left_blinker, right_blinker, lane_change_edge_block; + int blinker_frame; + int blinker_state = 0; + + cereal::LongitudinalPlanSP::SpeedLimitControlState slcState; + int longitudinalPersonality; + int dynamicLaneProfile; + QString mpcMode; + + int speed_limit_frame; + bool slcShowSign = true; + QPixmap plus_arrow_up_img; + QPixmap minus_arrow_down_img; + QRect sl_sign_rect; + int rn_offset = 0; + bool e2eLongAlertUi, dynamicExperimentalControlToggle, speedLimitControlToggle, speedLimitWarningFlash; + cereal::CarParams::Reader car_params; + bool cruiseStateEnabled; + bool experimentalMode; + + bool featureStatusToggle; + + cereal::ModelGeneration drivingModelGen; + +protected: + void paintGL() override; + void initializeGL() override; + void showEvent(QShowEvent *event) override; + void updateFrameMat() override; + void drawLaneLines(QPainter &painter, const UIStateSP *s); + void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, + int num, const cereal::CarState::Reader &car_data, int chevron_data); + void drawHud(QPainter &p); + void drawDriverState(QPainter &painter, const UIStateSP *s); + inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); } + inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); } + inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); } + + void rocketFuel(QPainter &p); + + double prev_draw_t = 0; + FirstOrderFilter fps_filter; +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/buttons.cc b/selfdrive/ui/sunnypilot/qt/onroad/buttons.cc new file mode 100644 index 0000000000..3a4fe68695 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/buttons.cc @@ -0,0 +1,96 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/onroad/buttons.h" + +#include "selfdrive/ui/qt/util.h" + +static void drawCustomButtonIcon(QPainter &p, const int btn_size_x, const int btn_size_y, const QPixmap &img, const QBrush &bg, float opacity) { + QPoint center(btn_size_x / 2, btn_size_y / 2); + p.setRenderHint(QPainter::Antialiasing); + p.setOpacity(1.0); // bg dictates opacity of ellipse + p.setPen(Qt::NoPen); + p.setBrush(bg); + p.drawEllipse(center, btn_size_x / 2, btn_size_y / 2); + p.setOpacity(opacity); + p.drawPixmap(center - QPoint(img.width() / 2, img.height() / 2), img); + p.setOpacity(1.0); +} + +// ExperimentalButtonSP +void ExperimentalButtonSP::updateState(const UIStateSP &s) { + const auto cs = (*s.sm)["controlsState"].getControlsState(); + bool eng = cs.getEngageable() || cs.getEnabled(); + if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) { + engageable = eng; + experimental_mode = cs.getExperimentalMode(); + update(); + } +} + + +// OnroadSettingsButton +OnroadSettingsButton::OnroadSettingsButton(QWidget *parent) : QPushButton(parent) { + // btn_size: 192 * 80% ~= 152 + // img_size: (152 / 4) * 3 = 114 + setFixedSize(152, 152); + settings_img = loadPixmap("../assets/navigation/icon_settings.svg", {114, 114}); + + // hidden by default, made visible if Driving Personality / GAC, DLP, DEC, or SLC is enabled + setVisible(false); + setEnabled(false); +} + +void OnroadSettingsButton::paintEvent(QPaintEvent *event) { + QPainter p(this); + drawCustomButtonIcon(p, 152, 152, settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0); +} + +void OnroadSettingsButton::updateState(const UIStateSP &s) { + const auto cp = (*s.sm)["carParams"].getCarParams(); + auto dlp_enabled = s.scene.driving_model_generation == cereal::ModelGeneration::ONE; + bool allow_btn = s.scene.onroad_settings_toggle && (dlp_enabled || hasLongitudinalControl(cp) || !cp.getPcmCruiseSpeed()); + + setVisible(allow_btn); + setEnabled(allow_btn); +} + +// MapSettingsButton +MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) { + // btn_size: 192 * 80% ~= 152 + // img_size: (152 / 4) * 3 = 114 + setFixedSize(152, 152); + settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {114, 114}); + + // hidden by default, made visible if map is created (has prime or mapbox token) + setVisible(false); + setEnabled(false); +} + +void MapSettingsButton::paintEvent(QPaintEvent *event) { + QPainter p(this); + drawCustomButtonIcon(p, 152, 152, settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/buttons.h b/selfdrive/ui/sunnypilot/qt/onroad/buttons.h new file mode 100644 index 0000000000..055f3dab32 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/buttons.h @@ -0,0 +1,68 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include "selfdrive/ui/qt/onroad/buttons.h" + +#include "selfdrive/ui/sunnypilot/ui.h" + +#include "selfdrive/ui/ui.h" + +class ExperimentalButtonSP : public ExperimentalButton { + Q_OBJECT + +public: + explicit ExperimentalButtonSP(QWidget *parent = 0) : ExperimentalButton(parent) {}; + void updateState(const UIStateSP &s); +}; + +class OnroadSettingsButton : public QPushButton { + Q_OBJECT + +public: + explicit OnroadSettingsButton(QWidget *parent = 0); + void updateState(const UIStateSP &s); + +private: + void paintEvent(QPaintEvent *event) override; + + QPixmap settings_img; +}; + + +class MapSettingsButton : public QPushButton { + Q_OBJECT + +public: + explicit MapSettingsButton(QWidget *parent = 0); + +private: + void paintEvent(QPaintEvent *event) override; + + QPixmap settings_img; +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc new file mode 100644 index 0000000000..dd22ebdcb8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc @@ -0,0 +1,224 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h" + +#include + +#include "common/util.h" + +// ********** metrics ********** + +// Add Relative Distance to Primary Lead Car +// Unit: Meters +UiElement DeveloperUi::getDRel(bool lead_status, float lead_d_rel) { + QString value = lead_status ? QString::number(lead_d_rel, 'f', 0) : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lead_status) { + // Orange if close, Red if very close + if (lead_d_rel < 5) { + color = QColor(255, 0, 0, 255); + } else if (lead_d_rel < 15) { + color = QColor(255, 188, 0, 255); + } + } + + return UiElement(value, "REL DIST", "m", color); +} + +// Add Relative Velocity vs Primary Lead Car +// Unit: kph if metric, else mph +UiElement DeveloperUi::getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit) { + QString value = lead_status ? QString::number(lead_v_rel * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lead_status) { + // Red if approaching faster than 10mph + // Orange if approaching (negative) + if (lead_v_rel < -4.4704) { + color = QColor(255, 0, 0, 255); + } else if (lead_v_rel < 0) { + color = QColor(255, 188, 0, 255); + } + } + + return UiElement(value, "REL SPEED", speed_unit, color); +} + +// Add Real Steering Angle +// Unit: Degrees +UiElement DeveloperUi::getSteeringAngleDeg(float angle_steers, bool mads_enabled, bool lat_active) { + QString value = QString("%1%2%3").arg(QString::number(angle_steers, 'f', 1)).arg("°").arg(""); + QColor color = (mads_enabled && lat_active) ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); + + // Red if large steering angle + // Orange if moderate steering angle + if (std::fabs(angle_steers) > 180) { + color = QColor(255, 0, 0, 255); + } else if (std::fabs(angle_steers) > 90) { + color = QColor(255, 188, 0, 255); + } + + return UiElement(value, "REAL STEER", "", color); +} + +// Add Actual Lateral Acceleration (roll compensated) when using Torque +// Unit: m/s² +UiElement DeveloperUi::getActualLateralAccel(float curvature, float v_ego, float roll, bool mads_enabled, bool lat_active) { + double actualLateralAccel = (curvature * pow(v_ego, 2)) - (roll * 9.81); + + QString value = QString::number(actualLateralAccel, 'f', 2); + QColor color = (mads_enabled && lat_active) ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "ACTUAL LAT", "m/s²", color); +} + +// Add Desired Steering Angle when using PID +// Unit: Degrees +UiElement DeveloperUi::getSteeringAngleDesiredDeg(bool mads_enabled, bool lat_active, float steer_angle_desired, float angle_steers) { + QString value = (mads_enabled && lat_active) ? QString("%1%2%3").arg(QString::number(steer_angle_desired, 'f', 1)).arg("°").arg("") : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (mads_enabled && lat_active) { + // Red if large steering angle + // Orange if moderate steering angle + if (std::fabs(angle_steers) > 180) { + color = QColor(255, 0, 0, 255); + } else if (std::fabs(angle_steers) > 90) { + color = QColor(255, 188, 0, 255); + } else { + color = QColor(0, 255, 0, 255); + } + } + + return UiElement(value, "DESIRED STEER", "", color); +} + +// Add Device Memory (RAM) Usage +// Unit: Percent +UiElement DeveloperUi::getMemoryUsagePercent(int memory_usage_percent) { + QString value = QString("%1%2").arg(QString::number(memory_usage_percent, 'd', 0)).arg("%"); + QColor color = (memory_usage_percent > 85) ? QColor(255, 188, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "RAM", "", color); +} + +// Add Vehicle Current Acceleration +// Unit: m/s² +UiElement DeveloperUi::getAEgo(float a_ego) { + QString value = QString::number(a_ego, 'f', 1); + QColor color = QColor(255, 255, 255, 255); + + return UiElement(value, "ACC.", "m/s²", color); +} + +// Add Relative Velocity to Primary Lead Car +// Unit: kph if metric, else mph +UiElement DeveloperUi::getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit) { + QString value = lead_status ? QString::number((lead_v_rel + v_ego) * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lead_status) { + // Red if approaching faster than 10mph + // Orange if approaching (negative) + if (lead_v_rel < -4.4704) { + color = QColor(255, 0, 0, 255); + } else if (lead_v_rel < 0) { + color = QColor(255, 188, 0, 255); + } + } + + return UiElement(value, "L.S.", speed_unit, color); +} + +// Add Friction Coefficient Raw from torqued +// Unit: None +UiElement DeveloperUi::getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid) { + QString value = QString::number(friction_coefficient_filtered, 'f', 3); + QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "FRIC.", "", color); +} + +// Add Lateral Acceleration Factor Raw from torqued +// Unit: m/s² +UiElement DeveloperUi::getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid) { + QString value = QString::number(lat_accel_factor_filtered, 'f', 3); + QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "L.A.", "m/s²", color); +} + +// Add Steering Torque from Car EPS +// Unit: Newton Meters +UiElement DeveloperUi::getSteeringTorqueEps(float steering_torque_eps) { + QString value = QString::number(std::fabs(steering_torque_eps), 'f', 1); + QColor color = QColor(255, 255, 255, 255); + + return UiElement(value, "E.T.", "N·dm", color); +} + +// Add Bearing Degree and Direction from Car (Compass) +// Unit: Meters +UiElement DeveloperUi::getBearingDeg(float bearing_accuracy_deg, float bearing_deg) { + QString value = (bearing_accuracy_deg != 180.00) ? QString("%1%2%3").arg(QString::number(bearing_deg, 'd', 0)).arg("°").arg("") : "-"; + QColor color = QColor(255, 255, 255, 255); + QString dir_value; + + if (bearing_accuracy_deg != 180.00) { + if (((bearing_deg >= 337.5) && (bearing_deg <= 360)) || ((bearing_deg >= 0) && (bearing_deg <= 22.5))) { + dir_value = "N"; + } else if ((bearing_deg > 22.5) && (bearing_deg < 67.5)) { + dir_value = "NE"; + } else if ((bearing_deg >= 67.5) && (bearing_deg <= 112.5)) { + dir_value = "E"; + } else if ((bearing_deg > 112.5) && (bearing_deg < 157.5)) { + dir_value = "SE"; + } else if ((bearing_deg >= 157.5) && (bearing_deg <= 202.5)) { + dir_value = "S"; + } else if ((bearing_deg > 202.5) && (bearing_deg < 247.5)) { + dir_value = "SW"; + } else if ((bearing_deg >= 247.5) && (bearing_deg <= 292.5)) { + dir_value = "W"; + } else if ((bearing_deg > 292.5) && (bearing_deg < 337.5)) { + dir_value = "NW"; + } + } else { + dir_value = "OFF"; + } + + return UiElement(QString("%1 | %2").arg(dir_value).arg(value), "B.D.", "", color); +} + +// Add Altitude of Current Location +// Unit: Meters +UiElement DeveloperUi::getAltitude(float gps_accuracy, float altitude) { + QString value = (gps_accuracy != 0.00) ? QString::number(altitude, 'f', 1) : "-"; + QColor color = QColor(255, 255, 255, 255); + + return UiElement(value, "ALT.", "m", color); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h new file mode 100644 index 0000000000..2cb9b9a76a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h @@ -0,0 +1,46 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h" + +class DeveloperUi { +public: + static UiElement getDRel(bool lead_status, float lead_d_rel); + static UiElement getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit); + static UiElement getSteeringAngleDeg(float angle_steers, bool mads_enabled, bool lat_active); + static UiElement getActualLateralAccel(float curvature, float v_ego, float roll, bool mads_enabled, bool lat_active); + static UiElement getSteeringAngleDesiredDeg(bool mads_enabled, bool lat_active, float steer_angle_desired, float angle_steers); + static UiElement getMemoryUsagePercent(int memory_usage_percent); + static UiElement getAEgo(float a_ego); + static UiElement getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit); + static UiElement getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid); + static UiElement getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid); + static UiElement getSteeringTorqueEps(float steering_torque_eps); + static UiElement getBearingDeg(float bearing_accuracy_deg, float bearing_deg); + static UiElement getAltitude(float gps_accuracy, float altitude); +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h new file mode 100644 index 0000000000..3910c83022 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h @@ -0,0 +1,39 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +struct UiElement { + QString value{}; + QString label{}; + QString units{}; + QColor color{}; + + explicit UiElement(const QString &value = "", const QString &label = "", const QString &units = "", const QColor &color = QColor(255, 255, 255, 255)) + : value(value), label(label), units(units), color(color) {} +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc new file mode 100644 index 0000000000..a7acf4865c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc @@ -0,0 +1,163 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h" + + +#ifdef ENABLE_MAPS +#include "selfdrive/ui/sunnypilot/qt/maps/map_helpers.h" +#include "selfdrive/ui/qt/maps/map_panel.h" +#endif +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.h" +#endif + +#include "common/swaglog.h" +#include "selfdrive/ui/qt/util.h" + +OnroadWindowSP::OnroadWindowSP(QWidget *parent) : OnroadWindow(parent) { + // QObject::disconnect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState); + // QObject::disconnect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition); + + if (getenv("MAP_RENDER_VIEW")) { + CameraWidget *map_render = new CameraWidget("navd", VISION_STREAM_MAP, false, this); + split->insertWidget(0, map_render); //TODO: We MIGHT to override the split variable because it is added on onroad_home.cc and we need to access it before. I am not sure so we will need to test it before + } + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &OnroadWindowSP::updateState); + QObject::connect(uiStateSP(), &UIStateSP::offroadTransition, this, &OnroadWindowSP::offroadTransition); +} + +void OnroadWindowSP::updateState(const UIStateSP &s) { + if (!s.scene.started) { + return; + } + + if (s.scene.map_on_left || s.scene.mapbox_fullscreen) { + split->setDirection(QBoxLayout::LeftToRight); + } else { + split->setDirection(QBoxLayout::RightToLeft); + } + + OnroadWindow::updateState(s); // Carry on with the parent class updateState method +} + + +void OnroadWindowSP::mousePressEvent(QMouseEvent* e) { +#ifdef ENABLE_MAPS + UIStateSP *s = uiStateSP(); + UISceneSP &scene = s->scene; + if (map != nullptr && !isOnroadSettingsVisible()) { + if (wakeScreenTimeout()) { + // Switch between map and sidebar when using navigate on openpilot + bool sidebarVisible = geometry().x() > 0; + bool show_map = uiStateSP()->scene.navigate_on_openpilot_deprecated ? sidebarVisible : !sidebarVisible; + updateMapSize(scene); + map->setVisible(show_map && !map->isVisible()); + } + } +#endif + if (onroad_settings != nullptr && !isMapVisible()) { + if (wakeScreenTimeout()) { + onroad_settings->setVisible(false); + } + } + // propagation event to parent(HomeWindow) + QWidget::mousePressEvent(e); +} + +void OnroadWindowSP::createMapWidget() { +#ifdef ENABLE_MAPS + LOGD("Creating map widget"); + auto m = new MapPanel(get_mapbox_settings()); + map = m; + + QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindowSP::mapPanelRequested); + QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindowSP::onroadSettingsPanelNotRequested); + QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings); + nvg->map_settings_btn->setEnabled(true); + + m->setFixedWidth(uiStateSP()->scene.mapbox_fullscreen ? topWidget(this)->width() : + topWidget(this)->width() / 2 - UI_BORDER_SIZE); + split->insertWidget(0, m); + + // hidden by default, made visible when navRoute is published + m->setVisible(false); +#endif +} + +void OnroadWindowSP::createOnroadSettingsWidget() { + auto os = new OnroadSettingsPanel(this); + onroad_settings = os; + + QObject::connect(os, &OnroadSettingsPanel::onroadSettingsPanelRequested, this, &OnroadWindowSP::onroadSettingsPanelRequested); + QObject::connect(os, &OnroadSettingsPanel::onroadSettingsPanelRequested, this, &OnroadWindowSP::mapPanelNotRequested); + QObject::connect(nvg->onroad_settings_btn, &OnroadSettingsButton::clicked, os, &OnroadSettingsPanel::toggleOnroadSettings); + nvg->onroad_settings_btn->setEnabled(true); + + os->setFixedWidth(topWidget(this)->width() / 2.6 - UI_BORDER_SIZE); + split->insertWidget(0, os); + + // hidden by default + os->setVisible(false); +} + +void OnroadWindowSP::offroadTransition(bool offroad) { + + if (!offroad) { +#ifdef ENABLE_MAPS + LOGD("We'd like to create the map widget, the condition is map is [%s] and hasPrime is [%s] and MAPBOX_TOKEN is [%s]", map == nullptr ? "null" : "not null", uiStateSP()->hasPrime() ? "true" : "false", MAPBOX_TOKEN.isEmpty() ? "empty" : "not empty"); + if (map == nullptr && (uiStateSP()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) { + createMapWidget(); + } +#else + LOGD("Maps are disabled"); +#endif + if (onroad_settings == nullptr) { + createOnroadSettingsWidget(); + } + } + + OnroadWindow::offroadTransition(offroad); // Carry on with the parent class offroadTransition method +} + +void OnroadWindowSP::updateMapSize(const UISceneSP &scene) { + map->setFixedWidth(scene.mapbox_fullscreen ? topWidget(this)->width() : + topWidget(this)->width() / 2 - UI_BORDER_SIZE); + split->insertWidget(0, map); +} + +void OnroadWindowSP::primeChanged(bool prime) { +#ifdef ENABLE_MAPS + if (map && (!prime && MAPBOX_TOKEN.isEmpty())) { + nvg->map_settings_btn->setEnabled(false); + nvg->map_settings_btn->setVisible(false); + map->deleteLater(); + map = nullptr; + } else if (!map && (prime || !MAPBOX_TOKEN.isEmpty())) { + createMapWidget(); + } +#endif +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h new file mode 100644 index 0000000000..121c0ba75d --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h @@ -0,0 +1,72 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/onroad/onroad_home.h" + +#include "common/params.h" + +class OnroadWindowSP : public OnroadWindow { + Q_OBJECT + +public: + OnroadWindowSP(QWidget* parent = 0); + bool isMapVisible() const { return map && map->isVisible(); } + void showMapPanel(bool show) { if (map) map->setVisible(show); } + + bool isOnroadSettingsVisible() const { return onroad_settings && onroad_settings->isVisible(); } + bool isMapAvailable() const { return map; } + void mapPanelNotRequested() { if (map) map->setVisible(false); } + void onroadSettingsPanelNotRequested() { if (onroad_settings) onroad_settings->setVisible(false); } + + bool wakeScreenTimeout() { + if ((uiStateSP()->scene.sleep_btn != 0 && uiStateSP()->scene.sleep_btn_opacity != 0) || + (uiStateSP()->scene.sleep_time != 0 && uiStateSP()->scene.onroadScreenOff != -2)) { + return true; + } + return false; + } + +signals: + void mapPanelRequested(); + void onroadSettingsPanelRequested(); + +private: + void createMapWidget(); + void createOnroadSettingsWidget(); + void mousePressEvent(QMouseEvent* e) override; + QWidget *map = nullptr; + QWidget *onroad_settings = nullptr; + + Params params; + +protected slots: + void offroadTransition(bool offroad) override; + void updateState(const UIStateSP &s) override; + void primeChanged(bool prime); + void updateMapSize(const UISceneSP &scene); +}; diff --git a/selfdrive/ui/qt/onroad_settings.cc b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.cc similarity index 86% rename from selfdrive/ui/qt/onroad_settings.cc rename to selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.cc index 00810294ba..e886e4b43e 100644 --- a/selfdrive/ui/qt/onroad_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.cc @@ -1,13 +1,40 @@ -#include "selfdrive/ui/qt/onroad_settings.h" +/** +The MIT License +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.h" + +#include +#include #include #include -#include #include "common/util.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" -#include "selfdrive/ui/qt/offroad/sunnypilot/speed_limit_policy_settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/speed_limit_policy_settings.h" OnroadSettings::OnroadSettings(bool closeable, QWidget *parent) : QFrame(parent) { setContentsMargins(0, 0, 0, 0); @@ -87,7 +114,7 @@ OnroadSettings::OnroadSettings(bool closeable, QWidget *parent) : QFrame(parent) options_layout->addStretch(); - ScrollView *options_scroller = new ScrollView(options_container, this); + ScrollViewSP *options_scroller = new ScrollViewSP(options_container, this); options_scroller->setFrameShape(QFrame::NoFrame); frame->addWidget(options_scroller); @@ -118,7 +145,7 @@ OnroadSettings::OnroadSettings(bool closeable, QWidget *parent) : QFrame(parent) } void OnroadSettings::changeDynamicLaneProfile() { - UIScene &scene = uiState()->scene; + UISceneSP &scene = uiStateSP()->scene; bool can_change = scene.driving_model_generation == cereal::ModelGeneration::ONE; if (can_change) { scene.dynamic_lane_profile++; @@ -129,8 +156,8 @@ void OnroadSettings::changeDynamicLaneProfile() { } void OnroadSettings::changeGapAdjustCruise() { - UIScene &scene = uiState()->scene; - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); + UISceneSP &scene = uiStateSP()->scene; + const auto cp = (*uiStateSP()->sm)["carParams"].getCarParams(); bool can_change = hasLongitudinalControl(cp); if (can_change) { scene.longitudinal_personality--; @@ -141,8 +168,8 @@ void OnroadSettings::changeGapAdjustCruise() { } void OnroadSettings::changeAccelerationPersonality() { - UIScene &scene = uiState()->scene; - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); + UISceneSP &scene = uiStateSP()->scene; + const auto cp = (*uiStateSP()->sm)["carParams"].getCarParams(); bool can_change = hasLongitudinalControl(cp); if (can_change) { scene.longitudinal_accel_personality--; @@ -153,8 +180,8 @@ void OnroadSettings::changeAccelerationPersonality() { } void OnroadSettings::changeDynamicPersonality() { - UIScene &scene = uiState()->scene; - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); + UISceneSP &scene = uiStateSP()->scene; + const auto cp = (*uiStateSP()->sm)["carParams"].getCarParams(); bool can_change = hasLongitudinalControl(cp); if (can_change) { scene.dynamic_personality = !scene.dynamic_personality; @@ -164,8 +191,8 @@ void OnroadSettings::changeDynamicPersonality() { } void OnroadSettings::changeDynamicExperimentalControl() { - UIScene &scene = uiState()->scene; - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); + UISceneSP &scene = uiStateSP()->scene; + const auto cp = (*uiStateSP()->sm)["carParams"].getCarParams(); bool can_change = hasLongitudinalControl(cp); if (can_change) { scene.dynamic_experimental_control = !scene.dynamic_experimental_control; @@ -175,8 +202,8 @@ void OnroadSettings::changeDynamicExperimentalControl() { } void OnroadSettings::changeSpeedLimitControl() { - UIScene &scene = uiState()->scene; - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); + UISceneSP &scene = uiStateSP()->scene; + const auto cp = (*uiStateSP()->sm)["carParams"].getCarParams(); bool can_change = hasLongitudinalControl(cp) || !cp.getPcmCruiseSpeed(); int max_policy = SpeedLimitPolicySettings::speed_limit_policy_texts.size() - 1; @@ -185,7 +212,7 @@ void OnroadSettings::changeSpeedLimitControl() { // If EnableSlc is OFF, set it to ON and reset SpeedLimitControlPolicy scene.speed_limit_control_enabled = true; scene.speed_limit_control_policy = 0; - } else if(scene.speed_limit_control_policy < max_policy) { + } else if (scene.speed_limit_control_policy < max_policy) { // If EnableSlc is already ON then increase SpeedLimitControlPolicy till it reaches 6 scene.speed_limit_control_policy++; } else { @@ -211,7 +238,7 @@ void OnroadSettings::refresh() { param_watcher->addParam("DynamicExperimentalControl"); param_watcher->addParam("EnableSlc"); - UIScene &scene = uiState()->scene; + UISceneSP &scene = uiStateSP()->scene; // Update live params on Feature Status on camera view scene.dynamic_lane_profile = std::atoi(params.get("DynamicLaneProfile").c_str()); scene.longitudinal_personality = std::atoi(params.get("LongitudinalPersonality").c_str()); @@ -225,7 +252,7 @@ void OnroadSettings::refresh() { setUpdatesEnabled(false); - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); + const auto cp = (*uiStateSP()->sm)["carParams"].getCarParams(); // Dynamic Lane Profile dlp_widget->updateDynamicLaneProfile("DynamicLaneProfile"); @@ -271,11 +298,11 @@ OptionWidget::OptionWidget(QWidget *parent) : QPushButton(parent) { inner_frame->setContentsMargins(0, 0, 0, 0); inner_frame->setSpacing(0); { - title = new ElidedLabel(this); + title = new ElidedLabelSP(this); title->setAttribute(Qt::WA_TransparentForMouseEvents); inner_frame->addWidget(title); - subtitle = new ElidedLabel(this); + subtitle = new ElidedLabelSP(this); subtitle->setAttribute(Qt::WA_TransparentForMouseEvents); subtitle->setObjectName("subtitle"); inner_frame->addWidget(subtitle); @@ -304,12 +331,10 @@ void OptionWidget::updateDynamicLaneProfile(QString param) { if (dlp == 0) { title_text = "Laneful"; icon_color = "#2020f8"; - } - else if (dlp == 1) { + } else if (dlp == 1) { title_text = "Laneless"; icon_color = "#0df87a"; - } - else if (dlp == 2) { + } else if (dlp == 2) { title_text = "Auto"; icon_color = "#0df8f8"; } @@ -451,8 +476,7 @@ void OptionWidget::updateSpeedLimitControl(QString param) { if (enable_slc_status == 0) { title_text = "Disabled"; icon_color = "#3B4356"; // Color for "Disabled status" - } - else if (enable_slc_status == 1) { + } else if (enable_slc_status == 1) { title_text = title_text; icon_color = color_map[speed_limit_control_policy_status]; // Color for "Enabled status" } diff --git a/selfdrive/ui/qt/onroad_settings.h b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.h similarity index 51% rename from selfdrive/ui/qt/onroad_settings.h rename to selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.h index fe881d1f07..372688bfa5 100644 --- a/selfdrive/ui/qt/onroad_settings.h +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.h @@ -1,14 +1,39 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + #pragma once -#include -#include -#include -#include - #include "common/params.h" -#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/sunnypilot/ui.h" #include "selfdrive/ui/qt/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif class OptionWidget; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.cc b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.cc new file mode 100644 index 0000000000..47a6f06235 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.cc @@ -0,0 +1,51 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.h" + +#include + +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_settings.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" + +OnroadSettingsPanel::OnroadSettingsPanel(QWidget *parent) : QFrame(parent) { + content_stack = new QStackedLayout(this); + content_stack->setContentsMargins(0, 0, 0, 0); + + auto onroad_settings = new OnroadSettings(true, parent); + QObject::connect(onroad_settings, &OnroadSettings::closeSettings, this, &OnroadSettings::hide); + content_stack->addWidget(onroad_settings); +} + +void OnroadSettingsPanel::toggleOnroadSettings() { + if (isVisible()) { + hide(); + } else { + emit onroadSettingsPanelRequested(); + show(); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.h b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.h new file mode 100644 index 0000000000..57603c4e88 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_settings_panel.h @@ -0,0 +1,49 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#ifdef ENABLE_MAPS +#include +#endif +#include + +class OnroadSettingsPanel : public QFrame { + Q_OBJECT + +public: + explicit OnroadSettingsPanel(QWidget *parent = nullptr); + +signals: + void onroadSettingsPanelRequested(); + +public slots: + void toggleOnroadSettings(); + +private: + QStackedLayout *content_stack; +}; diff --git a/selfdrive/ui/sunnypilot/qt/request_repeater.cc b/selfdrive/ui/sunnypilot/qt/request_repeater.cc new file mode 100644 index 0000000000..91ac3cbff8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/request_repeater.cc @@ -0,0 +1,62 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/request_repeater.h" + +#include "common/swaglog.h" + +RequestRepeaterSP::RequestRepeaterSP(QObject *parent, const QString &requestURL, const QString &cacheKey, + int period, bool whileOnroad, bool sunnylink) : HttpRequestSP(parent, true, 20000, sunnylink) { + request_url = requestURL; + while_onroad = whileOnroad; + timer = new QTimer(this); + timer->setTimerType(Qt::VeryCoarseTimer); + connect(timer, &QTimer::timeout, [=]() { this->timerTick(); }); + timer->start(period * 1000); + + if (!cacheKey.isEmpty()) { + prevResp = QString::fromStdString(params.get(cacheKey.toStdString())); + if (!prevResp.isEmpty()) { + QTimer::singleShot(500, [=]() { emit requestDone(prevResp, true, QNetworkReply::NoError); }); + } + connect(this, &HttpRequest::requestDone, [=](const QString &resp, bool success) { + if (success && resp != prevResp) { + params.put(cacheKey.toStdString(), resp.toStdString()); + prevResp = resp; + } + }); + } + + // Don't wait for the timer to fire to send the first request + ForceUpdate(); +} + +void RequestRepeaterSP::timerTick() { + if ((!uiState()->scene.started || while_onroad) && device()->isAwake() && !active()) { + LOGD("Sending request for %s", qPrintable(request_url)); + sendRequest(request_url); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/request_repeater.h b/selfdrive/ui/sunnypilot/qt/request_repeater.h new file mode 100644 index 0000000000..2af97a0fca --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/request_repeater.h @@ -0,0 +1,52 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/request_repeater.h" + +#include "common/swaglog.h" +#include "common/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/api.h" + +class RequestRepeaterSP : public HttpRequestSP { + +private: + Params params; + QTimer *timer; + QString prevResp; + QString request_url; + bool while_onroad; + void timerTick(); + +public: + RequestRepeaterSP(QObject *parent, const QString &requestURL, const QString &cacheKey = "", int period = 0, bool whileOnroad=false, bool sunnylink = false); + void ForceUpdate() { + LOGD("Forcing update for %s", qPrintable(request_url)); + timerTick(); + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/sidebar.cc b/selfdrive/ui/sunnypilot/qt/sidebar.cc new file mode 100644 index 0000000000..67447a57e6 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/sidebar.cc @@ -0,0 +1,132 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/sidebar.h" + +#include + +#include + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/qt/util.h" +#include "common/params.h" + +SidebarSP::SidebarSP(QWidget *parent) : Sidebar(parent) { + // Because I know that stock sidebar makes this connection, I will disconnect it and connect it to the new updateState function + QObject::disconnect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &SidebarSP::updateState); +} + +void SidebarSP::updateState(const UIStateSP &s) { + if (!isVisible()) return; + Sidebar::updateState(s); + auto &sm = *(s.sm); + auto deviceState = sm["deviceState"].getDeviceState(); + + if (sm.frame % UI_FREQ == 0) { // Update every 1 Hz + switch (s.scene.sidebar_temp_options) { + case 0: + break; + case 1: + sidebar_temp = QString::number((int)deviceState.getMemoryTempC()); + break; + case 2: { + const auto& cpu_temp_list = deviceState.getCpuTempC(); + float max_cpu_temp = std::numeric_limits::lowest(); + + for (const float& temp : cpu_temp_list) { + max_cpu_temp = std::max(max_cpu_temp, temp); + } + + if (max_cpu_temp >= 0) { + sidebar_temp = QString::number(std::nearbyint(max_cpu_temp)); + } + break; + } + case 3: { + const auto& gpu_temp_list = deviceState.getGpuTempC(); + float max_gpu_temp = std::numeric_limits::lowest(); + + for (const float& temp : gpu_temp_list) { + max_gpu_temp = std::max(max_gpu_temp, temp); + } + + if (max_gpu_temp >= 0) { + sidebar_temp = QString::number(std::nearbyint(max_gpu_temp)); + } + break; + } + case 4: + sidebar_temp = QString::number((int)deviceState.getMaxTempC()); + break; + default: + break; + } + + setProperty("sidebarTemp", sidebar_temp + "°C"); + } + + bool show_sidebar_temp = s.scene.sidebar_temp_options != 0; + ItemStatus tempStatus = {{tr("TEMP"), show_sidebar_temp ? sidebar_temp_str : tr("HIGH")}, danger_color}; + auto ts = deviceState.getThermalStatus(); + if (ts == cereal::DeviceState::ThermalStatus::GREEN) { + tempStatus = {{tr("TEMP"), show_sidebar_temp ? sidebar_temp_str : tr("GOOD")}, good_color}; + } else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) { + tempStatus = {{tr("TEMP"), show_sidebar_temp ? sidebar_temp_str : tr("OK")}, warning_color}; + } + setProperty("tempStatus", QVariant::fromValue(tempStatus)); + + ItemStatus sunnylinkStatus; + auto sl_dongle_id = getSunnylinkDongleId(); + auto last_sunnylink_ping_str = params.get("LastSunnylinkPingTime"); + auto last_sunnylink_ping = std::stoull(last_sunnylink_ping_str.empty() ? "0" : last_sunnylink_ping_str); + auto elapsed_sunnylink_ping = nanos_since_boot() - last_sunnylink_ping; + auto sunnylink_enabled = params.getBool("SunnylinkEnabled"); + + QString status = tr("DISABLED"); + QColor color = disabled_color; + + if (sunnylink_enabled && last_sunnylink_ping == 0) { + // If sunnylink is enabled, but we don't have a dongle id, and we haven't received a ping yet, we are registering + status = sl_dongle_id.has_value() ? tr("OFFLINE") : tr("REGIST..."); + color = sl_dongle_id.has_value() ? warning_color : progress_color; + } else if (sunnylink_enabled) { + // If sunnylink is enabled, we are considered online if we have received a ping in the last 80 seconds, else error. + status = elapsed_sunnylink_ping < 80000000000ULL ? tr("ONLINE") : tr("ERROR"); + color = elapsed_sunnylink_ping < 80000000000ULL ? good_color : danger_color; + } + sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), status}, color }; + setProperty("sunnylinkStatus", QVariant::fromValue(sunnylinkStatus)); +} + +void SidebarSP::DrawSidebar(QPainter &p){ + Sidebar::DrawSidebar(p); + // metrics + drawMetric(p, temp_status.first, temp_status.second, 310); + drawMetric(p, panda_status.first, panda_status.second, 440); + drawMetric(p, connect_status.first, connect_status.second, 570); + drawMetric(p, sunnylink_status.first, sunnylink_status.second, 700); +} diff --git a/selfdrive/ui/sunnypilot/qt/sidebar.h b/selfdrive/ui/sunnypilot/qt/sidebar.h new file mode 100644 index 0000000000..69de76f7a0 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/sidebar.h @@ -0,0 +1,57 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include + +#include "selfdrive/ui/qt/sidebar.h" + +#include "selfdrive/ui/sunnypilot/ui.h" + +class SidebarSP : public Sidebar { + Q_OBJECT + Q_PROPERTY(ItemStatus sunnylinkStatus MEMBER sunnylink_status NOTIFY valueChanged); + Q_PROPERTY(QString sidebarTemp MEMBER sidebar_temp_str NOTIFY valueChanged); + +public: + explicit SidebarSP(QWidget* parent = 0); + +public slots: + void updateState(const UIStateSP &s); + +protected: + const QColor progress_color = QColor(3, 132, 252); + const QColor disabled_color = QColor(128, 128, 128); + + ItemStatus sunnylink_status; + +private: + Params params; + void DrawSidebar(QPainter &p) override; + QString sidebar_temp = "0"; + QString sidebar_temp_str = "0"; +}; diff --git a/selfdrive/ui/sunnypilot/qt/text.cc b/selfdrive/ui/sunnypilot/qt/text.cc new file mode 100644 index 0000000000..50e4174ece --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/text.cc @@ -0,0 +1,162 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/params.h" +#include "common/swaglog.h" +#include "system/hardware/hw.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +std::string executeCommand(const char* cmd) { + std::array buffer{}; + std::ostringstream result; + std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) { + LOGW("Failed to open pipe"); + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result << buffer.data(); + } + return result.str(); +} + +// The format is intentional! +QString text = QString("%1\n%2\n%3\n%4\n%5\n%6\n%7") + .arg(" ________________________________________") + .arg("| |") + .arg("| " + QObject::tr("Update downloaded. Ready to reboot.") + " |") + .arg("| |") + .arg("| " + QObject::tr("Update: Check and Download Update") + " |") + .arg("| " + QObject::tr("Reboot: Reboot Device") + " |") + .arg("|________________________________________|"); + +int main(int argc, char *argv[]) { + initApp(argc, argv); + QApplication a(argc, argv); + QWidget window; + setMainWindow(&window); + + QGridLayout *main_layout = new QGridLayout(&window); + main_layout->setMargin(50); + + QLabel *label = new QLabel(argv[1]); + label->setWordWrap(true); + label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + label->setAlignment(Qt::AlignTop | Qt::AlignLeft); + ScrollView *scroll = new ScrollView(label); + scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + main_layout->addWidget(scroll, 0, 0, Qt::AlignTop); + + // Scroll to the bottom + QObject::connect(scroll->verticalScrollBar(), &QAbstractSlider::rangeChanged, [=]() { + scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum()); + }); + + QPushButton *btn = new QPushButton(); + QPushButton *update_btn = new QPushButton(); + update_btn->setText(QObject::tr("Update")); +#ifdef __aarch64__ + btn->setText(QObject::tr("Reboot")); + QFutureWatcher watcher; + QObject::connect(btn, &QPushButton::clicked, [=]() { + Hardware::reboot(); + }); + QObject::connect(update_btn, &QPushButton::clicked, [=, &watcher]() { + btn->setEnabled(false); + update_btn->setEnabled(false); + update_btn->setText(QObject::tr("Updating...")); + const std::string git_branch = Params().get("GitBranch"); + const std::string git_remote = Params().get("GitRemote"); + const std::string to_home_dir = "cd /data/openpilot"; + const std::string check_remote = "git remote | grep origin-update"; + const std::string reset_remote = "git remote remove origin-update && git remote add origin-update " + git_remote; + const std::string add_remote = "git remote add origin-update " + git_remote; + const std::string fetch_remote = "git fetch origin-update " + git_branch; + const std::string reset_branch = "git reset --hard origin-update/" + git_branch + " 2>&1"; + + std::string remote_cmd = add_remote; + if (!std::system((to_home_dir + " && " + check_remote).c_str())) { + remote_cmd = reset_remote; + } + const std::string cmd = to_home_dir + "; " + remote_cmd + "; " + fetch_remote + "; " + reset_branch; + + QFuture future = QtConcurrent::run([=]() { + label->clear(); + std::string output = executeCommand(cmd.c_str()); + //LOGW("CHECK OUTPUT PLS\n%s", output.c_str()); + QMetaObject::invokeMethod(label, "setText", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(output) + text)); + }); + QObject::connect(&watcher, &QFutureWatcher::finished, [=]() { + btn->setEnabled(true); + update_btn->setEnabled(true); + update_btn->setText(QObject::tr("Update")); + }); + watcher.setFuture(future); + }); +#else + update_btn->setEnabled(false); + btn->setText(QObject::tr("Exit")); + QObject::connect(btn, &QPushButton::clicked, &a, &QApplication::quit); +#endif + main_layout->addWidget(btn, 0, 0, Qt::AlignRight | Qt::AlignBottom); + main_layout->addWidget(update_btn, 0, 0, Qt::AlignLeft | Qt::AlignBottom); + + window.setStyleSheet(R"( + * { + outline: none; + color: white; + background-color: black; + font-size: 60px; + } + QPushButton { + padding: 50px; + padding-right: 100px; + padding-left: 100px; + border: 2px solid white; + border-radius: 20px; + margin-right: 40px; + } + QPushButton:disabled { + color: #33FFFFFF; + border: 2px solid #33FFFFFF; + } + )"); + + return a.exec(); +} diff --git a/selfdrive/ui/sunnypilot/qt/ui_scene.h b/selfdrive/ui/sunnypilot/qt/ui_scene.h new file mode 100644 index 0000000000..e590c94046 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/ui_scene.h @@ -0,0 +1,115 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +const float DRIVING_PATH_WIDE = 0.9; +const float DRIVING_PATH_NARROW = 0.25; + +typedef struct UISceneSP : UIScene { + cereal::ControlsState::Reader controlsState; + + // Debug UI + bool show_debug_ui; + + // Speed limit control + bool speed_limit_control_enabled; + int speed_limit_control_policy; + double last_speed_limit_sign_tap; + + QPolygonF track_edge_vertices; + QPolygonF lane_barrier_vertices[2]; + + bool navigate_on_openpilot_deprecated = false; + cereal::AccelerationPersonality accel_personality; + + bool map_on_left; + + int dynamic_lane_profile; + bool dynamic_lane_profile_status = true; + + bool visual_brake_lights; + + int onroadScreenOff, osoTimer, brightness, onroadScreenOffBrightness, awake; + bool onroadScreenOffEvent; + int sleep_time = -1; + bool touched2 = false; + + bool stand_still_timer; + + bool hide_vego_ui, true_vego_ui; + + int chevron_data; + + bool gac; + int longitudinal_personality; + int longitudinal_accel_personality; + + bool map_visible; + int dev_ui_info; + bool live_torque_toggle; + + bool touch_to_wake = false; + int sleep_btn = -1; + bool sleep_btn_fading_in = false; + int sleep_btn_opacity = 20; + bool button_auto_hide; + + bool reverse_dm_cam; + + bool e2e_long_alert_light, e2e_long_alert_lead, e2e_long_alert_ui; + float e2eX[13] = {0}; + + int sidebar_temp_options; + + float mads_path_scale = DRIVING_PATH_WIDE - DRIVING_PATH_NARROW; + float mads_path_range = DRIVING_PATH_WIDE - DRIVING_PATH_NARROW; // 0.9 - 0.25 = 0.65 + + bool onroad_settings_visible; + + bool map_3d_buildings; + + bool torqued_override; + + bool dynamic_experimental_control; + + int speed_limit_control_engage_type; + + bool mapbox_fullscreen; + + bool speed_limit_warning_flash; + int speed_limit_warning_type; + int speed_limit_warning_value_offset; + + bool custom_driving_model_valid; + cereal::ModelGeneration driving_model_generation; + uint32_t driving_model_capabilities; + + bool feature_status_toggle; + bool onroad_settings_toggle; + + bool dynamic_personality; +} UISceneSP; diff --git a/selfdrive/ui/sunnypilot/qt/util.cc b/selfdrive/ui/sunnypilot/qt/util.cc new file mode 100644 index 0000000000..871a22f04e --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/util.cc @@ -0,0 +1,104 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/util.h" +#include "selfdrive/ui/qt/util.h" + +#include +#include + +#include +#include +#include +#include + +#include "system/hardware/hw.h" + +QString getUserAgent(bool sunnylink) { + return (sunnylink ? "sunnypilot-" : "openpilot-") + getVersion(); +} + +std::optional getSunnylinkDongleId() { + return getParamIgnoringDefault("SunnylinkDongleId", "UnregisteredDevice"); +} + +QMap getCarNames() { + return getFromJsonFile("../car/sunnypilot_carname.json"); +} + +void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom){ + qreal w_2 = rect.width() / 2; + qreal h_2 = rect.height() / 2; + + xRadiusTop = 100 * qMin(xRadiusTop, w_2) / w_2; + yRadiusTop = 100 * qMin(yRadiusTop, h_2) / h_2; + + xRadiusBottom = 100 * qMin(xRadiusBottom, w_2) / w_2; + yRadiusBottom = 100 * qMin(yRadiusBottom, h_2) / h_2; + + qreal x = rect.x(); + qreal y = rect.y(); + qreal w = rect.width(); + qreal h = rect.height(); + + qreal rxx2Top = w*xRadiusTop/100; + qreal ryy2Top = h*yRadiusTop/100; + + qreal rxx2Bottom = w*xRadiusBottom/100; + qreal ryy2Bottom = h*yRadiusBottom/100; + + QPainterPath path; + path.arcMoveTo(x, y, rxx2Top, ryy2Top, 180); + path.arcTo(x, y, rxx2Top, ryy2Top, 180, -90); + path.arcTo(x+w-rxx2Top, y, rxx2Top, ryy2Top, 90, -90); + path.arcTo(x+w-rxx2Bottom, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 0, -90); + path.arcTo(x, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 270, -90); + path.closeSubpath(); + + painter.drawPath(path); +} + +QColor interpColor(float xv, std::vector xp, std::vector fp) { + assert(xp.size() == fp.size()); + + int N = xp.size(); + int hi = 0; + + while (hi < N and xv > xp[hi]) hi++; + int low = hi - 1; + + if (hi == N && xv > xp[low]) { + return fp[fp.size() - 1]; + } else if (hi == 0){ + return fp[0]; + } else { + return QColor( + (xv - xp[low]) * (fp[hi].red() - fp[low].red()) / (xp[hi] - xp[low]) + fp[low].red(), + (xv - xp[low]) * (fp[hi].green() - fp[low].green()) / (xp[hi] - xp[low]) + fp[low].green(), + (xv - xp[low]) * (fp[hi].blue() - fp[low].blue()) / (xp[hi] - xp[low]) + fp[low].blue(), + (xv - xp[low]) * (fp[hi].alpha() - fp[low].alpha()) / (xp[hi] - xp[low]) + fp[low].alpha()); + } +} \ No newline at end of file diff --git a/selfdrive/ui/sunnypilot/qt/util.h b/selfdrive/ui/sunnypilot/qt/util.h new file mode 100644 index 0000000000..cafcfc6201 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/util.h @@ -0,0 +1,39 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +#include +#include + +QString getUserAgent(bool sunnylink = false); +std::optional getSunnylinkDongleId(); +QMap getCarNames(); +void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom); +QColor interpColor(float xv, std::vector xp, std::vector fp); diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc new file mode 100644 index 0000000000..f1168e96e8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc @@ -0,0 +1,247 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +#include +#include +#include "selfdrive/ui/sunnypilot/sunnypilot_main.h" + +QFrame *horizontal_line(QWidget *parent) { + QFrame *line = new QFrame(parent); + line->setFrameShape(QFrame::StyledPanel); + line->setStyleSheet(R"( + border-width: 2px; + border-bottom-style: solid; + border-color: gray; + )"); + line->setFixedHeight(10); + return line; +} + +AbstractControlSP::AbstractControlSP(const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : AbstractControl(title, desc, icon, parent) { + + main_layout = new QVBoxLayout(this); + main_layout->setMargin(0); + + hlayout = new QHBoxLayout; + hlayout->setMargin(0); + hlayout->setSpacing(20); + + // left icon + icon_label = new QLabel(this); + hlayout->addWidget(icon_label); + if (!icon.isEmpty()) { + icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation); + icon_label->setPixmap(icon_pixmap); + icon_label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + } + icon_label->setVisible(!icon.isEmpty()); + + // title + title_label = new QPushButton(title); + title_label->setFixedHeight(120); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); + hlayout->addWidget(title_label, 1); + + // value next to control button + value = new ElidedLabelSP(); + value->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + value->setStyleSheet("color: #aaaaaa"); + hlayout->addWidget(value); + + main_layout->addLayout(hlayout); + + // description + description = new QLabel(desc); + description->setContentsMargins(40, 20, 40, 20); + description->setStyleSheet("font-size: 40px; color: grey"); + description->setWordWrap(true); + description->setVisible(false); + main_layout->addWidget(description); + + connect(title_label, &QPushButton::clicked, [=]() { + if (!description->isVisible()) { + emit showDescriptionEvent(); + } + + if (!description->text().isEmpty()) { + description->setVisible(!description->isVisible()); + } + }); + + main_layout->addStretch(); +} + +void AbstractControlSP::hideEvent(QHideEvent *e) { + if (description != nullptr) { + description->hide(); + } +} + +AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : AbstractControlSP(title, desc, icon, parent) { + + if (value != nullptr) { + ReplaceWidget(value, new QWidget()); + value = nullptr; + } + + QLayoutItem* item; + while ((item = main_layout->takeAt(0)) != nullptr) { + if (item->widget()) { + delete item->widget(); + } + delete item; + } + + main_layout->setMargin(0); + + hlayout = new QHBoxLayout; + hlayout->setMargin(0); + hlayout->setSpacing(0); + + // title + if (!title.isEmpty()) { + title_label = new QPushButton(title); + title_label->setFixedHeight(120); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); + main_layout->addWidget(title_label, 1); + + connect(title_label, &QPushButton::clicked, [=]() { + if (!description->isVisible()) { + emit showDescriptionEvent(); + } + + if (!description->text().isEmpty()) { + description->setVisible(!description->isVisible()); + } + }); + } else { + main_layout->addSpacing(20); + } + + main_layout->addLayout(hlayout); + main_layout->addSpacing(2); + + // description + description = new QLabel(desc); + description->setContentsMargins(0, 20, 40, 20); + description->setStyleSheet("font-size: 40px; color: grey"); + description->setWordWrap(true); + description->setVisible(false); + main_layout->addWidget(description); + + main_layout->addStretch(); +} + +// controls + +ButtonControlSP::ButtonControlSP(const QString &title, const QString &text, const QString &desc, QWidget *parent) : AbstractControlSP(title, desc, "", parent) { + btn.setText(text); + btn.setStyleSheet(R"( + QPushButton { + padding: 0; + border-radius: 50px; + font-size: 35px; + font-weight: 500; + color: #E4E4E4; + background-color: #393939; + } + QPushButton:pressed { + background-color: #4a4a4a; + } + QPushButton:disabled { + color: #33E4E4E4; + } + )"); + btn.setFixedSize(250, 100); + QObject::connect(&btn, &QPushButton::clicked, this, &ButtonControlSP::clicked); + hlayout->addWidget(&btn); +} + +// ElidedLabelSP + +ElidedLabelSP::ElidedLabelSP(QWidget *parent) : ElidedLabelSP({}, parent) {} + +ElidedLabelSP::ElidedLabelSP(const QString &text, QWidget *parent) : QLabel(text.trimmed(), parent) { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + setMinimumWidth(1); +} + +void ElidedLabelSP::resizeEvent(QResizeEvent* event) { + QLabel::resizeEvent(event); + lastText_ = elidedText_ = ""; +} + +void ElidedLabelSP::paintEvent(QPaintEvent *event) { + const QString curText = text(); + if (curText != lastText_) { + elidedText_ = fontMetrics().elidedText(curText, Qt::ElideRight, contentsRect().width()); + lastText_ = curText; + } + + QPainter painter(this); + drawFrame(&painter); + QStyleOption opt; + opt.initFrom(this); + style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); +} + +// ParamControlSP + +ParamControlSP::ParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : ToggleControlSP(title, desc, icon, false, parent) { + key = param.toStdString(); + QObject::connect(this, &ParamControlSP::toggleFlipped, this, &ParamControlSP::toggleClicked); + + hlayout->removeWidget(&toggle); + hlayout->insertWidget(0, &toggle); + + hlayout->removeWidget(this->icon_label); + this->icon_pixmap = QPixmap(icon).scaledToWidth(20, Qt::SmoothTransformation); + this->icon_label->setPixmap(this->icon_pixmap); + this->icon_label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + hlayout->insertWidget(1, this->icon_label); +} + +void ParamControlSP::toggleClicked(bool state) { + auto do_confirm = [this]() { + QString content("

" + title_label->text() + "


" + "

" + getDescription() + "

"); + return ConfirmationDialog(content, tr("Enable"), tr("Cancel"), true, this).exec(); + }; + + bool confirmed = store_confirm && params.getBool(key + "Confirmed"); + if (!confirm || confirmed || !state || do_confirm()) { + if (store_confirm && state) params.putBool(key + "Confirmed", true); + params.putBool(key, state); + setIcon(state); + } else { + toggle.togglePosition(); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h new file mode 100644 index 0000000000..c7784a36c8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -0,0 +1,582 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include +#include +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/input.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/toggle.h" + +QFrame *horizontal_line(QWidget *parent = nullptr); + +class ElidedLabelSP : public QLabel { + Q_OBJECT + +public: + explicit ElidedLabelSP(QWidget *parent = 0); + explicit ElidedLabelSP(const QString &text, QWidget *parent = 0); + + void setColor(const QString &color) { + setStyleSheet("QLabel { color : " + color + "; }"); + } + +signals: + void clicked(); + +protected: + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent* event) override; + void mouseReleaseEvent(QMouseEvent *event) override { + if (rect().contains(event->pos())) { + emit clicked(); + } + } + QString lastText_, elidedText_; +}; + +class AbstractControlSP : public AbstractControl { + Q_OBJECT + +public: + void setDescription(const QString &desc) { + if (description) description->setText(desc); + } + + void setValue(const QString &val, std::optional color = std::nullopt) { + value->setText(val); + if (color.has_value()) { + value->setColor(color.value()); + } + } + + const QString getDescription() { + return description->text(); + } + + void hideDescription() { + description->setVisible(false); + } + +public slots: + void showDescription() { + description->setVisible(true); + } + +protected: + AbstractControlSP(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); + void hideEvent(QHideEvent *e) override; + + QVBoxLayout *main_layout; + ElidedLabelSP *value; + QLabel *description = nullptr; +}; + +class AbstractControlSP_SELECTOR : public AbstractControlSP { + Q_OBJECT + +protected: + AbstractControlSP_SELECTOR(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); +}; + +// widget to display a value +class LabelControlSP : public AbstractControlSP { + Q_OBJECT + +public: + LabelControlSP(const QString &title, const QString &text = "", const QString &desc = "", QWidget *parent = nullptr) : AbstractControlSP(title, desc, "", parent) { + label.setText(text); + label.setAlignment(Qt::AlignRight | Qt::AlignVCenter); + hlayout->addWidget(&label); + } + void setText(const QString &text) { label.setText(text); } + +private: + ElidedLabelSP label; +}; + +// widget for a button with a label +class ButtonControlSP : public AbstractControlSP { + Q_OBJECT + +public: + ButtonControlSP(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr); + inline void setText(const QString &text) { btn.setText(text); } + inline QString text() const { return btn.text(); } + inline void click() { btn.click(); } + +signals: + void clicked(); + +public slots: + void setEnabled(bool enabled) { btn.setEnabled(enabled); } + +private: + QPushButton btn; +}; + +class ToggleControlSP : public AbstractControlSP { + Q_OBJECT + +public: + ToggleControlSP(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControlSP(title, desc, icon, parent) { + toggle.setFixedSize(150, 100); + if (state) { + toggle.togglePosition(); + } + hlayout->addWidget(&toggle); + QObject::connect(&toggle, &ToggleSP::stateChanged, this, &ToggleControlSP::toggleFlipped); + } + + void setEnabled(bool enabled) { + toggle.setEnabled(enabled); + toggle.update(); + } + +signals: + void toggleFlipped(bool state); + +protected: + ToggleSP toggle; +}; + +// widget to toggle params +class ParamControlSP : public ToggleControlSP { + Q_OBJECT + +public: + ParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr); + void setConfirmation(bool _confirm, bool _store_confirm) { + confirm = _confirm; + store_confirm = _store_confirm; + } + + void setActiveIcon(const QString &icon) { + active_icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation); + } + + void refresh() { + bool state = params.getBool(key); + if (state != toggle.on) { + toggle.togglePosition(); + setIcon(state); + } + } + + void showEvent(QShowEvent *event) override { + refresh(); + } + + bool isToggled() { return params.getBool(key); } + +private: + void toggleClicked(bool state); + void setIcon(bool state) { + if (state && !active_icon_pixmap.isNull()) { + icon_label->setPixmap(active_icon_pixmap); + } else if (!icon_pixmap.isNull()) { + icon_label->setPixmap(icon_pixmap); + } + } + + std::string key; + Params params; + QPixmap active_icon_pixmap; + bool confirm = false; + bool store_confirm = false; +}; + +class ButtonParamControlSP : public AbstractControlSP_SELECTOR { + Q_OBJECT +public: + ButtonParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + const std::vector &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) { + const QString style = R"( + QPushButton { + border-radius: 20px; + font-size: 50px; + font-weight: 450; + height:150px; + padding: 0 25 0 25; + color: #FFFFFF; + } + QPushButton:pressed { + background-color: #4a4a4a; + } + QPushButton:checked:enabled { + background-color: #696868; + } + QPushButton:disabled { + color: #33FFFFFF; + } + QPushButton:checked:disabled { + background-color: #121212; + color: #33FFFFFF; + } + )"; + key = param.toStdString(); + int value = atoi(params.get(key).c_str()); + + button_group = new QButtonGroup(this); + button_group->setExclusive(true); + for (int i = 0; i < button_texts.size(); i++) { + QPushButton *button = new QPushButton(button_texts[i], this); + button->setCheckable(true); + button->setChecked(i == value); + button->setStyleSheet(style); + button->setMinimumWidth(minimum_button_width); + if (i == 0) hlayout->addSpacing(2); + hlayout->addWidget(button); + button_group->addButton(button, i); + } + + hlayout->setAlignment(Qt::AlignLeft); + + QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), [=](int id) { + params.put(key, std::to_string(id)); + emit buttonToggled(id); + }); + } + + void setEnabled(bool enable) { + for (auto btn : button_group->buttons()) { + btn->setEnabled(enable); + } + button_group_enabled = enable; + + update(); + } + + void setCheckedButton(int id) { + button_group->button(id)->setChecked(true); + } + + void refresh() { + int value = atoi(params.get(key).c_str()); + + if (value >= button_texts.size()) { + value = button_texts.size() - 1; + } + if (value < 0) { + value = 0; + } + + button_group->button(value)->setChecked(true); + } + + void showEvent(QShowEvent *event) override { + refresh(); + } + + void setButton(QString param) { + key = param.toStdString(); + int value = atoi(params.get(key).c_str()); + for (int i = 0; i < button_group->buttons().size(); i++) { + button_group->buttons()[i]->setChecked(i == value); + } + } + + void setDisabledSelectedButton(std::string val) { + int value = atoi(val.c_str()); + for (int i = 0; i < button_group->buttons().size(); i++) { + button_group->buttons()[i]->setEnabled(i != value); + } + } + +protected: + void paintEvent(QPaintEvent *event) override { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + // Calculate the total width and height for the background rectangle + int w = 0; + int h = 150; + + for (int i = 0; i < hlayout->count(); ++i) { + QPushButton *button = qobject_cast(hlayout->itemAt(i)->widget()); + if (button) { + w += button->width(); + } + } + + // Draw the rectangle + QRect rect(0 + 2, h - 24, w, h); + p.setPen(QPen(QColor(button_group_enabled ? "#696868" : "#121212"), 3)); + p.drawRoundedRect(rect, 20, 20); + } + +signals: + void buttonToggled(int btn_id); + +private: + std::string key; + Params params; + QButtonGroup *button_group; + std::vector button_texts; + + bool button_group_enabled = true; +}; + +class ListWidgetSP : public QWidget { + Q_OBJECT + public: + explicit ListWidgetSP(QWidget *parent = 0, const bool split_line = true) : QWidget(parent), _split_line(split_line), outer_layout(this) { + outer_layout.setMargin(0); + outer_layout.setSpacing(0); + outer_layout.addLayout(&inner_layout); + inner_layout.setMargin(0); + inner_layout.setSpacing(25); // default spacing is 25 + outer_layout.addStretch(); + } + inline void addItem(QWidget *w) { inner_layout.addWidget(w); } + inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); } + inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); } + + inline void AddWidgetAt(const int index, QWidget *new_widget) { inner_layout.insertWidget(index, new_widget); } + inline void RemoveWidgetAt(const int index) { + if (QLayoutItem* item; (item = inner_layout.takeAt(index)) != nullptr) { + if (item->widget()) delete item->widget(); + delete item; + } + } + + inline void ReplaceOrAddWidget(QWidget *old_widget, QWidget *new_widget) { + if (const int index = inner_layout.indexOf(old_widget); index != -1) { + RemoveWidgetAt(index); + AddWidgetAt(index, new_widget); + } else { + addItem(new_widget); + } + } + +private: + void paintEvent(QPaintEvent *) override { + QPainter p(this); + p.setPen(Qt::gray); + for (int i = 0; i < inner_layout.count() - 1; ++i) { + QWidget *widget = inner_layout.itemAt(i)->widget(); + if ((widget == nullptr || widget->isVisible()) && _split_line) { + QRect r = inner_layout.itemAt(i)->geometry(); + int bottom = r.bottom() + inner_layout.spacing() / 2; + p.drawLine(r.left() + 40, bottom, r.right() - 40, bottom); + } + } + } + QVBoxLayout outer_layout; + QVBoxLayout inner_layout; + + bool _split_line; +}; + +// convenience class for wrapping layouts +class LayoutWidgetSP : public QWidget { + Q_OBJECT + +public: + LayoutWidgetSP(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) { + setLayout(l); + } +}; + +class OptionControlSP : public AbstractControlSP_SELECTOR { + Q_OBJECT + +private: + struct MinMaxValue { + int min_value; + int max_value; + }; + +public: + OptionControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + const MinMaxValue &range, const int per_value_change = 1) : _title(title), AbstractControlSP_SELECTOR(title, desc, icon) { + const QString style = R"( + QPushButton { + border-radius: 20px; + font-size: 60px; + font-weight: 500; + width: 150px; + height: 150px; + padding: -3 25 3 25; + color: #FFFFFF; + font-weight: bold; + } + QPushButton:pressed { + color: #5C5C5C; + } + QPushButton:disabled { + color: #5C5C5C; + } + )"; + + label.setStyleSheet(label_enabled_style); + label.setFixedWidth(300); + label.setAlignment(Qt::AlignCenter); + + const std::vector button_texts{"-", "+"}; + + key = param.toStdString(); + value = atoi(params.get(key).c_str()); + + button_group = new QButtonGroup(this); + button_group->setExclusive(true); + for (int i = 0; i < button_texts.size(); i++) { + QPushButton *button = new QPushButton(button_texts[i], this); + button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" : + "QPushButton { text-align: right; }")); + hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); + if (i == 0) { + hlayout->addWidget(&label, 0, Qt::AlignCenter); + } + button_group->addButton(button, i); + + QObject::connect(button, &QPushButton::clicked, [=]() { + int change_value = (i == 0) ? -per_value_change : per_value_change; + key = param.toStdString(); + value = atoi(params.get(key).c_str()); + value += change_value; + value = std::clamp(value, range.min_value, range.max_value); + params.put(key, QString::number(value).toStdString()); + + button_group->button(0)->setEnabled(!(value <= range.min_value)); + button_group->button(1)->setEnabled(!(value >= range.max_value)); + + updateLabels(); + + if (request_update) { + emit updateOtherToggles(); + } + }); + } + + hlayout->setAlignment(Qt::AlignLeft); + } + + void setUpdateOtherToggles(bool _update) { + request_update = _update; + } + + inline void setLabel(const QString &text) { label.setText(text); } + + void setEnabled(bool enabled) { + for (auto btn : button_group->buttons()) { + btn->setEnabled(enabled); + } + label.setEnabled(enabled); + label.setStyleSheet(enabled ? label_enabled_style : label_disabled_style); + button_enabled = enabled; + + update(); + } + +protected: + void paintEvent(QPaintEvent *event) override { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + // Calculate the total width and height for the background rectangle + int w = 0; + int h = 150; + + for (int i = 0; i < hlayout->count(); ++i) { + QWidget *widget = qobject_cast(hlayout->itemAt(i)->widget()); + if (widget) { + w += widget->width(); + } + } + + // Draw the rectangle + QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h); + p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color + p.setPen(QPen(Qt::NoPen)); + p.drawRoundedRect(rect, 20, 20); + } + +signals: + void updateLabels(); + void updateOtherToggles(); + +private: + std::string key; + int value; + QButtonGroup *button_group; + QLabel label; + Params params; + std::map option_label = {}; + bool request_update = false; + QString _title = ""; + + const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;"; + const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;"; + + bool button_enabled = true; +}; + +class SubPanelButton : public QPushButton { + Q_OBJECT + +public: + SubPanelButton(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) { + const QString buttonStyle = R"( + QPushButton { + border-radius: 20px; + font-size: 50px; + font-weight: 450; + height: 150px; + padding: 0 25px 0 25px; + color: #FFFFFF; + } + QPushButton:enabled { + background-color: #393939; + } + QPushButton:pressed { + background-color: #4A4A4A; + } + QPushButton:disabled { + background-color: #121212; + color: #5C5C5C; + } + )"; + + setStyleSheet(buttonStyle); + setFixedWidth(minimum_button_width); + } +}; + +class PanelBackButton : public QPushButton { + Q_OBJECT + +public: + PanelBackButton(const QString &label = "Back", QWidget *parent = nullptr) : QPushButton(label, parent) { + setObjectName("back_btn"); + setFixedSize(400, 100); + } +}; diff --git a/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc b/selfdrive/ui/sunnypilot/qt/widgets/drive_stats.cc similarity index 73% rename from selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc rename to selfdrive/ui/sunnypilot/qt/widgets/drive_stats.cc index e4c127e6b5..8d54ec4f6c 100644 --- a/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc +++ b/selfdrive/ui/sunnypilot/qt/widgets/drive_stats.cc @@ -1,8 +1,33 @@ -#include "selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h" +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/widgets/drive_stats.h" #include #include -#include #include #include "common/params.h" diff --git a/selfdrive/ui/sunnypilot/qt/widgets/drive_stats.h b/selfdrive/ui/sunnypilot/qt/widgets/drive_stats.h new file mode 100644 index 0000000000..7bd1d39cfe --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/drive_stats.h @@ -0,0 +1,51 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include + +class DriveStats : public QFrame { + Q_OBJECT + +public: + explicit DriveStats(QWidget* parent = 0); + +private: + void showEvent(QShowEvent *event) override; + void updateStats(); + inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); } + + bool metric_; + QJsonDocument stats_; + struct StatsLabels { + QLabel *routes, *distance, *distance_unit, *hours; + } all_, week_; + +private slots: + void parseResponse(const QString &response, bool success); +}; diff --git a/selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc new file mode 100644 index 0000000000..5b87a9f6dd --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc @@ -0,0 +1,37 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +#include + +void ScrollViewSP::setLastScrollPosition() { + lastScrollPosition = verticalScrollBar()->value(); +} + +void ScrollViewSP::restoreScrollPosition() { + verticalScrollBar()->setValue(lastScrollPosition); +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/scrollview.h b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.h new file mode 100644 index 0000000000..37d3ae4457 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.h @@ -0,0 +1,43 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class ScrollViewSP : public ScrollView { + Q_OBJECT + +public: + explicit ScrollViewSP(QWidget *w = nullptr, QWidget *parent = nullptr) : ScrollView(w, parent) {} + +public slots: + void setLastScrollPosition(); + void restoreScrollPosition(); + +private: + int lastScrollPosition = 0; +}; diff --git a/selfdrive/ui/sunnypilot/qt/widgets/toggle.cc b/selfdrive/ui/sunnypilot/qt/widgets/toggle.cc new file mode 100644 index 0000000000..b6737babd2 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/toggle.cc @@ -0,0 +1,49 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/qt/widgets/toggle.h" + +#include + +ToggleSP::ToggleSP(QWidget *parent) : Toggle(parent) { + _height_rect = 80; +} + +void ToggleSP::paintEvent(QPaintEvent *e) { + this->setFixedHeight(100); + QPainter p(this); + p.setPen(Qt::NoPen); + p.setRenderHint(QPainter::Antialiasing, true); + + // Draw toggle background + enabled ? green.setRgb(0x1e79e8) : green.setRgb(0x125db8); + p.setBrush(on ? green : QColor(0x292929)); + p.drawRoundedRect(QRect(0, 10, width(), _height_rect),_height_rect/2, _height_rect/2); + + // Draw toggle circle + p.setBrush(circleColor); + p.drawEllipse(QRectF(_x_circle - _radius + 6, 16, 68, 68)); +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/toggle.h b/selfdrive/ui/sunnypilot/qt/widgets/toggle.h new file mode 100644 index 0000000000..34f41a0c29 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/toggle.h @@ -0,0 +1,39 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/widgets/toggle.h" + +class ToggleSP : public Toggle { + Q_OBJECT + +public: + explicit ToggleSP(QWidget* parent = nullptr); + +protected: + void paintEvent(QPaintEvent*) override; +}; diff --git a/selfdrive/ui/sunnypilot/qt/window.cc b/selfdrive/ui/sunnypilot/qt/window.cc new file mode 100644 index 0000000000..43fe9a9f25 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/window.cc @@ -0,0 +1,44 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "window.h" + +MainWindowSP::MainWindowSP(QWidget *parent) : MainWindow(parent, new HomeWindowSP(parent), + new SettingsWindowSP(parent), new OnboardingWindowSP(parent)) { + homeWindow = dynamic_cast(MainWindow::homeWindow); + settingsWindow = dynamic_cast(MainWindow::settingsWindow); + onboardingWindow = dynamic_cast(MainWindow::onboardingWindow); +} + +void MainWindowSP::closeSettings() { + MainWindow::closeSettings(); + if (uiState()->scene.started) { + homeWindow->showSidebar(false); + if (uiStateSP()->scene.navigate_on_openpilot_deprecated) { + homeWindow->showMapPanel(true); + } + } +} diff --git a/selfdrive/ui/sunnypilot/qt/window.h b/selfdrive/ui/sunnypilot/qt/window.h new file mode 100644 index 0000000000..1c0e6a81cb --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/window.h @@ -0,0 +1,45 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/qt/window.h" +#include "selfdrive/ui/sunnypilot/qt/home.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "offroad/settings/onboarding.h" + +class MainWindowSP : public MainWindow { + Q_OBJECT + +public: + explicit MainWindowSP(QWidget *parent = 0); + +private: + HomeWindowSP *homeWindow; + SettingsWindowSP *settingsWindow; + OnboardingWindowSP *onboardingWindow; + void closeSettings() override; +}; diff --git a/selfdrive/ui/sunnypilot/sunnypilot_main.h b/selfdrive/ui/sunnypilot/sunnypilot_main.h new file mode 100644 index 0000000000..a1732da764 --- /dev/null +++ b/selfdrive/ui/sunnypilot/sunnypilot_main.h @@ -0,0 +1,45 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/display_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/monitoring_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_settings.h" + +inline void ReplaceWidget(QWidget* old_widget, QWidget* new_widget) { + if (old_widget && old_widget->parentWidget() && old_widget->parentWidget()->layout()) { + old_widget->parentWidget()->layout()->replaceWidget(old_widget, new_widget); + old_widget->hide(); + old_widget->deleteLater(); + } +} diff --git a/selfdrive/ui/sunnypilot/ui.cc b/selfdrive/ui/sunnypilot/ui.cc new file mode 100644 index 0000000000..ce490431d1 --- /dev/null +++ b/selfdrive/ui/sunnypilot/ui.cc @@ -0,0 +1,348 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#include "selfdrive/ui/sunnypilot/ui.h" + +#include +#include +#include + +#include + +#include "common/transformations/orientation.hpp" +#include "common/params.h" +#include "common/util.h" +#include "common/watchdog.h" +#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h" +#include "system/hardware/hw.h" + +#define BACKLIGHT_DT 0.05 +#define BACKLIGHT_TS 10.00 + +// Projects a point in car to space to the corresponding point in full frame +// image space. +static bool sp_calib_frame_to_full_frame(const UIStateSP *s, float in_x, float in_y, float in_z, QPointF *out) { + return calib_frame_to_full_frame(s, in_x, in_y, in_z, out, 1000.0f); +} + +//todo: revisit, we could reuse from OG update_model. But this is an overload in this case. +void update_line_data(const UIStateSP *s, const cereal::XYZTData::Reader &line, + float y_off, float z_off_left, float z_off_right, QPolygonF *pvd, int max_idx, bool allow_invert=true) { + const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); + QPointF left, right; + pvd->clear(); + for (int i = 0; i <= max_idx; i++) { + // highly negative x positions are drawn above the frame and cause flickering, clip to zy plane of camera + if (line_x[i] < 0) continue; + + bool l = sp_calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off_left, &left); + bool r = sp_calib_frame_to_full_frame(s, line_x[i], line_y[i] + y_off, line_z[i] + z_off_right, &right); + if (l && r) { + // For wider lines the drawn polygon will "invert" when going over a hill and cause artifacts + if (!allow_invert && pvd->size() && left.y() > pvd->back().y()) { + continue; + } + pvd->push_back(left); + pvd->push_front(right); + } + } +} + +//todo: revisit, we could reuse from OG update_model +void sp_update_model(UIStateSP *s, const cereal::ModelDataV2::Reader &model) { + UISceneSP &scene = s->scene; + auto model_position = model.getPosition(); + float max_distance = std::clamp(*(model_position.getX().end() - 1), + MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); + + // update lane lines + const auto lane_lines = model.getLaneLines(); + const auto lane_line_probs = model.getLaneLineProbs(); + int max_idx = get_path_length_idx(lane_lines[0], max_distance); + for (int i = 0; i < std::size(scene.lane_line_vertices); i++) { + scene.lane_line_probs[i] = lane_line_probs[i]; + update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, 0, &scene.lane_line_vertices[i], max_idx); + } + + // lane barriers for blind spot + int max_distance_barrier = 40; + int max_idx_barrier = std::min(max_idx, get_path_length_idx(lane_lines[0], max_distance_barrier)); + update_line_data(s, lane_lines[1], 0, -0.05, -0.6, &scene.lane_barrier_vertices[0], max_idx_barrier, false); + update_line_data(s, lane_lines[2], 0, -0.05, -0.6, &scene.lane_barrier_vertices[1], max_idx_barrier, false); + + // update road edges + const auto road_edges = model.getRoadEdges(); + const auto road_edge_stds = model.getRoadEdgeStds(); + for (int i = 0; i < std::size(scene.road_edge_vertices); i++) { + scene.road_edge_stds[i] = road_edge_stds[i]; + update_line_data(s, road_edges[i], 0.025, 0, 0, &scene.road_edge_vertices[i], max_idx); + } + + // update path + auto lead_one = (*s->sm)["radarState"].getRadarState().getLeadOne(); + if (lead_one.getStatus()) { + const float lead_d = lead_one.getDRel() * 2.; + max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance); + } + max_idx = get_path_length_idx(model_position, max_distance); + update_line_data(s, model_position, 0.9 - scene.mads_path_range * scene.mads_path_scale, 1.22, 1.22, &scene.track_vertices, max_idx, false); + update_line_data(s, model_position, 1.0 - scene.mads_path_range * scene.mads_path_scale - 0.1 * scene.mads_path_scale, 1.22, 1.22, &scene.track_edge_vertices, max_idx, false); +} + +static void sp_update_state(UIStateSP *s) { + update_state(s); + SubMaster &sm = *(s->sm); + UISceneSP &scene = s->scene; + auto params = Params(); + scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition && !params.getBool("ForceOffroad"); + // TODO: SP - Set this dynamically on init with manual toggle or driving model selection + if (sm.updated("lateralPlanSPDEPRECATED")) { + scene.dynamic_lane_profile_status = sm["lateralPlanSPDEPRECATED"].getLateralPlanSPDEPRECATED().getDynamicLaneProfileStatus(); + } + if (sm.updated("controlsState")) { + scene.controlsState = sm["controlsState"].getControlsState(); + } + if (sm.updated("longitudinalPlanSP")) { + for (int i = 0; i < std::size(scene.e2eX); i++) { + scene.e2eX[i] = sm["longitudinalPlanSP"].getLongitudinalPlanSP().getE2eX()[i]; + } + } + if (sm.updated("modelV2SP")) { + auto model_v2_sp = sm["modelV2SP"].getModelV2SP(); + scene.custom_driving_model_valid = model_v2_sp.getCustomModel(); + scene.driving_model_generation = model_v2_sp.getModelGeneration(); + scene.driving_model_capabilities = model_v2_sp.getModelCapabilities(); + } +} + +void sp_ui_update_params(UIStateSP *s) { + ui_update_params(s); + auto params = Params(); + s->scene.map_on_left = params.getBool("NavSettingLeftSide"); + + s->scene.visual_brake_lights = params.getBool("BrakeLights"); + s->scene.onroadScreenOff = std::atoi(params.get("OnroadScreenOff").c_str()); + s->scene.onroadScreenOffBrightness = std::atoi(params.get("OnroadScreenOffBrightness").c_str()); + s->scene.onroadScreenOffEvent = params.getBool("OnroadScreenOffEvent"); + s->scene.stand_still_timer = params.getBool("StandStillTimer"); + s->scene.show_debug_ui = params.getBool("ShowDebugUI"); + s->scene.hide_vego_ui = params.getBool("HideVEgoUi"); + s->scene.true_vego_ui = params.getBool("TrueVEgoUi"); + s->scene.chevron_data = std::atoi(params.get("ChevronInfo").c_str()); + s->scene.dev_ui_info = std::atoi(params.get("DevUIInfo").c_str()); + s->scene.button_auto_hide = params.getBool("ButtonAutoHide"); + s->scene.reverse_dm_cam = params.getBool("ReverseDmCam"); + s->scene.e2e_long_alert_light = params.getBool("EndToEndLongAlertLight"); + s->scene.e2e_long_alert_lead = params.getBool("EndToEndLongAlertLead"); + s->scene.e2e_long_alert_ui = params.getBool("EndToEndLongAlertUI"); + s->scene.map_3d_buildings = params.getBool("Map3DBuildings"); + s->scene.live_torque_toggle = params.getBool("LiveTorque"); + s->scene.torqued_override = params.getBool("TorquedOverride"); + s->scene.speed_limit_control_engage_type = std::atoi(params.get("SpeedLimitEngageType").c_str()); + s->scene.mapbox_fullscreen = params.getBool("MapboxFullScreen"); + s->scene.speed_limit_warning_flash = params.getBool("SpeedLimitWarningFlash"); + s->scene.speed_limit_warning_type = std::atoi(params.get("SpeedLimitWarningType").c_str()); + s->scene.speed_limit_warning_value_offset = std::atoi(params.get("SpeedLimitWarningValueOffset").c_str()); + s->scene.speed_limit_control_enabled = params.getBool("EnableSlc"); + s->scene.feature_status_toggle = params.getBool("FeatureStatus"); + s->scene.onroad_settings_toggle = params.getBool("OnroadSettings"); + + // Handle Onroad Screen Off params + if (s->scene.onroadScreenOff > 0) { + s->scene.osoTimer = s->scene.onroadScreenOff * 60 * UI_FREQ; + } else if (s->scene.onroadScreenOff == 0) { + s->scene.osoTimer = 30 * UI_FREQ; + } else if (s->scene.onroadScreenOff == -1) { + s->scene.osoTimer = 15 * UI_FREQ; + } else { + s->scene.osoTimer = -1; + } +} + +void UIStateSP::updateStatus() { + UIState::updateStatus(); + auto params = Params(); + if (scene.started && sm->updated("controlsState")) { + auto car_control = (*sm)["carControl"].getCarControl(); + auto car_state = (*sm)["carState"].getCarState(); + auto mads_enabled = car_state.getMadsEnabled(); + if (status != STATUS_OVERRIDE) { + status = mads_enabled && car_control.getLongActive() ? STATUS_ENGAGED : mads_enabled ? STATUS_MADS : STATUS_DISENGAGED; + } + + if (mads_enabled != last_mads_enabled) { + mads_path_state = true; + } + last_mads_enabled = mads_enabled; + if (mads_path_state) { + if (mads_enabled) { + mads_path_count = fmax(mads_path_count - 1, 0); + if (mads_path_count == 0) { + mads_path_state = false; + } + } else { + mads_path_count = fmin(mads_path_count + 1, mads_path_timestep); + if (mads_path_count == mads_path_timestep) { + mads_path_state = false; + } + } + } + scene.mads_path_scale = mads_path_count * (1 / mads_path_timestep); + } + + if (scene.started) { + // Auto hide UI button state machine + { + if (scene.button_auto_hide) { + if (scene.touch_to_wake) { + scene.sleep_btn = 30 * UI_FREQ; + } else if (scene.sleep_btn > 0) { + scene.sleep_btn--; + } else if (scene.sleep_btn == -1) { + scene.sleep_btn = 30 * UI_FREQ; + } + // Check if the sleep button should be fading in + if (scene.sleep_btn_fading_in) { + // Increase the opacity of the sleep button by a small amount + if (scene.sleep_btn_opacity < 20) { + scene.sleep_btn_opacity+= 10; + } + if (scene.sleep_btn_opacity >= 20) { + // If the opacity has reached its maximum value, stop fading in + scene.sleep_btn_fading_in = false; + scene.sleep_btn_opacity = 20; + } + } else if (scene.sleep_btn == 0) { + // Fade out the sleep button as before + if (scene.sleep_btn_opacity > 0) { + scene.sleep_btn_opacity-= 2; + } + } else { + // Set the opacity of the sleep button to its maximum value + scene.sleep_btn_opacity = 20; + } + } else { + scene.sleep_btn_opacity = 20; + } + } + + // Onroad Screen Off Brightness + Timer + Global Brightness + { + if (scene.onroadScreenOff != -2 && scene.touched2) { + scene.sleep_time = scene.osoTimer; + } else if (scene.onroadScreenOff != -2 && + ((scene.controlsState.getAlertSize() != cereal::ControlsState::AlertSize::NONE) && + ((scene.controlsState.getAlertStatus() == cereal::ControlsState::AlertStatus::NORMAL && scene.onroadScreenOffEvent) || + (scene.controlsState.getAlertStatus() != cereal::ControlsState::AlertStatus::NORMAL)))) { + scene.sleep_time = scene.osoTimer; + } else if (scene.sleep_time > 0 && scene.onroadScreenOff != -2) { + scene.sleep_time--; + } else if (scene.sleep_time == -1 && scene.onroadScreenOff != -2) { + scene.sleep_time = scene.osoTimer; + } + } + } + + if (sm->frame % UI_FREQ == 0) { // Update every 1 Hz + scene.sidebar_temp_options = std::atoi(params.get("SidebarTemperatureOptions").c_str()); + } + + scene.brightness = std::atoi(params.get("BrightnessControl").c_str()); +} + +UIStateSP::UIStateSP(QObject *parent) : UIState(parent) { +// todo: Can we simply append to SM? + sm = std::make_unique>({ + "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", + "pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2", + "wideRoadCameraState", "managerState", "navInstruction", "navRoute", "clocks", "longitudinalPlanSP", "liveMapDataSP", + "carControl", "lateralPlanSPDEPRECATED", "gpsLocation", "gpsLocationExternal", "liveParameters", "liveTorqueParameters", + "controlsStateSP", "modelV2SP" + }); + + // update timer + timer = new QTimer(this); + QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update); + timer->start(1000 / UI_FREQ); +} + +// Note: This method overrides completely the update method from the parent class intentionally. +void UIStateSP::update() { + update_sockets(this); + sp_update_state(this); + updateStatus(); + + if (sm->frame % UI_FREQ == 0) { + watchdog_kick(nanos_since_boot()); + } + emit uiUpdate(*this); +} + + +void UIStateSP::setSunnylinkRoles(const std::vector& roles) { + sunnylinkRoles = roles; + emit sunnylinkRolesChanged(roles); +} + +void UIStateSP::setSunnylinkDeviceUsers(const std::vector& users) { + sunnylinkUsers = users; + emit sunnylinkDeviceUsersChanged(users); +} + +DeviceSP::DeviceSP(QObject *parent) : Device(parent){ + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update); +} + +//todo: revisit this +void DeviceSP::updateBrightness(const UIStateSP &s) { + Device::updateBrightness(s); + int brightness = brightness_filter.update(clipped_brightness); + if (!awake) { + brightness = 0; + } else if (s.scene.started && s.scene.sleep_time == 0 && s.scene.onroadScreenOff != -2) { + brightness = s.scene.onroadScreenOffBrightness * 0.01 * brightness; + } else if (s.scene.brightness) { + brightness = s.scene.brightness * 0.99; + } + + if (brightness != last_brightness) { + if (!brightness_future.isRunning()) { + brightness_future = QtConcurrent::run(Hardware::set_brightness, brightness); + last_brightness = brightness; + } + } +} + +UIStateSP *uiStateSP() { + static UIStateSP ui_state; + return &ui_state; +} +UIStateSP *uiState() { return uiStateSP(); } + +DeviceSP *deviceSP() { + static DeviceSP _device; + return &_device; +} diff --git a/selfdrive/ui/sunnypilot/ui.h b/selfdrive/ui/sunnypilot/ui.h new file mode 100644 index 0000000000..434b334f75 --- /dev/null +++ b/selfdrive/ui/sunnypilot/ui.h @@ -0,0 +1,154 @@ +/** +The MIT License + +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Last updated: July 29, 2024 +***/ + +#pragma once + +#include +#include +#include + +#include "selfdrive/ui/ui.h" + +#include "cereal/messaging/messaging.h" +#include "qt/network/sunnylink/models/role_model.h" +#include "qt/network/sunnylink/models/sponsor_role_model.h" +#include "qt/network/sunnylink/models/user_model.h" +#include "system/hardware/hw.h" + +const int UI_ROAD_NAME_MARGIN_X = 14; + +struct FeatureStatusText { + const QStringList dlp_list_text = { "Laneful", "Laneless", "Auto"}; + const QStringList gac_list_text = { "Aggressive", "Moderate", "Standard", "Relaxed"}; + const QStringList slc_list_text = { "Inactive", "Temp Off", "Adapting", "Active", "Pre Active"}; +}; + +struct FeatureStatusColor { + const QStringList dlp_list_color = { "#2020f8", "#0df87a", "#0df8f8" }; + const QStringList gac_list_color = { "#ff4b4b", "#fcff4b", "#4bff66", "#6a0ac9" }; + const QStringList slc_list_color = { "#ffffff", "#ffffff", "#fcff4b", "#4bff66", "#fcff4b" }; +}; + + +const QColor sp_bg_colors [] = { + [STATUS_DISENGAGED] = bg_colors[STATUS_DISENGAGED], + [STATUS_OVERRIDE] = bg_colors[STATUS_OVERRIDE], + [STATUS_ENGAGED] = QColor(0x00, 0xc8, 0x00, 0xf1), + [STATUS_MADS] = QColor(0x00, 0xc8, 0xc8, 0xf1), +}; +#define bg_colors sp_bg_colors // Override the bg_colors array with the sp_bg_colors array + +const QColor tcs_colors [] = { + [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::DISABLED)] = QColor(0x0, 0x0, 0x0, 0xff), + [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::ENTERING)] = QColor(0xC9, 0x22, 0x31, 0xf1), + [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::TURNING)] = QColor(0xDA, 0x6F, 0x25, 0xf1), + [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::LEAVING)] = QColor(0x17, 0x86, 0x44, 0xf1), +}; + +class UIStateSP : public UIState { + Q_OBJECT + +public: + UIStateSP(QObject* parent = 0); + void updateStatus() override; + + void setSunnylinkRoles(const std::vector &roles); + void setSunnylinkDeviceUsers(const std::vector &users); + + inline std::vector sunnylinkDeviceRoles() const { return sunnylinkRoles; } + inline bool isSunnylinkAdmin() const { + return std::any_of(sunnylinkRoles.begin(), sunnylinkRoles.end(), [](const RoleModel &role) { + return role.roleType == RoleType::Admin; + }); + } + inline bool isSunnylinkSponsor() const { + return std::any_of(sunnylinkRoles.begin(), sunnylinkRoles.end(), [](const RoleModel &role) { + return role.roleType == RoleType::Sponsor && role.as().roleTier != SponsorTier::Free; + }); + } + inline SponsorRoleModel sunnylinkSponsorRole() const { + std::optional sponsorRoleWithHighestTier = std::nullopt; + for (const auto &role : sunnylinkRoles) { + if(role.roleType != RoleType::Sponsor) + continue; + + if (auto sponsorRole = role.as(); !sponsorRoleWithHighestTier.has_value() || sponsorRoleWithHighestTier->roleTier < sponsorRole.roleTier) { + sponsorRoleWithHighestTier = sponsorRole; + } + } + return sponsorRoleWithHighestTier.value_or(SponsorRoleModel(RoleType::Sponsor, SponsorTier::Free)); + } + inline SponsorTier sunnylinkSponsorTier() const { + return sunnylinkSponsorRole().roleTier; + } + inline std::vector sunnylinkDeviceUsers() const { return sunnylinkUsers; } + inline bool isSunnylinkPaired() const { + return std::any_of(sunnylinkUsers.begin(), sunnylinkUsers.end(), [](const UserModel &user) { + return user.user_id.toLower() != "unregisteredsponsor" && user.user_id.toLower() != "temporarysponsor"; + }); + } + +signals: + void sunnylinkRoleChanged(bool subscriber); + void sunnylinkRolesChanged(std::vector roles); + void sunnylinkDeviceUsersChanged(std::vector users); + void uiUpdate(const UIStateSP &s); + +private slots: + void update() override; + + +private: + std::vector sunnylinkRoles = {}; + std::vector sunnylinkUsers = {}; + + bool last_mads_enabled = false; + bool mads_path_state = false; + float mads_path_timestep = 4; // UI runs at 20 Hz, therefore 0.2 second is [0.2 second / (1 / 20 Hz) = 4] + float mads_path_count = 4; // UI runs at 20 Hz, therefore 0.2 second is [0.2 second / (1 / 20 Hz) = 4] +}; + +UIStateSP *uiStateSP(); +UIStateSP *uiState(); + +// device management class +class DeviceSP : public Device { + Q_OBJECT + +public: + DeviceSP(QObject *parent = 0); +protected: + void updateBrightness(const UIStateSP &s); + void updateBrightness(const UIState &s) override { updateBrightness(dynamic_cast(s)); } +}; + +DeviceSP *deviceSP(); +inline DeviceSP *device() { return deviceSP(); } + +void sp_update_model(UIStateSP *s, const cereal::ModelDataV2::Reader &model); +void sp_ui_update_params(UIStateSP *s); +void update_line_data(const UIStateSP *s, const cereal::XYZTData::Reader &line, + float y_off, float z_off_left, float z_off_right, QPolygonF *pvd, int max_idx, bool allow_invert); diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 7dae31a53e..28610a671e 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -1517,6 +1517,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 6b8fd1efc1..ab80c261fd 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -1499,6 +1499,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 6527ddfc12..90fc8381c9 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -1505,6 +1505,22 @@ Esto puede tardar hasta un minuto. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index 0071a33e47..22b5808a62 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -1501,6 +1501,22 @@ Cela peut prendre jusqu'à une minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 5db645c9f4..0ffade1416 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -1495,6 +1495,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 1a55d59a3b..337f338633 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -1497,6 +1497,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 856ef26ca8..63d83d7448 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -1501,6 +1501,22 @@ Isso pode levar até um minuto. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 220b6575c4..a0ed3caf37 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -1497,6 +1497,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index a8c3992fd2..07205a2b12 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -1495,6 +1495,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index fddf413a50..da8e79536c 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -1497,6 +1497,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 684447b113..6c93b6239a 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -1497,6 +1497,22 @@ This may take up to a minute. Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation. + + Enable Automatic Brake Hold (AHB) + + + + WARNING: Only for Toyota/Lexus vehicles with TSS2/LSS2. USE AT YOUR OWN RISK. + + + + When you stop the vehicle completely by depressing the brake pedal, sunnypilot will activate Auto Brake Hold. + + + + Changing this setting takes effect when the car is powered off. + + Enable Enhanced Blind Spot Monitor diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 126be0d6b1..5d6b6c5722 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -11,7 +11,7 @@ #include "common/swaglog.h" #include "common/util.h" #include "common/watchdog.h" -#include "qt/network/sunnylink/models/role_model.h" +#include "qt/util.h" #include "system/hardware/hw.h" #define BACKLIGHT_DT 0.05 @@ -19,8 +19,7 @@ // Projects a point in car to space to the corresponding point in full frame // image space. -static bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, float in_z, QPointF *out) { - const float margin = 1000.0f; +bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, float in_z, QPointF *out, const float margin) { const QRectF clip_region{-margin, -margin, s->fb_w + 2 * margin, s->fb_h + 2 * margin}; const vec3 pt = (vec3){{in_x, in_y, in_z}}; @@ -56,7 +55,7 @@ void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, con } void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, - float y_off, float z_off_left, float z_off_right, QPolygonF *pvd, int max_idx, bool allow_invert=true) { + float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert=true) { const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); QPointF left, right; pvd->clear(); @@ -64,8 +63,8 @@ void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, // highly negative x positions are drawn above the frame and cause flickering, clip to zy plane of camera if (line_x[i] < 0) continue; - bool l = calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off_left, &left); - bool r = calib_frame_to_full_frame(s, line_x[i], line_y[i] + y_off, line_z[i] + z_off_right, &right); + bool l = calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off, &left); + bool r = calib_frame_to_full_frame(s, line_x[i], line_y[i] + y_off, line_z[i] + z_off, &right); if (l && r) { // For wider lines the drawn polygon will "invert" when going over a hill and cause artifacts if (!allow_invert && pvd->size() && left.y() > pvd->back().y()) { @@ -90,21 +89,15 @@ void update_model(UIState *s, int max_idx = get_path_length_idx(lane_lines[0], max_distance); for (int i = 0; i < std::size(scene.lane_line_vertices); i++) { scene.lane_line_probs[i] = lane_line_probs[i]; - update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, 0, &scene.lane_line_vertices[i], max_idx); + update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx); } - // lane barriers for blind spot - int max_distance_barrier = 40; - int max_idx_barrier = std::min(max_idx, get_path_length_idx(lane_lines[0], max_distance_barrier)); - update_line_data(s, lane_lines[1], 0, -0.05, -0.6, &scene.lane_barrier_vertices[0], max_idx_barrier, false); - update_line_data(s, lane_lines[2], 0, -0.05, -0.6, &scene.lane_barrier_vertices[1], max_idx_barrier, false); - // update road edges const auto road_edges = model.getRoadEdges(); const auto road_edge_stds = model.getRoadEdgeStds(); for (int i = 0; i < std::size(scene.road_edge_vertices); i++) { scene.road_edge_stds[i] = road_edge_stds[i]; - update_line_data(s, road_edges[i], 0.025, 0, 0, &scene.road_edge_vertices[i], max_idx); + update_line_data(s, road_edges[i], 0.025, 0, &scene.road_edge_vertices[i], max_idx); } // update path @@ -114,8 +107,7 @@ void update_model(UIState *s, max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance); } max_idx = get_path_length_idx(model_position, max_distance); - update_line_data(s, model_position, 0.9 - scene.mads_path_range * scene.mads_path_scale, 1.22, 1.22, &scene.track_vertices, max_idx, false); - update_line_data(s, model_position, 1.0 - scene.mads_path_range * scene.mads_path_scale - 0.1 * scene.mads_path_scale, 1.22, 1.22, &scene.track_edge_vertices, max_idx, false); + update_line_data(s, model_position, 0.9, 1.22, &scene.track_vertices, max_idx, false); } void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd) { @@ -153,14 +145,13 @@ void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &drivers } } -static void update_sockets(UIState *s) { +void update_sockets(UIState *s) { s->sm->update(0); } -static void update_state(UIState *s) { +void update_state(UIState *s) { SubMaster &sm = *(s->sm); UIScene &scene = s->scene; - auto params = Params(); if (sm.updated("liveCalibration")) { auto live_calib = sm["liveCalibration"].getLiveCalibration(); @@ -212,108 +203,28 @@ static void update_state(UIState *s) { } else if (!sm.allAliveAndValid({"wideRoadCameraState"})) { scene.light_sensor = -1; } - scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition && !params.getBool("ForceOffroad"); + scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition; scene.world_objects_visible = scene.world_objects_visible || (scene.started && sm.rcv_frame("liveCalibration") > scene.started_frame && sm.rcv_frame("modelV2") > scene.started_frame); - // TODO: SP - Set this dynamically on init with manual toggle or driving model selection - if (sm.updated("lateralPlanSPDEPRECATED")) { - scene.dynamic_lane_profile_status = sm["lateralPlanSPDEPRECATED"].getLateralPlanSPDEPRECATED().getDynamicLaneProfileStatus(); - } - if (sm.updated("controlsState")) { - scene.controlsState = sm["controlsState"].getControlsState(); - } - if (sm.updated("longitudinalPlanSP")) { - for (int i = 0; i < std::size(scene.e2eX); i++) { - scene.e2eX[i] = sm["longitudinalPlanSP"].getLongitudinalPlanSP().getE2eX()[i]; - } - } - if (sm.updated("modelV2SP")) { - auto model_v2_sp = sm["modelV2SP"].getModelV2SP(); - scene.custom_driving_model_valid = model_v2_sp.getCustomModel(); - scene.driving_model_generation = model_v2_sp.getModelGeneration(); - scene.driving_model_capabilities = model_v2_sp.getModelCapabilities(); - } } void ui_update_params(UIState *s) { auto params = Params(); s->scene.is_metric = params.getBool("IsMetric"); - s->scene.map_on_left = params.getBool("NavSettingLeftSide"); - - s->scene.visual_brake_lights = params.getBool("BrakeLights"); - s->scene.onroadScreenOff = std::atoi(params.get("OnroadScreenOff").c_str()); - s->scene.onroadScreenOffBrightness = std::atoi(params.get("OnroadScreenOffBrightness").c_str()); - s->scene.onroadScreenOffEvent = params.getBool("OnroadScreenOffEvent"); - s->scene.stand_still_timer = params.getBool("StandStillTimer"); - s->scene.show_debug_ui = params.getBool("ShowDebugUI"); - s->scene.hide_vego_ui = params.getBool("HideVEgoUi"); - s->scene.true_vego_ui = params.getBool("TrueVEgoUi"); - s->scene.chevron_data = std::atoi(params.get("ChevronInfo").c_str()); - s->scene.dev_ui_info = std::atoi(params.get("DevUIInfo").c_str()); - s->scene.button_auto_hide = params.getBool("ButtonAutoHide"); - s->scene.reverse_dm_cam = params.getBool("ReverseDmCam"); - s->scene.e2e_long_alert_light = params.getBool("EndToEndLongAlertLight"); - s->scene.e2e_long_alert_lead = params.getBool("EndToEndLongAlertLead"); - s->scene.e2e_long_alert_ui = params.getBool("EndToEndLongAlertUI"); - s->scene.map_3d_buildings = params.getBool("Map3DBuildings"); - s->scene.live_torque_toggle = params.getBool("LiveTorque"); - s->scene.torqued_override = params.getBool("TorquedOverride"); - s->scene.speed_limit_control_engage_type = std::atoi(params.get("SpeedLimitEngageType").c_str()); - s->scene.mapbox_fullscreen = params.getBool("MapboxFullScreen"); - s->scene.speed_limit_warning_flash = params.getBool("SpeedLimitWarningFlash"); - s->scene.speed_limit_warning_type = std::atoi(params.get("SpeedLimitWarningType").c_str()); - s->scene.speed_limit_warning_value_offset = std::atoi(params.get("SpeedLimitWarningValueOffset").c_str()); - s->scene.speed_limit_control_enabled = params.getBool("EnableSlc"); - s->scene.feature_status_toggle = params.getBool("FeatureStatus"); - s->scene.onroad_settings_toggle = params.getBool("OnroadSettings"); - - // Handle Onroad Screen Off params - if (s->scene.onroadScreenOff > 0) { - s->scene.osoTimer = s->scene.onroadScreenOff * 60 * UI_FREQ; - } else if (s->scene.onroadScreenOff == 0) { - s->scene.osoTimer = 30 * UI_FREQ; - } else if (s->scene.onroadScreenOff == -1) { - s->scene.osoTimer = 15 * UI_FREQ; - } else { - s->scene.osoTimer = -1; - } } void UIState::updateStatus() { - auto params = Params(); if (scene.started && sm->updated("controlsState")) { auto controls_state = (*sm)["controlsState"].getControlsState(); - auto car_control = (*sm)["carControl"].getCarControl(); - auto car_state = (*sm)["carState"].getCarState(); - auto mads_enabled = car_state.getMadsEnabled(); auto state = controls_state.getState(); if (state == cereal::ControlsState::OpenpilotState::PRE_ENABLED || state == cereal::ControlsState::OpenpilotState::OVERRIDING) { status = STATUS_OVERRIDE; } else { - status = car_state.getMadsEnabled() ? car_control.getLongActive() ? STATUS_ENGAGED : STATUS_MADS : STATUS_DISENGAGED; + status = controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED; } - - if (mads_enabled != last_mads_enabled) { - mads_path_state = true; - } - last_mads_enabled = mads_enabled; - if (mads_path_state) { - if (mads_enabled) { - mads_path_count = fmax(mads_path_count - 1, 0); - if (mads_path_count == 0) { - mads_path_state = false; - } - } else { - mads_path_count = fmin(mads_path_count + 1, mads_path_timestep); - if (mads_path_count == mads_path_timestep) { - mads_path_state = false; - } - } - } - scene.mads_path_scale = mads_path_count * (1 / mads_path_timestep); } // Handle onroad/offroad transition @@ -326,74 +237,17 @@ void UIState::updateStatus() { scene.world_objects_visible = false; emit offroadTransition(!scene.started); } - - if (scene.started) { - // Auto hide UI button state machine - { - if (scene.button_auto_hide) { - if (scene.touch_to_wake) { - scene.sleep_btn = 30 * UI_FREQ; - } else if (scene.sleep_btn > 0) { - scene.sleep_btn--; - } else if (scene.sleep_btn == -1) { - scene.sleep_btn = 30 * UI_FREQ; - } - // Check if the sleep button should be fading in - if (scene.sleep_btn_fading_in) { - // Increase the opacity of the sleep button by a small amount - if (scene.sleep_btn_opacity < 20) { - scene.sleep_btn_opacity+= 10; - } - if (scene.sleep_btn_opacity >= 20) { - // If the opacity has reached its maximum value, stop fading in - scene.sleep_btn_fading_in = false; - scene.sleep_btn_opacity = 20; - } - } else if (scene.sleep_btn == 0) { - // Fade out the sleep button as before - if (scene.sleep_btn_opacity > 0) { - scene.sleep_btn_opacity-= 2; - } - } else { - // Set the opacity of the sleep button to its maximum value - scene.sleep_btn_opacity = 20; - } - } else { - scene.sleep_btn_opacity = 20; - } - } - - // Onroad Screen Off Brightness + Timer + Global Brightness - { - if (scene.onroadScreenOff != -2 && scene.touched2) { - scene.sleep_time = scene.osoTimer; - } else if (scene.onroadScreenOff != -2 && - ((scene.controlsState.getAlertSize() != cereal::ControlsState::AlertSize::NONE) && - ((scene.controlsState.getAlertStatus() == cereal::ControlsState::AlertStatus::NORMAL && scene.onroadScreenOffEvent) || - (scene.controlsState.getAlertStatus() != cereal::ControlsState::AlertStatus::NORMAL)))) { - scene.sleep_time = scene.osoTimer; - } else if (scene.sleep_time > 0 && scene.onroadScreenOff != -2) { - scene.sleep_time--; - } else if (scene.sleep_time == -1 && scene.onroadScreenOff != -2) { - scene.sleep_time = scene.osoTimer; - } - } - } - - if (sm->frame % UI_FREQ == 0) { // Update every 1 Hz - scene.sidebar_temp_options = std::atoi(params.get("SidebarTemperatureOptions").c_str()); - } - - scene.brightness = std::atoi(params.get("BrightnessControl").c_str()); } +// #ifdef SUNNYPILOT +// #include "selfdrive/ui/sunnypilot/qt/ui_scene.h" +// #define UIScene UISceneSP +// #endif UIState::UIState(QObject *parent) : QObject(parent) { sm = std::make_unique>({ "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2", - "wideRoadCameraState", "managerState", "navInstruction", "navRoute", "clocks", "longitudinalPlanSP", "liveMapDataSP", - "carControl", "lateralPlanSPDEPRECATED", "gpsLocation", "gpsLocationExternal", "liveParameters", "liveTorqueParameters", - "controlsStateSP", "modelV2SP" + "wideRoadCameraState", "managerState", "clocks", }); Params params; @@ -403,6 +257,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { prime_type = static_cast(std::atoi(prime_value.c_str())); } + RETURN_IF_SUNNYPILOT // update timer timer = new QTimer(this); QObject::connect(timer, &QTimer::timeout, this, &UIState::update); @@ -410,10 +265,15 @@ UIState::UIState(QObject *parent) : QObject(parent) { } void UIState::update() { + RETURN_IF_SUNNYPILOT update_sockets(this); update_state(this); updateStatus(); + if (std::getenv("PRIME_TYPE")) { + setPrimeType((PrimeType)atoi(std::getenv("PRIME_TYPE"))); + } + if (sm->frame % UI_FREQ == 0) { watchdog_kick(nanos_since_boot()); } @@ -435,21 +295,12 @@ void UIState::setPrimeType(PrimeType type) { } } -void UIState::setSunnylinkRoles(const std::vector& roles) { - sunnylinkRoles = roles; - emit sunnylinkRolesChanged(roles); -} - -void UIState::setSunnylinkDeviceUsers(const std::vector& users) { - sunnylinkUsers = users; - emit sunnylinkDeviceUsersChanged(users); -} - Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) { setAwake(true); resetInteractiveTimeout(); - +#ifndef SUNNYPILOT QObject::connect(uiState(), &UIState::uiUpdate, this, &Device::update); +#endif } void Device::update(const UIState &s) { @@ -474,7 +325,7 @@ void Device::resetInteractiveTimeout(int timeout) { } void Device::updateBrightness(const UIState &s) { - float clipped_brightness = offroad_brightness; + clipped_brightness = offroad_brightness; if (s.scene.started && s.scene.light_sensor > 0) { clipped_brightness = s.scene.light_sensor; @@ -488,14 +339,11 @@ void Device::updateBrightness(const UIState &s) { // Scale back to 10% to 100% clipped_brightness = std::clamp(100.0f * clipped_brightness, 10.0f, 100.0f); } - + RETURN_IF_SUNNYPILOT + int brightness = brightness_filter.update(clipped_brightness); if (!awake) { brightness = 0; - } else if (s.scene.started && s.scene.sleep_time == 0 && s.scene.onroadScreenOff != -2) { - brightness = s.scene.onroadScreenOffBrightness * 0.01 * brightness; - } else if (s.scene.brightness) { - brightness = s.scene.brightness * 0.99; } if (brightness != last_brightness) { @@ -519,6 +367,7 @@ void Device::updateWakefulness(const UIState &s) { setAwake(s.scene.ignition || interactive_timeout > 0); } +#ifndef SUNNYPILOT UIState *uiState() { static UIState ui_state; return &ui_state; @@ -528,3 +377,4 @@ Device *device() { static Device _device; return &_device; } +#endif \ No newline at end of file diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index a9fb6f0671..5626af6a91 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -15,31 +14,11 @@ #include "common/mat.h" #include "common/params.h" #include "common/timing.h" -#include "qt/network/sunnylink/models/role_model.h" -#include "qt/network/sunnylink/models/sponsor_role_model.h" -#include "qt/network/sunnylink/models/user_model.h" #include "system/hardware/hw.h" const int UI_BORDER_SIZE = 30; const int UI_HEADER_HEIGHT = 420; -const int UI_ROAD_NAME_MARGIN_X = 14; - -struct FeatureStatusText { - const QStringList dlp_list_text = { "Laneful", "Laneless", "Auto"}; - const QStringList gac_list_text = { "Aggressive", "Moderate", "Standard", "Relaxed"}; - const QStringList slc_list_text = { "Inactive", "Temp Off", "Adapting", "Active", "Pre Active"}; -}; - -struct FeatureStatusColor { - const QStringList dlp_list_color = { "#2020f8", "#0df87a", "#0df8f8" }; - const QStringList gac_list_color = { "#ff4b4b", "#fcff4b", "#4bff66", "#6a0ac9" }; - const QStringList slc_list_color = { "#ffffff", "#ffffff", "#fcff4b", "#4bff66", "#fcff4b" }; -}; - -const float DRIVING_PATH_WIDE = 0.9; -const float DRIVING_PATH_NARROW = 0.25; - const int UI_FREQ = 20; // Hz const int BACKLIGHT_OFFROAD = 50; @@ -66,12 +45,18 @@ constexpr vec3 default_face_kpts_3d[] = { {18.02, -49.14, 8.00}, {6.36, -51.20, 8.00}, {-5.98, -51.20, 8.00}, }; +//Example of a macro +#ifdef SUNNYPILOT +#define EXTRA_UI_STATES STATUS_MADS +#else +#define EXTRA_UI_STATES +#endif typedef enum UIStatus { STATUS_DISENGAGED, STATUS_OVERRIDE, STATUS_ENGAGED, - STATUS_MADS, + EXTRA_UI_STATES } UIStatus; enum PrimeType { @@ -88,18 +73,10 @@ enum PrimeType { const QColor bg_colors [] = { [STATUS_DISENGAGED] = QColor(0x17, 0x33, 0x49, 0xc8), [STATUS_OVERRIDE] = QColor(0x91, 0x9b, 0x95, 0xf1), - [STATUS_ENGAGED] = QColor(0x00, 0xc8, 0x00, 0xf1), - [STATUS_MADS] = QColor(0x00, 0xc8, 0xc8, 0xf1), + [STATUS_ENGAGED] = QColor(0x17, 0x86, 0x44, 0xf1), }; -const QColor tcs_colors [] = { - [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::DISABLED)] = QColor(0x0, 0x0, 0x0, 0xff), - [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::ENTERING)] = QColor(0xC9, 0x22, 0x31, 0xf1), - [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::TURNING)] = QColor(0xDA, 0x6F, 0x25, 0xf1), - [int(cereal::LongitudinalPlanSP::VisionTurnControllerState::LEAVING)] = QColor(0x17, 0x86, 0x44, 0xf1), -}; - typedef struct UIScene { bool calibration_valid = false; bool calibration_wide_valid = false; @@ -107,24 +84,13 @@ typedef struct UIScene { mat3 view_from_calib = DEFAULT_CALIBRATION; mat3 view_from_wide_calib = DEFAULT_CALIBRATION; cereal::PandaState::PandaType pandaType; - cereal::ControlsState::Reader controlsState; - - // Debug UI - bool show_debug_ui; - - // Speed limit control - bool speed_limit_control_enabled; - int speed_limit_control_policy; - double last_speed_limit_sign_tap; // modelV2 float lane_line_probs[4]; float road_edge_stds[2]; QPolygonF track_vertices; - QPolygonF track_edge_vertices; QPolygonF lane_line_vertices[4]; QPolygonF road_edge_vertices[2]; - QPolygonF lane_barrier_vertices[2]; // lead QPointF lead_vertices[2]; @@ -136,87 +102,24 @@ typedef struct UIScene { float driver_pose_coss[3]; vec3 face_kpts_draw[std::size(default_face_kpts_3d)]; - bool navigate_on_openpilot_deprecated = false; cereal::LongitudinalPersonality personality; - cereal::AccelerationPersonality accel_personality; float light_sensor = -1; - bool started, ignition, is_metric, map_on_left, longitudinal_control; + bool started, ignition, is_metric, longitudinal_control; bool world_objects_visible = false; uint64_t started_frame; - - int dynamic_lane_profile; - bool dynamic_lane_profile_status = true; - - bool visual_brake_lights; - - int onroadScreenOff, osoTimer, brightness, onroadScreenOffBrightness, awake; - bool onroadScreenOffEvent; - int sleep_time = -1; - bool touched2 = false; - - bool stand_still_timer; - - bool hide_vego_ui, true_vego_ui; - - int chevron_data; - - bool gac; - int longitudinal_personality; - int longitudinal_accel_personality; - - bool map_visible; - int dev_ui_info; - bool live_torque_toggle; - - bool touch_to_wake = false; - int sleep_btn = -1; - bool sleep_btn_fading_in = false; - int sleep_btn_opacity = 20; - bool button_auto_hide; - - bool reverse_dm_cam; - - bool e2e_long_alert_light, e2e_long_alert_lead, e2e_long_alert_ui; - float e2eX[13] = {0}; - - int sidebar_temp_options; - - float mads_path_scale = DRIVING_PATH_WIDE - DRIVING_PATH_NARROW; - float mads_path_range = DRIVING_PATH_WIDE - DRIVING_PATH_NARROW; // 0.9 - 0.25 = 0.65 - - bool onroad_settings_visible; - - bool map_3d_buildings; - - bool torqued_override; - - bool dynamic_experimental_control; - - int speed_limit_control_engage_type; - - bool mapbox_fullscreen; - - bool speed_limit_warning_flash; - int speed_limit_warning_type; - int speed_limit_warning_value_offset; - - bool custom_driving_model_valid; - cereal::ModelGeneration driving_model_generation; - uint32_t driving_model_capabilities; - - bool feature_status_toggle; - bool onroad_settings_toggle; - - bool dynamic_personality; } UIScene; +#ifdef SUNNYPILOT +#include "sunnypilot/qt/ui_scene.h" +#define UIScene UISceneSP +#endif class UIState : public QObject { Q_OBJECT public: UIState(QObject* parent = 0); - void updateStatus(); + virtual void updateStatus(); inline bool engaged() const { return scene.started && (*sm)["controlsState"].getControlsState().getEnabled(); } @@ -225,42 +128,6 @@ public: inline PrimeType primeType() const { return prime_type; } inline bool hasPrime() const { return prime_type > PrimeType::NONE; } - void setSunnylinkRoles(const std::vector &roles); - void setSunnylinkDeviceUsers(const std::vector &users); - - inline std::vector sunnylinkDeviceRoles() const { return sunnylinkRoles; } - inline bool isSunnylinkAdmin() const { - return std::any_of(sunnylinkRoles.begin(), sunnylinkRoles.end(), [](const RoleModel &role) { - return role.roleType == RoleType::Admin; - }); - } - inline bool isSunnylinkSponsor() const { - return std::any_of(sunnylinkRoles.begin(), sunnylinkRoles.end(), [](const RoleModel &role) { - return role.roleType == RoleType::Sponsor && role.as().roleTier != SponsorTier::Free; - }); - } - inline SponsorRoleModel sunnylinkSponsorRole() const { - std::optional sponsorRoleWithHighestTier = std::nullopt; - for (const auto &role : sunnylinkRoles) { - if(role.roleType != RoleType::Sponsor) - continue; - - if (auto sponsorRole = role.as(); !sponsorRoleWithHighestTier.has_value() || sponsorRoleWithHighestTier->roleTier < sponsorRole.roleTier) { - sponsorRoleWithHighestTier = sponsorRole; - } - } - return sponsorRoleWithHighestTier.value_or(SponsorRoleModel(RoleType::Sponsor, SponsorTier::Free)); - } - inline SponsorTier sunnylinkSponsorTier() const { - return sunnylinkSponsorRole().roleTier; - } - inline std::vector sunnylinkDeviceUsers() const { return sunnylinkUsers; } - inline bool isSunnylinkPaired() const { - return std::any_of(sunnylinkUsers.begin(), sunnylinkUsers.end(), [](const UserModel &user) { - return user.user_id.toLower() != "unregisteredsponsor" && user.user_id.toLower() != "temporarysponsor"; - }); - } - int fb_w = 0, fb_h = 0; std::unique_ptr sm; @@ -278,27 +145,21 @@ signals: void primeChanged(bool prime); void primeTypeChanged(PrimeType prime_type); - void sunnylinkRoleChanged(bool subscriber); - void sunnylinkRolesChanged(std::vector roles); - void sunnylinkDeviceUsersChanged(std::vector users); +protected slots: + virtual void update(); -private slots: - void update(); - -private: +protected: QTimer *timer; - bool started_prev = false; PrimeType prime_type = PrimeType::UNKNOWN; - std::vector sunnylinkRoles = {}; - std::vector sunnylinkUsers = {}; - bool last_mads_enabled = false; - bool mads_path_state = false; - float mads_path_timestep = 4; // UI runs at 20 Hz, therefore 0.2 second is [0.2 second / (1 / 20 Hz) = 4] - float mads_path_count = 4; // UI runs at 20 Hz, therefore 0.2 second is [0.2 second / (1 / 20 Hz) = 4] +private: + bool started_prev = false; }; +#undef UIScene +#ifndef SUNNYPILOT UIState *uiState(); +#endif // device management class class Device : public QObject { @@ -311,7 +172,7 @@ public: offroad_brightness = std::clamp(brightness, 0, 100); } -private: +protected: bool awake = false; int interactive_timeout = 0; bool ignition_on = false; @@ -321,9 +182,10 @@ private: FirstOrderFilter brightness_filter; QFuture brightness_future; - void updateBrightness(const UIState &s); + virtual void updateBrightness(const UIState &s); void updateWakefulness(const UIState &s); void setAwake(bool on); + float clipped_brightness; signals: void displayPowerChanged(bool on); @@ -334,7 +196,9 @@ public slots: void update(const UIState &s); }; +#ifndef SUNNYPILOT Device *device(); +#endif void ui_update_params(UIState *s); int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_height); @@ -343,4 +207,8 @@ void update_model(UIState *s, void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd); void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line); void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, - float y_off, float z_off_left, float z_off_right, QPolygonF *pvd, int max_idx, bool allow_invert); + float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert); + +bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, float in_z, QPointF *out, float margin=500.0f); +void update_state(UIState *s); +void update_sockets(UIState *s); diff --git a/system/manager/process_config.py b/system/manager/process_config.py index a718ea021b..1db62dc2c4 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -45,8 +45,7 @@ def only_offroad(started, params, CP: car.CarParams) -> bool: return not started def use_gitlab_runner(started, params, CP: car.CarParams) -> bool: - return (not PC and params.get_bool("EnableGitlabRunner") and only_offroad(started, params, CP) - and os.path.exists("./gitlab_runner.sh")) + return not PC and params.get_bool("EnableGitlabRunner") and only_offroad(started, params, CP) def model_use_nav(started, params, CP: car.CarParams) -> bool: custom_model_metadata = CustomModelMetadata(params=params, init_only=True) @@ -62,7 +61,7 @@ def sunnylink_need_register_shim(started, params, CP: car.CarParams) -> bool: def use_sunnylink_uploader_shim(started, params, CP: car.CarParams) -> bool: """Shim for use_sunnylink_uploader to match the process manager signature.""" - return use_sunnylink_uploader(params) and os.path.exists("../loggerd/sunnylink_uploader.py") + return use_sunnylink_uploader(params) procs = [ DaemonProcess("manage_athenad", "system.athena.manage_athenad", "AthenadPid"), @@ -120,12 +119,15 @@ procs = [ PythonProcess("webrtcd", "system.webrtc.webrtcd", notcar), PythonProcess("webjoystick", "tools.bodyteleop.web", notcar), - # Sunnypilot devs - NativeProcess("gitlab_runner_start", "system/manager", ["./gitlab_runner.sh", "start"], use_gitlab_runner, sigkill=False), - # Sunnylink <3 + # sunnylink <3 DaemonProcess("manage_sunnylinkd", "system.athena.manage_sunnylinkd", "SunnylinkdPid"), PythonProcess("sunnylink_registration", "system.manager.sunnylink", sunnylink_need_register_shim), - PythonProcess("sunnylink_uploader", "system.loggerd.sunnylink_uploader", use_sunnylink_uploader_shim), ] +if os.path.exists("./gitlab_runner.sh"): + procs += [NativeProcess("gitlab_runner_start", "system/manager", ["./gitlab_runner.sh", "start"], use_gitlab_runner, sigkill=False)] + +if os.path.exists("../loggerd/sunnylink_uploader.py"): + procs += [PythonProcess("sunnylink_uploader", "system.loggerd.sunnylink_uploader", use_sunnylink_uploader_shim)] + managed_processes = {p.name: p for p in procs} diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 15e1ee5f2f..f1700ebe20 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -6,7 +6,12 @@ #include #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define ElidedLabel ElidedLabelSP +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif #include "tools/cabana/binaryview.h" #include "tools/cabana/chart/chartswidget.h" #include "tools/cabana/historylog.h" From eb1b0d7ddcc88541aefeb0081ca8be5546d490dd Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 29 Jul 2024 06:55:29 -0400 Subject: [PATCH 12/12] Update public panda submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 2fb99d017c..d794b781f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "panda"] path = panda - url = ../../sunnyhaibin/panda-special.git + url = ../../sunnyhaibin/panda.git [submodule "opendbc"] path = opendbc url = https://github.com/sunnyhaibin/opendbc.git