Merge branch 'master' into mads-hkg-available-fix

# Conflicts:
#	SConstruct
#	selfdrive/ui/SConscript
#	selfdrive/ui/sunnypilot/SConscript
This commit is contained in:
Jason Wen
2024-07-29 15:19:03 -04:00
228 changed files with 34363 additions and 5470 deletions
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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
+5 -3
View File
@@ -32,14 +32,16 @@ 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
* 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)
+21
View File
@@ -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 <support@sunnypilot> for permission requests.
---
Haibin Wen, SUNNYPILOT LLC
+8 -4
View File
@@ -113,11 +113,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) or True, # check if the current user is a sunnypilot developer. TODO: Remove the 'or True' part once the GPG keys are available.
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 +222,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(' ')
-1
View File
@@ -137,7 +137,6 @@ struct CarEvent @0x9b1657f34caf3ad3 {
speedLimitPreActive @139;
speedLimitConfirmed @140;
torqueNNLoad @141;
spAutoBrakeHold @142;
radarCanErrorDEPRECATED @15;
communityFeatureDisallowedDEPRECATED @62;
+2 -1
View File
@@ -318,7 +318,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"TorqueFriction", PERSISTENT | BACKUP},
{"TorqueMaxLatAccel", PERSISTENT | BACKUP},
{"TorquedOverride", PERSISTENT | BACKUP},
{"ToyotaAutoHold", PERSISTENT | BACKUP},
{"ToyotaAutoLockBySpeed", PERSISTENT | BACKUP},
{"ToyotaAutoUnlockByShifter", PERSISTENT | BACKUP},
{"ToyotaEnhancedBsm", PERSISTENT | BACKUP},
@@ -336,6 +335,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SunnylinkCache_Users", PERSISTENT},
{"SunnylinkCache_Roles", PERSISTENT},
{"EnableGitlabRunner", PERSISTENT | BACKUP},
{"EnableSunnylinkUploader", PERSISTENT | BACKUP},
// PFEIFER - MAPD {{
{"MapdVersion", PERSISTENT},
+1 -1
Submodule panda updated: ff1ef85735...5d92bdc6de
+22 -4
View File
@@ -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():
-3
View File
@@ -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
+8 -4
View File
@@ -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:
+12 -9
View File
@@ -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"]
+9 -26
View File
@@ -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
+8 -6
View File
@@ -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
+13 -11
View File
@@ -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
+9 -27
View File
@@ -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
+11 -10
View File
@@ -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
+9 -17
View File
@@ -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,
+7 -4
View File
@@ -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)
+8 -15
View File
@@ -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,12 +205,12 @@ 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.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 not self.CP.pcmCruiseSpeed:
@@ -233,7 +233,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:
@@ -244,17 +244,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
+7 -1
View File
@@ -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]]
+5 -4
View File
@@ -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:
+12 -9
View File
@@ -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
+12 -26
View File
@@ -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
+10 -8
View File
@@ -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
+7 -20
View File
@@ -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)
+6 -21
View File
@@ -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)
-31
View File
@@ -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
-9
View File
@@ -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)
+7 -29
View File
@@ -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
@@ -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
@@ -212,11 +209,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 +241,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],
@@ -289,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
-34
View File
@@ -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)
-1
View File
@@ -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):
+3 -23
View File
@@ -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.
+10 -33
View File
@@ -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
-10
View File
@@ -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))
-8
View File
@@ -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"),
+4
View File
@@ -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"
+4
View File
@@ -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;
+11 -8
View File
@@ -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)
+6 -1
View File
@@ -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);
+19 -143
View File
@@ -2,7 +2,6 @@
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <QApplication>
#include <QCryptographicHash>
@@ -10,12 +9,10 @@
#include <QDebug>
#include <QJsonDocument>
#include <QNetworkRequest>
#include <QSsl>
#include <memory>
#include <string>
#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<char*>(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<char*>(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();
+9 -9
View File
@@ -6,14 +6,13 @@
#include <QTimer>
#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();
};
+4
View File
@@ -5,7 +5,11 @@
#include <QPushButton>
#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
+1 -171
View File
@@ -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")));
}
}
+13 -30
View File
@@ -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 &param = "");
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);
};
+9 -62
View File
@@ -6,9 +6,14 @@
#include <QDebug>
#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");
+10 -7
View File
@@ -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<QMapLibre::Map> 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:
+6 -1
View File
@@ -3,8 +3,13 @@
#include <QDateTime>
#include <QPainter>
#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;
+4
View File
@@ -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 <algorithm>
#include <string>
+1 -2
View File
@@ -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";
+6 -1
View File
@@ -3,8 +3,13 @@
#include <QDir>
#include <QVBoxLayout>
#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";
+7 -1
View File
@@ -3,10 +3,16 @@
#include <QHBoxLayout>
#include <QWidget>
#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);
+4
View File
@@ -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";
+13 -52
View File
@@ -6,11 +6,16 @@
#include <QScrollBar>
#include <QStyle>
#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);
}
}
+8 -3
View File
@@ -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);
};
};
@@ -1,33 +0,0 @@
#ifndef USER_MODEL_H
#define USER_MODEL_H
#include <QJsonObject>
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
@@ -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 &param_name, const QString &param_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;
}
}
@@ -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
@@ -1,29 +0,0 @@
#include "selfdrive/ui/qt/network/sunnylink/services/role_service.h"
#include <QJsonArray>
#include <QJsonDocument>
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<RoleModel> roles;
for (const auto &value : jsonArray) {
roles.emplace_back(value.toObject());
}
emit rolesReady(roles);
uiState()->setSunnylinkRoles(roles);
}
@@ -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<RoleModel> &roles);
protected:
void handleResponse(const QString&response, bool success) override;
private:
QString url = "/roles";
};
#endif
@@ -1,31 +0,0 @@
#include "selfdrive/ui/qt/network/sunnylink/services/user_service.h"
#include <QJsonArray>
#include <QJsonDocument>
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<UserModel> users;
for (const auto &value : jsonArray) {
users.emplace_back(value.toObject());
}
emit usersReady(users);
uiState()->setSunnylinkDeviceUsers(users);
}
@@ -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<UserModel>&users);
protected:
void handleResponse(const QString&response, bool success) override;
private:
QString url = "/users";
};
#endif
@@ -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);
}
@@ -1,18 +0,0 @@
#ifndef SUNNYLINK_CLIENT_H
#define SUNNYLINK_CLIENT_H
#include <QObject>
#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
+4
View File
@@ -2,7 +2,11 @@
#include <utility>
#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"
+6 -1
View File
@@ -7,13 +7,18 @@
#include <QStyle>
#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;
+3 -17
View File
@@ -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();
}
}
+9 -8
View File
@@ -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();
};
};
+43 -319
View File
@@ -5,28 +5,29 @@
#include <vector>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QDateTime>
#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<std::tuple<QString, QString, QString, QString>> 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<QString> longi_button_texts{tr("Aggressive"), tr("Moderate"), tr("Standard"), tr("Relaxed")};
std::vector<QString> 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<QString> 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 &param_name, const QString &param_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<int>(accel_personality));
}
uiState()->scene.accel_personality = accel_personality;
}
}
void TogglesPanel::expandToggleDescription(const QString &param) {
@@ -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<br>"
"<h4>%2</h4><br>"
"%3<br>"
@@ -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("<b>" + long_desc + "</b><br><br>" + 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<std::string> 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<ButtonControl *>()) {
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 &param) {
}
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<PanelInfo> 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<QPair<QString, QWidget *>> 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;
}
)");
+21 -32
View File
@@ -8,12 +8,23 @@
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>
#include <QWidget>
#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 &param);
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 &param);
void updateToggles();
private slots:
void updateState(const UIState &s);
protected slots:
virtual void updateState(const UIState &s);
private:
protected:
Params params;
std::map<std::string, ParamControl*> 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;
+12 -5
View File
@@ -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"));
@@ -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<std::string, ParamControl*> toggles;
CameraOffset *camera_offset;
PathOffset *path_offset;
};
@@ -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<std::string, ParamControl*> toggles;
OnroadScreenOff *onroad_screen_off;
OnroadScreenOffBrightness *onroad_screen_off_brightness;
};
@@ -1,35 +0,0 @@
#pragma once
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QEventLoop>
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;
}
};
@@ -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<std::string, ParamControl*> toggles;
AutoLaneChangeTimer *auto_lane_change_timer;
PauseLateralSpeed *pause_lateral_speed;
};
@@ -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<std::string, ParamControl*> toggles;
ButtonParamControl *dlob_settings;
};
@@ -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<std::tuple<QString, QString, QString, QString>> 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);
}
@@ -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<std::string, ParamControl*> toggles;
};
@@ -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 &param_name, const QString &param_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));
}
@@ -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<std::string, ParamControl*> toggles;
ParamWatcher *param_watcher;
TorqueFriction *friction;
TorqueMaxLatAccel *lat_accel_factor;
ButtonParamControl *dlp_settings;
ScrollView *scrollView = nullptr;
const QString nnff_description = QString("%1<br><br>"
"%2")
.arg(tr("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."))
.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: ") + "<font color='white'><b>#tuning-nnlc</b></font>");
QString nnffDescriptionBuilder(const QString &custom_description) {
QString description = "<b>" + custom_description + "</b><br><br>" + 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."));
};
@@ -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;
}
)");
}
@@ -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;
};
@@ -1,59 +0,0 @@
#pragma once
#include <QApplication>
#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<br><br>"
"%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 = "<b>" + custom_description + "</b><br><br>" + toyotaEnhancedBsmDescription;
return description;
}
};
@@ -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<std::string, ParamControl*> toggles;
ButtonParamControl *dev_ui_settings;
ButtonParamControl *chevron_info_settings;
ButtonParamControl *sidebar_temp_setting;
};
-11
View File
@@ -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"
+152
View File
@@ -0,0 +1,152 @@
#include "selfdrive/ui/qt/offroad_home.h"
#include <QHBoxLayout>
#include <QMouseEvent>
#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")));
}
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include <QStackedWidget>
#include <QWidget>
#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 &param = "");
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;
};
File diff suppressed because it is too large Load Diff
+1 -175
View File
@@ -2,14 +2,10 @@
#include <QVBoxLayout>
#include <memory>
#include <QTimer>
#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<PubMaster> 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<PubMaster> 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;
};
+1 -59
View File
@@ -15,18 +15,6 @@ void drawIcon(QPainter &p, const QPoint &center, 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);
}
+8 -29
View File
@@ -2,7 +2,11 @@
#include <QPushButton>
#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 &center, const QPixmap &img, const QBrush &bg, float opacity);
+4 -107
View File
@@ -3,12 +3,6 @@
#include <QPainter>
#include <QStackedLayout>
#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));
+12 -34
View File
@@ -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);
};
-26
View File
@@ -1,26 +0,0 @@
#include "selfdrive/ui/qt/onroad_settings_panel.h"
#include <QHBoxLayout>
#include <QWidget>
#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();
}
}
-23
View File
@@ -1,23 +0,0 @@
#pragma once
#include <QFrame>
#ifdef ENABLE_MAPS
#include <QMapLibre/Settings>
#endif
#include <QStackedLayout>
class OnroadSettingsPanel : public QFrame {
Q_OBJECT
public:
explicit OnroadSettingsPanel(QWidget *parent = nullptr);
signals:
void onroadSettingsPanelRequested();
public slots:
void toggleOnroadSettings();
private:
QStackedLayout *content_stack;
};
+17 -17
View File
@@ -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);
}
}
+11 -12
View File
@@ -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();
}
};
+13 -76
View File
@@ -1,11 +1,8 @@
#include "selfdrive/ui/qt/sidebar.h"
#include <cmath>
#include <QMouseEvent>
#include "selfdrive/ui/qt/util.h"
#include "common/params.h"
void Sidebar::drawMetric(QPainter &p, const QPair<QString, QString> &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<float>::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<float>::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);
}
+7 -9
View File
@@ -4,8 +4,13 @@
#include <QFrame>
#include <QMap>
#include "cereal/messaging/messaging.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#else
#include "selfdrive/ui/ui.h"
#endif
typedef QPair<QPair<QString, QString>, 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<QString, QString> &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<PubMaster> pm;
QString sidebar_temp = "0";
QString sidebar_temp_str = "0";
};
-72
View File
@@ -4,41 +4,12 @@
#include <QScrollBar>
#include <QVBoxLayout>
#include <QWidget>
#include <QtConcurrent>
#include <cstdio>
#include <sstream>
#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<char, 128> buffer{};
std::ostringstream result;
std::unique_ptr<FILE, decltype(&pclose)> 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<void> 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<void> 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<void>::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();

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