mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-07-26 11:52:04 +08:00
Merge branch 'master' of https://github.com/sunnypilot/sunnypilot into sync
This commit is contained in:
@@ -151,6 +151,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
||||
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"BlinkerLateralReengageDelay", {PERSISTENT | BACKUP, INT, "0"}}, // seconds
|
||||
{"BlinkerMinLateralControlSpeed", {PERSISTENT | BACKUP, INT, "20"}}, // MPH or km/h
|
||||
{"BlinkerPauseLateralControl", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
{"Brightness", {PERSISTENT | BACKUP, INT, "0"}},
|
||||
|
||||
@@ -4,27 +4,190 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from enum import IntEnum
|
||||
|
||||
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.cruise_sub_layouts.speed_limit_settings import SpeedLimitSettingsLayout
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.sunnypilot.widgets.list_view import toggle_item_sp, option_item_sp, simple_button_item_sp
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
|
||||
|
||||
class PanelType(IntEnum):
|
||||
CRUISE = 0
|
||||
SLA = 1
|
||||
|
||||
|
||||
ICBM_DESC = tr_noop("When enabled, sunnypilot will attempt to manage the built-in cruise control buttons " +
|
||||
"by emulating button presses for limited longitudinal control.")
|
||||
ICMB_UNAVAILABLE = tr_noop("Intelligent Cruise Button Management is currently unavailable on this platform.")
|
||||
ICMB_UNAVAILABLE_LONG_AVAILABLE = tr_noop("Disable the sunnypilot Longitudinal Control (alpha) toggle to allow Intelligent Cruise Button Management.")
|
||||
ICMB_UNAVAILABLE_LONG_UNAVAILABLE = tr_noop("sunnypilot Longitudinal Control is the default longitudinal control for this platform.")
|
||||
|
||||
ACC_ENABLED_DESCRIPTION = tr_noop("Enable custom Short & Long press increments for cruise speed increase/decrease.")
|
||||
ACC_NOLONG_DESCRIPTION = tr_noop("This feature can only be used with sunnypilot longitudinal control enabled.")
|
||||
ACC_PCMCRUISE_DISABLED_DESCRIPTION = tr_noop("This feature is not supported on this platform due to vehicle limitations.")
|
||||
ONROAD_ONLY_DESCRIPTION = tr_noop("Start the vehicle to check vehicle compatibility.")
|
||||
|
||||
|
||||
class CruiseLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._current_panel = PanelType.CRUISE
|
||||
self._speed_limit_layout = SpeedLimitSettingsLayout(lambda: self._set_current_panel(PanelType.CRUISE))
|
||||
|
||||
self._params = Params()
|
||||
items = self._initialize_items()
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _initialize_items(self):
|
||||
|
||||
self.icbm_toggle = toggle_item_sp(
|
||||
title=tr("Intelligent Cruise Button Management (ICBM) (Alpha)"),
|
||||
description="",
|
||||
param="IntelligentCruiseButtonManagement")
|
||||
|
||||
self.scc_v_toggle = toggle_item_sp(
|
||||
title=tr("Smart Cruise Control - Vision"),
|
||||
description=tr("Use vision path predictions to estimate the appropriate speed to drive through turns ahead."),
|
||||
param="SmartCruiseControlVision")
|
||||
|
||||
self.scc_m_toggle = toggle_item_sp(
|
||||
title=tr("Smart Cruise Control - Map"),
|
||||
description=tr("Use map data to estimate the appropriate speed to drive through turns ahead."),
|
||||
param="SmartCruiseControlMap")
|
||||
|
||||
self.custom_acc_toggle = toggle_item_sp(
|
||||
title=tr("Custom ACC Speed Increments"),
|
||||
description="",
|
||||
param="CustomAccIncrementsEnabled",
|
||||
callback=self._on_custom_acc_toggle)
|
||||
|
||||
self.custom_acc_short_increment = option_item_sp(
|
||||
title=tr("Short Press Increment"),
|
||||
param="CustomAccShortPressIncrement",
|
||||
min_value=1, max_value=10, value_change_step=1,
|
||||
inline=True)
|
||||
|
||||
self.custom_acc_long_increment = option_item_sp(
|
||||
title=tr("Long Press Increment"),
|
||||
param="CustomAccLongPressIncrement",
|
||||
value_map={1: 1, 2: 5, 3: 10},
|
||||
min_value=1, max_value=3, value_change_step=1,
|
||||
inline=True)
|
||||
|
||||
self.sla_settings_button = simple_button_item_sp(
|
||||
button_text=lambda: tr("Speed Limit"),
|
||||
button_width=800,
|
||||
callback=lambda: self._set_current_panel(PanelType.SLA)
|
||||
)
|
||||
|
||||
self.dec_toggle = toggle_item_sp(
|
||||
title=tr("Enable Dynamic Experimental Control"),
|
||||
description=tr("Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal."),
|
||||
param="DynamicExperimentalControl")
|
||||
|
||||
items = [
|
||||
self.icbm_toggle,
|
||||
self.dec_toggle,
|
||||
self.scc_v_toggle,
|
||||
self.scc_m_toggle,
|
||||
self.custom_acc_toggle,
|
||||
self.custom_acc_short_increment,
|
||||
self.custom_acc_long_increment,
|
||||
self.sla_settings_button,
|
||||
]
|
||||
return items
|
||||
|
||||
def _render(self, rect):
|
||||
self._scroller.render(rect)
|
||||
if self._current_panel == PanelType.SLA:
|
||||
self._speed_limit_layout.render(rect)
|
||||
else:
|
||||
self._scroller.render(rect)
|
||||
|
||||
def show_event(self):
|
||||
self._set_current_panel(PanelType.CRUISE)
|
||||
self._scroller.show_event()
|
||||
self.icbm_toggle.show_description(True)
|
||||
self.custom_acc_toggle.show_description(True)
|
||||
|
||||
def _set_current_panel(self, panel: PanelType):
|
||||
self._current_panel = panel
|
||||
if panel == PanelType.SLA:
|
||||
self._speed_limit_layout.show_event()
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
|
||||
if ui_state.CP is not None and ui_state.CP_SP is not None:
|
||||
has_icbm = ui_state.has_icbm
|
||||
has_long = ui_state.has_longitudinal_control
|
||||
|
||||
if ui_state.CP_SP.intelligentCruiseButtonManagementAvailable and not has_long:
|
||||
self.icbm_toggle.action_item.set_enabled(ui_state.is_offroad())
|
||||
self.icbm_toggle.set_description(tr(ICBM_DESC))
|
||||
else:
|
||||
ui_state.params.remove("IntelligentCruiseButtonManagement")
|
||||
self.icbm_toggle.action_item.set_enabled(False)
|
||||
|
||||
long_desc = ICMB_UNAVAILABLE
|
||||
if has_long:
|
||||
if ui_state.CP.alphaLongitudinalAvailable:
|
||||
long_desc += " " + ICMB_UNAVAILABLE_LONG_AVAILABLE
|
||||
else:
|
||||
long_desc += " " + ICMB_UNAVAILABLE_LONG_UNAVAILABLE
|
||||
|
||||
new_desc = "<b>" + tr(long_desc) + "</b>\n\n" + tr(ICBM_DESC)
|
||||
if self.icbm_toggle.description != new_desc:
|
||||
self.icbm_toggle.set_description(new_desc)
|
||||
self.icbm_toggle.show_description(True)
|
||||
|
||||
if has_long or has_icbm:
|
||||
self.custom_acc_toggle.action_item.set_enabled(((has_long and not ui_state.CP.pcmCruise) or has_icbm) and ui_state.is_offroad())
|
||||
self.dec_toggle.action_item.set_enabled(has_long)
|
||||
self.scc_v_toggle.action_item.set_enabled(True)
|
||||
self.scc_m_toggle.action_item.set_enabled(True)
|
||||
else:
|
||||
ui_state.params.remove("CustomAccIncrementsEnabled")
|
||||
ui_state.params.remove("DynamicExperimentalControl")
|
||||
ui_state.params.remove("SmartCruiseControlVision")
|
||||
ui_state.params.remove("SmartCruiseControlMap")
|
||||
self.custom_acc_toggle.action_item.set_enabled(False)
|
||||
self.dec_toggle.action_item.set_enabled(False)
|
||||
self.scc_v_toggle.action_item.set_enabled(False)
|
||||
self.scc_m_toggle.action_item.set_enabled(False)
|
||||
|
||||
else:
|
||||
has_icbm = has_long = False
|
||||
self.icbm_toggle.action_item.set_enabled(False)
|
||||
self.icbm_toggle.set_description(tr(ONROAD_ONLY_DESCRIPTION))
|
||||
|
||||
show_custom_acc_desc = False
|
||||
|
||||
if ui_state.is_offroad():
|
||||
new_custom_acc_desc = tr(ONROAD_ONLY_DESCRIPTION)
|
||||
show_custom_acc_desc = True
|
||||
else:
|
||||
if has_long or has_icbm:
|
||||
if has_long and ui_state.CP.pcmCruise:
|
||||
new_custom_acc_desc = tr(ACC_PCMCRUISE_DISABLED_DESCRIPTION)
|
||||
show_custom_acc_desc = True
|
||||
else:
|
||||
new_custom_acc_desc = tr(ACC_ENABLED_DESCRIPTION)
|
||||
else:
|
||||
new_custom_acc_desc = tr(ACC_NOLONG_DESCRIPTION)
|
||||
show_custom_acc_desc = True
|
||||
self.custom_acc_toggle.action_item.set_state(False)
|
||||
|
||||
if self.custom_acc_toggle.description != new_custom_acc_desc:
|
||||
self.custom_acc_toggle.set_description(new_custom_acc_desc)
|
||||
if show_custom_acc_desc:
|
||||
self.custom_acc_toggle.show_description(True)
|
||||
|
||||
self._on_custom_acc_toggle(self.custom_acc_toggle.action_item.get_state())
|
||||
|
||||
def _on_custom_acc_toggle(self, state):
|
||||
self.custom_acc_short_increment.set_visible(state)
|
||||
self.custom_acc_long_increment.set_visible(state)
|
||||
self.custom_acc_short_increment.action_item.set_enabled(self.custom_acc_toggle.action_item.enabled)
|
||||
self.custom_acc_long_increment.action_item.set_enabled(self.custom_acc_toggle.action_item.enabled)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from collections.abc import Callable
|
||||
|
||||
import pyray as rl
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.sunnypilot.widgets.list_view import multiple_button_item_sp
|
||||
from openpilot.system.ui.widgets.network import NavButton
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.sunnypilot.widgets import get_highlighted_description
|
||||
|
||||
SPEED_LIMIT_POLICY_BUTTONS = [tr("Car Only"), tr("Map Only"), tr("Car First"), tr("Map First"), tr("Combined")]
|
||||
|
||||
SPEED_LIMIT_POLICY_DESCRIPTIONS = [
|
||||
tr("Car Only: Use Speed Limit data only from Car"),
|
||||
tr("Map Only: Use Speed Limit data only from OpenStreetMaps"),
|
||||
tr("Car First: Use Speed Limit data from Car if available, else use from OpenStreetMaps"),
|
||||
tr("Map First: Use Speed Limit data from OpenStreetMaps if available, else use from Car"),
|
||||
tr("Combined: Use combined Speed Limit data from Car & OpenStreetMaps")
|
||||
]
|
||||
|
||||
|
||||
class SpeedLimitPolicyLayout(Widget):
|
||||
def __init__(self, back_btn_callback: Callable):
|
||||
super().__init__()
|
||||
self._back_button = NavButton(tr("Back"))
|
||||
self._back_button.set_click_callback(back_btn_callback)
|
||||
|
||||
items = self._initialize_items()
|
||||
self._scroller = Scroller(items, line_separator=False, spacing=0)
|
||||
|
||||
def _initialize_items(self):
|
||||
self._speed_limit_policy = multiple_button_item_sp(
|
||||
title=lambda: tr("Speed Limit Source"),
|
||||
description=self._get_policy_description,
|
||||
buttons=SPEED_LIMIT_POLICY_BUTTONS,
|
||||
param="SpeedLimitPolicy",
|
||||
button_width=250,
|
||||
)
|
||||
|
||||
items = [
|
||||
self._speed_limit_policy
|
||||
]
|
||||
return items
|
||||
|
||||
@staticmethod
|
||||
def _get_policy_description():
|
||||
return get_highlighted_description(ui_state.params, "SpeedLimitPolicy", SPEED_LIMIT_POLICY_DESCRIPTIONS)
|
||||
|
||||
def _render(self, rect):
|
||||
self._back_button.set_position(self._rect.x, self._rect.y + 20)
|
||||
self._back_button.render()
|
||||
|
||||
content_rect = rl.Rectangle(rect.x, rect.y + self._back_button.rect.height + 40, rect.width, rect.height - self._back_button.rect.height - 40)
|
||||
self._scroller.render(content_rect)
|
||||
|
||||
def show_event(self):
|
||||
self._scroller.show_event()
|
||||
self._speed_limit_policy.show_description(True)
|
||||
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from collections.abc import Callable
|
||||
from enum import IntEnum
|
||||
|
||||
import pyray as rl
|
||||
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.cruise_sub_layouts.speed_limit_policy import SpeedLimitPolicyLayout
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Mode as SpeedLimitMode
|
||||
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import OffsetType as SpeedLimitOffsetType
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.sunnypilot.widgets import get_highlighted_description
|
||||
from openpilot.system.ui.sunnypilot.widgets.list_view import multiple_button_item_sp, option_item_sp, simple_button_item_sp, LineSeparatorSP
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.network import NavButton
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
|
||||
SPEED_LIMIT_MODE_BUTTONS = [tr("Off"), tr("Info"), tr("Warning"), tr("Assist")]
|
||||
SPEED_LIMIT_OFFSET_TYPE_BUTTONS = [tr("None"), tr("Fixed"), tr("%")]
|
||||
|
||||
SPEED_LIMIT_MODE_DESCRIPTIONS = [
|
||||
tr("Off: Disables the Speed Limit functions."),
|
||||
tr("Information: Displays the current road's speed limit."),
|
||||
tr("Warning: Provides a warning when exceeding the current road's speed limit."),
|
||||
tr("Assist: Adjusts the vehicle's cruise speed based on the current road's speed limit when operating the +/- buttons."),
|
||||
]
|
||||
|
||||
SPEED_LIMIT_OFFSET_DESCRIPTIONS = [
|
||||
tr("None: No Offset"),
|
||||
tr("Fixed: Adds a fixed offset [Speed Limit + Offset]"),
|
||||
tr("Percent: Adds a percent offset [Speed Limit + (Offset % Speed Limit)]"),
|
||||
]
|
||||
|
||||
|
||||
class PanelType(IntEnum):
|
||||
SETTINGS = 0
|
||||
POLICY = 1
|
||||
|
||||
|
||||
class SpeedLimitSettingsLayout(Widget):
|
||||
def __init__(self, back_btn_callback: Callable):
|
||||
super().__init__()
|
||||
self._current_panel = PanelType.SETTINGS
|
||||
|
||||
self._back_button = NavButton(tr("Back"))
|
||||
self._back_button.set_click_callback(back_btn_callback)
|
||||
|
||||
self._policy_layout = SpeedLimitPolicyLayout(lambda: self._set_current_panel(PanelType.SETTINGS))
|
||||
|
||||
items = self._initialize_items()
|
||||
self._scroller = Scroller(items, line_separator=False, spacing=0)
|
||||
|
||||
def _initialize_items(self):
|
||||
self._speed_limit_mode = multiple_button_item_sp(
|
||||
title=lambda: tr("Speed Limit"),
|
||||
description=self._get_mode_description,
|
||||
buttons=SPEED_LIMIT_MODE_BUTTONS,
|
||||
param="SpeedLimitMode",
|
||||
button_width=380,
|
||||
)
|
||||
|
||||
self._source_button = simple_button_item_sp(
|
||||
button_text=lambda: tr("Customize Source"),
|
||||
button_width=720,
|
||||
callback=lambda: self._set_current_panel(PanelType.POLICY)
|
||||
)
|
||||
|
||||
self._speed_limit_offset_type = multiple_button_item_sp(
|
||||
title=lambda: tr("Speed Limit Offset"),
|
||||
description="",
|
||||
buttons=SPEED_LIMIT_OFFSET_TYPE_BUTTONS,
|
||||
param="SpeedLimitOffsetType",
|
||||
button_width=450,
|
||||
)
|
||||
|
||||
self._speed_limit_value_offset = option_item_sp(
|
||||
title="",
|
||||
param="SpeedLimitValueOffset",
|
||||
min_value=-30,
|
||||
max_value=30,
|
||||
description=self._get_offset_description,
|
||||
label_callback=self._get_offset_label,
|
||||
)
|
||||
|
||||
items = [
|
||||
self._speed_limit_mode,
|
||||
LineSeparatorSP(40),
|
||||
self._source_button,
|
||||
LineSeparatorSP(40),
|
||||
self._speed_limit_offset_type,
|
||||
self._speed_limit_value_offset
|
||||
]
|
||||
return items
|
||||
|
||||
def _set_current_panel(self, panel: PanelType):
|
||||
self._current_panel = panel
|
||||
if panel == PanelType.POLICY:
|
||||
self._policy_layout.show_event()
|
||||
|
||||
@staticmethod
|
||||
def _get_mode_description():
|
||||
return get_highlighted_description(ui_state.params, "SpeedLimitMode", SPEED_LIMIT_MODE_DESCRIPTIONS)
|
||||
|
||||
@staticmethod
|
||||
def _get_offset_description():
|
||||
return get_highlighted_description(ui_state.params, "SpeedLimitOffsetType", SPEED_LIMIT_OFFSET_DESCRIPTIONS)
|
||||
|
||||
@staticmethod
|
||||
def _get_offset_label(value):
|
||||
offset_type = int(ui_state.params.get("SpeedLimitOffsetType", return_default=True))
|
||||
unit = tr("km/h") if ui_state.is_metric else tr("mph")
|
||||
|
||||
if offset_type == int(SpeedLimitOffsetType.percentage):
|
||||
return f"{value}%"
|
||||
elif offset_type == int(SpeedLimitOffsetType.fixed):
|
||||
return f"{value} {unit}"
|
||||
return str(value)
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
|
||||
speed_limit_mode_param = ui_state.params.get("SpeedLimitMode", return_default=True)
|
||||
if ui_state.CP is not None and ui_state.CP_SP is not None:
|
||||
brand = ui_state.CP.brand
|
||||
has_long = ui_state.has_longitudinal_control
|
||||
has_icbm = ui_state.has_icbm
|
||||
|
||||
"""
|
||||
Speed Limit Assist is available when:
|
||||
- has_long or has_icbm, and
|
||||
- is not a release branch or not a disallowed brand, and
|
||||
- is not always disallwed
|
||||
"""
|
||||
sla_disallow_in_release = brand == "tesla" and ui_state.is_sp_release
|
||||
sla_always_disallow = brand == "rivian"
|
||||
sla_available = (has_long or has_icbm) and not sla_disallow_in_release and not sla_always_disallow
|
||||
|
||||
if not sla_available and speed_limit_mode_param == int(SpeedLimitMode.assist):
|
||||
ui_state.params.put("SpeedLimitMode", int(SpeedLimitMode.warning))
|
||||
|
||||
else:
|
||||
sla_available = False
|
||||
|
||||
if not sla_available:
|
||||
self._speed_limit_mode.action_item.set_enabled_buttons({
|
||||
int(SpeedLimitMode.off),
|
||||
int(SpeedLimitMode.information),
|
||||
int(SpeedLimitMode.warning),
|
||||
})
|
||||
else:
|
||||
self._speed_limit_mode.action_item.set_enabled_buttons(None)
|
||||
|
||||
offset_type = ui_state.params.get("SpeedLimitOffsetType", return_default=True)
|
||||
self._speed_limit_value_offset.set_visible(offset_type != int(SpeedLimitOffsetType.off))
|
||||
|
||||
def _render(self, rect):
|
||||
if self._current_panel == PanelType.POLICY:
|
||||
self._policy_layout.render(rect)
|
||||
return
|
||||
|
||||
self._back_button.set_position(self._rect.x, self._rect.y + 20)
|
||||
self._back_button.render()
|
||||
|
||||
content_rect = rl.Rectangle(rect.x, rect.y + self._back_button.rect.height + 40, rect.width, rect.height - self._back_button.rect.height - 40)
|
||||
self._scroller.render(content_rect)
|
||||
|
||||
def show_event(self):
|
||||
self._current_panel = PanelType.SETTINGS
|
||||
self._scroller.show_event()
|
||||
self._speed_limit_mode.show_description(True)
|
||||
|
||||
def hide_event(self):
|
||||
self._current_panel = PanelType.SETTINGS
|
||||
self._scroller.hide_event()
|
||||
@@ -72,6 +72,15 @@ class SteeringLayout(Widget):
|
||||
description="",
|
||||
label_callback=lambda speed: f'{speed} {"km/h" if ui_state.is_metric else "mph"}',
|
||||
)
|
||||
self._blinker_reengage_delay = option_item_sp(
|
||||
param="BlinkerLateralReengageDelay",
|
||||
title=lambda: tr("Post-Blinker Delay"),
|
||||
min_value=0,
|
||||
max_value=10,
|
||||
value_change_step=1,
|
||||
description=lambda: tr("Delay before lateral control resumes after the turn signal ends."),
|
||||
label_callback=lambda delay: f'{delay} {"s"}'
|
||||
)
|
||||
self._torque_control_toggle = toggle_item_sp(
|
||||
param="EnforceTorqueControl",
|
||||
title=lambda: tr("Enforce Torque Lateral Control"),
|
||||
@@ -96,6 +105,7 @@ class SteeringLayout(Widget):
|
||||
LineSeparatorSP(40),
|
||||
self._blinker_control_toggle,
|
||||
self._blinker_control_options,
|
||||
self._blinker_reengage_delay,
|
||||
LineSeparatorSP(40),
|
||||
self._torque_control_toggle,
|
||||
self._torque_customization_button,
|
||||
@@ -128,6 +138,7 @@ class SteeringLayout(Widget):
|
||||
self._mads_toggle.action_item.set_enabled(ui_state.is_offroad())
|
||||
self._mads_settings_button.action_item.set_enabled(ui_state.is_offroad() and self._mads_toggle.action_item.get_state())
|
||||
self._blinker_control_options.set_visible(self._blinker_control_toggle.action_item.get_state())
|
||||
self._blinker_reengage_delay.set_visible(self._blinker_control_toggle.action_item.get_state())
|
||||
|
||||
enforce_torque_enabled = self._torque_control_toggle.action_item.get_state()
|
||||
nnlc_enabled = self._nnlc_toggle.action_item.get_state()
|
||||
|
||||
@@ -9,6 +9,7 @@ import pyray as rl
|
||||
|
||||
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.network import NavButton
|
||||
@@ -112,7 +113,7 @@ class MadsSettingsLayout(Widget):
|
||||
if self._mads_limited_settings():
|
||||
ui_state.params.remove("MadsMainCruiseAllowed")
|
||||
ui_state.params.put_bool("MadsUnifiedEngagementMode", True)
|
||||
ui_state.params.put("MadsSteeringMode", 2)
|
||||
ui_state.params.put("MadsSteeringMode", MadsSteeringModeOnBrake.DISENGAGE)
|
||||
|
||||
self._main_cruise_toggle.action_item.set_enabled(False)
|
||||
self._main_cruise_toggle.action_item.set_state(False)
|
||||
@@ -122,9 +123,9 @@ class MadsSettingsLayout(Widget):
|
||||
self._unified_engagement_toggle.action_item.set_state(True)
|
||||
self._unified_engagement_toggle.set_description("<b>" + DEFAULT_TO_ON + "</b><br>" + MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC)
|
||||
|
||||
self._steering_mode.action_item.set_enabled(False)
|
||||
self._steering_mode.set_description(STATUS_DISENGAGE_ONLY)
|
||||
self._steering_mode.action_item.set_selected_button(2)
|
||||
self._steering_mode.action_item.set_selected_button(MadsSteeringModeOnBrake.DISENGAGE)
|
||||
self._steering_mode.action_item.set_enabled_buttons({MadsSteeringModeOnBrake.DISENGAGE})
|
||||
else:
|
||||
self._main_cruise_toggle.action_item.set_enabled(True)
|
||||
self._main_cruise_toggle.set_description(MADS_MAIN_CRUISE_BASE_DESC)
|
||||
@@ -133,3 +134,4 @@ class MadsSettingsLayout(Widget):
|
||||
self._unified_engagement_toggle.set_description(MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC)
|
||||
|
||||
self._steering_mode.action_item.set_enabled(True)
|
||||
self._steering_mode.action_item.set_enabled_buttons(None)
|
||||
|
||||
@@ -39,6 +39,9 @@ class UIStateSP:
|
||||
self.onroad_brightness_timer: int = 0
|
||||
self.custom_interactive_timeout: int = self.params.get("InteractivityTimeout", return_default=True)
|
||||
self.reset_onroad_sleep_timer()
|
||||
self.CP_SP: custom.CarParamsSP | None = None
|
||||
self.has_icbm: bool = False
|
||||
self.is_sp_release: bool = self.params.get_bool("IsReleaseSpBranch")
|
||||
|
||||
def update(self) -> None:
|
||||
if self.sunnylink_enabled:
|
||||
@@ -121,6 +124,7 @@ class UIStateSP:
|
||||
CP_SP_bytes = self.params.get("CarParamsSPPersistent")
|
||||
if CP_SP_bytes is not None:
|
||||
self.CP_SP = messaging.log_from_bytes(CP_SP_bytes, custom.CarParamsSP)
|
||||
self.has_icbm = self.CP_SP.intelligentCruiseButtonManagementAvailable and self.params.get_bool("IntelligentCruiseButtonManagement")
|
||||
self.active_bundle = self.params.get("ModelManager_ActiveBundle")
|
||||
self.blindspot = self.params.get_bool("BlindSpot")
|
||||
self.chevron_metrics = self.params.get("ChevronInfo")
|
||||
@@ -138,15 +142,14 @@ class UIStateSP:
|
||||
self.torque_bar = self.params.get_bool("TorqueBar")
|
||||
self.true_v_ego_ui = self.params.get_bool("TrueVEgoUI")
|
||||
self.turn_signals = self.params.get_bool("ShowTurnSignals")
|
||||
self.boot_offroad_mode = self.params.get("DeviceBootMode", return_default=True)
|
||||
|
||||
|
||||
class DeviceSP:
|
||||
def __init__(self):
|
||||
self._params = Params()
|
||||
|
||||
def _set_awake(self, on: bool):
|
||||
if on and self._params.get("DeviceBootMode", return_default=True) == 1:
|
||||
self._params.put_bool("OffroadMode", True)
|
||||
@staticmethod
|
||||
def _set_awake(on: bool, _ui_state):
|
||||
if _ui_state.boot_offroad_mode == 1 and not on:
|
||||
_ui_state.params.put_bool("OffroadMode", True)
|
||||
|
||||
@staticmethod
|
||||
def set_onroad_brightness(_ui_state, awake: bool, cur_brightness: float) -> float:
|
||||
|
||||
@@ -321,7 +321,7 @@ class Device(DeviceSP):
|
||||
|
||||
def _set_awake(self, on: bool):
|
||||
if on != self._awake:
|
||||
DeviceSP._set_awake(self, on)
|
||||
DeviceSP._set_awake(on, ui_state)
|
||||
self._awake = on
|
||||
cloudlog.debug(f"setting display power {int(on)}")
|
||||
HARDWARE.set_display_power(on)
|
||||
|
||||
@@ -17,13 +17,16 @@ class BlinkerPauseLateral:
|
||||
self.enabled = self.params.get_bool("BlinkerPauseLateralControl")
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.min_speed = 0
|
||||
self.reengage_delay = 0
|
||||
self.blinker_off_timer = 0.0
|
||||
|
||||
def get_params(self) -> None:
|
||||
self.enabled = self.params.get_bool("BlinkerPauseLateralControl")
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.min_speed = self.params.get("BlinkerMinLateralControlSpeed")
|
||||
self.min_speed = self.params.get("BlinkerMinLateralControlSpeed", return_default=True)
|
||||
self.reengage_delay = self.params.get("BlinkerLateralReengageDelay", return_default=True)
|
||||
|
||||
def update(self, CS: car.CarState) -> bool:
|
||||
def update(self, CS: car.CarState, DT_CTRL: float = 0.01) -> bool:
|
||||
if not self.enabled:
|
||||
return False
|
||||
|
||||
@@ -31,4 +34,11 @@ class BlinkerPauseLateral:
|
||||
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
min_speed_ms = self.min_speed * speed_factor
|
||||
|
||||
return bool(one_blinker and CS.vEgo < min_speed_ms)
|
||||
below_speed = CS.vEgo < min_speed_ms
|
||||
|
||||
if one_blinker and below_speed:
|
||||
self.blinker_off_timer = self.reengage_delay
|
||||
elif self.blinker_off_timer > 0:
|
||||
self.blinker_off_timer -= DT_CTRL
|
||||
|
||||
return bool((one_blinker and below_speed) or self.blinker_off_timer > 0)
|
||||
|
||||
@@ -20,6 +20,8 @@ class TestBlinkerPauseLateral:
|
||||
self.blinker_pause_lateral.enabled = True
|
||||
self.blinker_pause_lateral.is_metric = False
|
||||
self.blinker_pause_lateral.min_speed = 20 # MPH
|
||||
self.blinker_pause_lateral.reengage_delay = 0
|
||||
self.blinker_pause_lateral.blinker_off_timer = 0.0
|
||||
|
||||
self.CS = car.CarState.new_message()
|
||||
self.CS.vEgo = 0
|
||||
@@ -46,6 +48,18 @@ class TestBlinkerPauseLateral:
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_reengage_delay(self):
|
||||
self.blinker_pause_lateral.reengage_delay = 2 # seconds
|
||||
self.CS.vEgo = 4.5 # ~10 MPH
|
||||
|
||||
expected_results = {
|
||||
(False, False): True,
|
||||
(True, False): True,
|
||||
(False, True): True,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_above_min_speed_blinker(self):
|
||||
self.CS.vEgo = 13.4 # ~30 MPH
|
||||
|
||||
|
||||
@@ -93,6 +93,10 @@
|
||||
"title": "[TIZI/TICI only] Blind Spot Detection",
|
||||
"description": "Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported."
|
||||
},
|
||||
"BlinkerLateralReengageDelay": {
|
||||
"title": "Post-Blinker Delay",
|
||||
"description": "Delay before lateral control resumes after the turn signal ends."
|
||||
},
|
||||
"BlinkerMinLateralControlSpeed": {
|
||||
"title": "Blinker Min Lateral Control Speed",
|
||||
"description": ""
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
|
||||
|
||||
def get_highlighted_description(params, param_name: str, descriptions: list[str]) -> str:
|
||||
index = int(params.get(param_name, return_default=True))
|
||||
lines = []
|
||||
for i, desc in enumerate(descriptions):
|
||||
if i == index:
|
||||
lines.append(f"<b>{desc}</b>")
|
||||
else:
|
||||
lines.append(f"{desc}")
|
||||
|
||||
return "<br>".join(lines)
|
||||
|
||||
@@ -134,6 +134,10 @@ class MultipleButtonActionSP(MultipleButtonAction):
|
||||
if self.param_key:
|
||||
self.selected_button = int(self.params.get(self.param_key, return_default=True))
|
||||
self._anim_x: float | None = None
|
||||
self.enabled_buttons: set[int] | None = None
|
||||
|
||||
def set_enabled_buttons(self, indices: set[int] | None):
|
||||
self.enabled_buttons = indices
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
|
||||
@@ -171,10 +175,31 @@ class MultipleButtonActionSP(MultipleButtonAction):
|
||||
text_x = button_x + (self.button_width - text_size.x) / 2
|
||||
text_y = button_y + (style.BUTTON_HEIGHT - text_size.y) / 2
|
||||
|
||||
rl.draw_text_ex(self._font, text, rl.Vector2(text_x, text_y), 40, 0, text_color)
|
||||
# Check individual button enabled state
|
||||
is_button_enabled = self.enabled and (self.enabled_buttons is None or i in self.enabled_buttons)
|
||||
current_text_color = text_color if is_button_enabled else style.MBC_DISABLED
|
||||
|
||||
rl.draw_text_ex(self._font, text, rl.Vector2(text_x, text_y), 40, 0, current_text_color)
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos: MousePos):
|
||||
MultipleButtonAction._handle_mouse_release(self, mouse_pos)
|
||||
# Override parent method to check individual button enabled state
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
button_y = self._rect.y + (self._rect.height - style.BUTTON_HEIGHT) / 2
|
||||
for i, _ in enumerate(self.buttons):
|
||||
button_x = self._rect.x + i * self.button_width
|
||||
button_rect = rl.Rectangle(button_x, button_y, self.button_width, style.BUTTON_HEIGHT)
|
||||
|
||||
if rl.check_collision_point_rec(mouse_pos, button_rect):
|
||||
# Check if this specific button is enabled
|
||||
if self.enabled_buttons is not None and i not in self.enabled_buttons:
|
||||
return
|
||||
|
||||
self.selected_button = i
|
||||
if self.callback:
|
||||
self.callback(i)
|
||||
|
||||
if self.param_key:
|
||||
self.params.put(self.param_key, self.selected_button)
|
||||
|
||||
@@ -212,6 +237,12 @@ class ListItemSP(ListItem):
|
||||
content_width = int(self._rect.width - style.ITEM_PADDING * 2)
|
||||
self._rect.height = self.get_item_height(self._font, content_width)
|
||||
|
||||
def set_parent_rect(self, parent_rect: rl.Rectangle) -> None:
|
||||
super().set_parent_rect(parent_rect)
|
||||
if self.description_visible:
|
||||
content_width = int(self._rect.width - style.ITEM_PADDING * 2)
|
||||
self._rect.height = self.get_item_height(self._font, content_width)
|
||||
|
||||
def get_item_height(self, font: rl.Font, max_width: int) -> float:
|
||||
height = super().get_item_height(font, max_width)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user