diff --git a/selfdrive/ui/sunnypilot/layouts/settings/steering.py b/selfdrive/ui/sunnypilot/layouts/settings/steering.py index 14d840138e..a4c1608a06 100644 --- a/selfdrive/ui/sunnypilot/layouts/settings/steering.py +++ b/selfdrive/ui/sunnypilot/layouts/settings/steering.py @@ -120,20 +120,13 @@ class SteeringLayout(Widget): def _update_state(self): super()._update_state() - torque_allowed = True + # Torque enforcement now handled in UIState.update_params() (shared by C3X + MICI) + torque_allowed = ui_state.CP is not None and ui_state.CP.steerControlType != car.CarParams.SteerControlType.angle if ui_state.CP is not None: mads_main_desc = self._mads_limited_desc if self._mads_settings_layout._mads_limited_settings() else self._mads_full_desc self._mads_toggle.set_description(f"{mads_main_desc}

{self._mads_base_desc}") - - if ui_state.CP.steerControlType == car.CarParams.SteerControlType.angle: - ui_state.params.remove("EnforceTorqueControl") - ui_state.params.remove("NeuralNetworkLateralControl") - torque_allowed = False else: self._mads_toggle.set_description(f"{self._mads_check_compat_desc}

{self._mads_base_desc}") - ui_state.params.remove("EnforceTorqueControl") - ui_state.params.remove("NeuralNetworkLateralControl") - torque_allowed = False 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()) diff --git a/selfdrive/ui/sunnypilot/ui_state.py b/selfdrive/ui/sunnypilot/ui_state.py index 80c05be8bb..d49bbb2486 100644 --- a/selfdrive/ui/sunnypilot/ui_state.py +++ b/selfdrive/ui/sunnypilot/ui_state.py @@ -6,7 +6,7 @@ See the LICENSE.md file in the root directory for more details. """ from enum import Enum -from cereal import messaging, log, custom +from cereal import messaging, log, car, custom from openpilot.common.params import Params from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import OnroadBrightness from openpilot.sunnypilot.sunnylink.sunnylink_state import SunnylinkState @@ -132,6 +132,8 @@ class UIStateSP: 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._enforce_sp_constraints() self.active_bundle = self.params.get("ModelManager_ActiveBundle") self.blindspot = self.params.get_bool("BlindSpot") self.chevron_metrics = self.params.get("ChevronInfo") @@ -152,6 +154,48 @@ class UIStateSP: self.boot_offroad_mode = self.params.get("DeviceBootMode", return_default=True) self._remote_cycle_pending = self.params.get_bool("OnroadCyclePendingRemote") + def _enforce_sp_constraints(self) -> None: + has_long = self.has_longitudinal_control if hasattr(self, 'has_longitudinal_control') else False + has_icbm = self.has_icbm + CP = self.CP if hasattr(self, 'CP') else None + + if CP is not None: + # Angle steering: no torque-based lateral controls + if CP.steerControlType == car.CarParams.SteerControlType.angle: + self.params.remove("EnforceTorqueControl") + self.params.remove("NeuralNetworkLateralControl") + + # Alpha longitudinal: clear if not available or on release branch + if not CP.alphaLongitudinalAvailable or self.params.get_bool("IsReleaseBranch"): + self.params.remove("AlphaLongitudinalEnabled") + + # BSM not available: clear BSM-dependent settings + if not CP.enableBsm: + self.params.remove("AutoLaneChangeBsmDelay") + else: + # No CarParams: clear all car-dependent params as safety default + self.params.remove("EnforceTorqueControl") + self.params.remove("NeuralNetworkLateralControl") + self.params.remove("AlphaLongitudinalEnabled") + + # No longitudinal control: no experimental mode + if not has_long: + self.params.remove("ExperimentalMode") + + # ICBM: clear if not available or if full longitudinal control is active + if self.CP_SP is not None: + if not self.CP_SP.intelligentCruiseButtonManagementAvailable or has_long: + self.params.remove("IntelligentCruiseButtonManagement") + else: + self.params.remove("IntelligentCruiseButtonManagement") + + # Cruise features requiring longitudinal or ICBM + if not (has_long or has_icbm): + self.params.remove("CustomAccIncrementsEnabled") + self.params.remove("DynamicExperimentalControl") + self.params.remove("SmartCruiseControlVision") + self.params.remove("SmartCruiseControlMap") + def check_remote_cycle_pending(self, ui_state_ref) -> None: """Check and handle pending remote onroad cycle when not engaged.""" if not self._remote_cycle_pending or self._remote_cycle_dialog_shown: