diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc index 97b78399d..2469bfa59 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc @@ -21,22 +21,11 @@ MadsSettings::MadsSettings(QWidget *parent) : QWidget(parent) { ListWidget *list = new ListWidget(this, false); // Main cruise - madsMainCruiseToggle = new ParamControl( - "MadsMainCruiseAllowed", - tr("Toggle with Main Cruise"), - tr("Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement."), - ""); + madsMainCruiseToggle = new ParamControl("MadsMainCruiseAllowed", tr("Toggle with Main Cruise"), "", ""); list->addItem(madsMainCruiseToggle); // Unified Engagement Mode - madsUnifiedEngagementModeToggle = new ParamControl( - "MadsUnifiedEngagementMode", - tr("Unified Engagement Mode (UEM)"), - QString("%1
" - "

%2

") - .arg(tr("Engage lateral and longitudinal control with cruise control engagement.")) - .arg(tr("Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off.")), - ""); + madsUnifiedEngagementModeToggle = new ParamControl("MadsUnifiedEngagementMode", tr("Unified Engagement Mode (UEM)"), "", ""); list->addItem(madsUnifiedEngagementModeToggle); // Steering Mode On Brake @@ -62,8 +51,51 @@ void MadsSettings::updateToggles(bool _offroad) { std::clamp(mads_steering_mode_param, static_cast(MadsSteeringMode::REMAIN_ACTIVE), static_cast(MadsSteeringMode::DISENGAGE)) ); - madsSteeringMode->setEnabled(_offroad); - madsSteeringMode->setDescription(madsSteeringModeDescription(steering_mode)); + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (isBrandInList(CP.getBrand(), mads_limited_settings_brands)) { + params.remove("MadsMainCruiseAllowed"); + params.putBool("MadsUnifiedEngagementMode", true); + params.put("MadsSteeringMode", std::to_string(static_cast(MadsSteeringMode::DISENGAGE))); + + madsMainCruiseToggle->setEnabled(false); + madsMainCruiseToggle->setDescription(madsDescriptionBuilder(DEFAULT_TO_OFF, MADS_MAIN_CRUISE_BASE_DESC)); + madsMainCruiseToggle->showDescription(); + + madsUnifiedEngagementModeToggle->setEnabled(false); + madsUnifiedEngagementModeToggle->setDescription(madsDescriptionBuilder(DEFAULT_TO_ON, MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC)); + madsUnifiedEngagementModeToggle->showDescription(); + + madsSteeringModeValues = convertMadsSteeringModeValues({MadsSteeringMode::DISENGAGE}); + madsSteeringMode->setDescription(madsDescriptionBuilder(STATUS_DISENGAGE_ONLY, madsSteeringModeDescription(steering_mode))); + } else { + madsMainCruiseToggle->setEnabled(true); + madsMainCruiseToggle->setDescription(MADS_MAIN_CRUISE_BASE_DESC); + + madsUnifiedEngagementModeToggle->setEnabled(true); + madsUnifiedEngagementModeToggle->setDescription(MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC); + + madsSteeringModeValues = convertMadsSteeringModeValues(getMadsSteeringModeValues()); + madsSteeringMode->setDescription(madsSteeringModeDescription(steering_mode)); + } + } else { + madsMainCruiseToggle->setEnabled(false); + madsMainCruiseToggle->setDescription(madsDescriptionBuilder(STATUS_CHECK_COMPATIBILITY, MADS_MAIN_CRUISE_BASE_DESC)); + madsMainCruiseToggle->showDescription(); + + madsUnifiedEngagementModeToggle->setEnabled(false); + madsUnifiedEngagementModeToggle->setDescription(madsDescriptionBuilder(STATUS_CHECK_COMPATIBILITY, MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC)); + madsUnifiedEngagementModeToggle->showDescription(); + + madsSteeringModeValues = {}; + madsSteeringMode->setDescription(madsDescriptionBuilder(STATUS_CHECK_COMPATIBILITY, madsSteeringModeDescription(steering_mode))); + } + + madsSteeringMode->setEnableSelectedButtons(_offroad, madsSteeringModeValues); madsSteeringMode->showDescription(); offroad = _offroad; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h index 07ca07834..405a2613d 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h @@ -12,6 +12,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +const std::vector mads_limited_settings_brands = {"rivian"}; + enum class MadsSteeringMode { REMAIN_ACTIVE = 0, PAUSE = 1, @@ -46,15 +48,44 @@ private: ParamControl *madsUnifiedEngagementModeToggle; ButtonParamControl *madsSteeringMode; + std::vector madsSteeringModeValues = {}; + + const QString MADS_MAIN_CRUISE_BASE_DESC = tr("Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement."); + const QString MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC = QString("%1
" + "

%2

") + .arg(tr("Engage lateral and longitudinal control with cruise control engagement.")) + .arg(tr("Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off.")); + + const QString STATUS_CHECK_COMPATIBILITY = tr("Start the vehicle to check vehicle compatibility."); + const QString DEFAULT_TO_OFF = tr("This feature defaults to OFF, and does not allow selection due to vehicle limitations."); + const QString DEFAULT_TO_ON = tr("This feature defaults to ON, and does not allow selection due to vehicle limitations."); + const QString STATUS_DISENGAGE_ONLY = tr("This platform only supports Disengage mode due to vehicle limitations."); + static const std::vector &madsSteeringModeOptions() { static const std::vector options = { - {MadsSteeringMode::REMAIN_ACTIVE, tr("Remain Active"), tr("Remain Active: ALC will remain active when the brake pedal is pressed.")}, - {MadsSteeringMode::PAUSE, tr("Pause"), tr("Pause: ALC will pause when the brake pedal is pressed.")}, - {MadsSteeringMode::DISENGAGE, tr("Disengage"), tr("Disengage: ALC will disengage when the brake pedal is pressed.")}, + {MadsSteeringMode::REMAIN_ACTIVE, tr("Remain Active"), tr("Remain Active: ALC will remain active when the brake pedal is pressed.")}, + {MadsSteeringMode::PAUSE, tr("Pause"), tr("Pause: ALC will pause when the brake pedal is pressed.")}, + {MadsSteeringMode::DISENGAGE, tr("Disengage"), tr("Disengage: ALC will disengage when the brake pedal is pressed.")}, }; return options; } + static std::vector getMadsSteeringModeValues() { + std::vector values; + for (const auto& option : madsSteeringModeOptions()) { + values.push_back(option.mode); + } + return values; + } + + static std::vector convertMadsSteeringModeValues(const std::vector &modes) { + std::vector values; + for (const auto& mode : modes) { + values.push_back(static_cast(mode)); + } + return values; + } + static std::vector madsSteeringModeTexts() { std::vector texts; for (const auto& option : madsSteeringModeOptions()) { @@ -63,7 +94,7 @@ private: return texts; } - static QString madsSteeringModeDescription(const MadsSteeringMode mode) { + static QString madsSteeringModeDescription(const MadsSteeringMode mode = MadsSteeringMode::REMAIN_ACTIVE) { QString base_desc = tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot."); QString result = base_desc + "

"; @@ -77,4 +108,8 @@ private: return result; } + + static QString madsDescriptionBuilder(const QString &custom_description, const QString &base_description) { + return "" + custom_description + "

" + base_description; + } }; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc index 75cd13664..0beae6e24 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc @@ -122,6 +122,21 @@ void LateralPanel::updateToggles(bool _offroad) { toggle->setEnabled(_offroad); } + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (isBrandInList(CP.getBrand(), mads_limited_settings_brands)) { + madsToggle->setDescription(descriptionBuilder(STATUS_MADS_SETTINGS_LIMITED_COMPATIBILITY, MADS_BASE_DESC)); + } else { + madsToggle->setDescription(descriptionBuilder(STATUS_MADS_SETTINGS_FULL_COMPATIBILITY, MADS_BASE_DESC)); + } + } else { + madsToggle->setDescription(descriptionBuilder(STATUS_MADS_CHECK_COMPATIBILITY, MADS_BASE_DESC)); + } + madsSettingsButton->setEnabled(madsToggle->isToggled()); offroad = _offroad; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h index 1b77939ee..cf003dfaf 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h @@ -30,6 +30,7 @@ public slots: void updateToggles(bool _offroad); private: + Params params; QStackedLayout* main_layout = nullptr; QWidget* sunnypilotScreen = nullptr; ScrollViewSP *sunnypilotScroller = nullptr; @@ -42,4 +43,14 @@ private: PushButtonSP *laneChangeSettingsButton; LaneChangeSettings *laneChangeWidget = nullptr; NeuralNetworkLateralControl *nnlcToggle = nullptr; + + const QString MADS_BASE_DESC = tr("Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC)."); + + const QString STATUS_MADS_CHECK_COMPATIBILITY = tr("Start the vehicle to check vehicle compatibility."); + const QString STATUS_MADS_SETTINGS_FULL_COMPATIBILITY = tr("This platform supports all MADS settings."); + const QString STATUS_MADS_SETTINGS_LIMITED_COMPATIBILITY = tr("This platform supports limited MADS settings."); + + static QString descriptionBuilder(const QString &custom_description, const QString &base_description) { + return "" + custom_description + "

" + base_description; + } }; diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py index 5f7f9840e..5c7b575d1 100644 --- a/sunnypilot/mads/helpers.py +++ b/sunnypilot/mads/helpers.py @@ -46,12 +46,17 @@ def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, p CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value CP_SP.safetyParam |= HyundaiSafetyFlagsSP.LONG_MAIN_CRUISE_TOGGLEABLE - # MADS is currently not supported in Tesla due to lack of consistent states to engage controls - # TODO-SP: To enable MADS for Tesla, identify consistent signals for MADS toggling - if CP.brand == "tesla": - params.remove("Mads") + # MADS Partial Support + # MADS is currently partially supported for these platforms due to lack of consistent states to engage controls + # Only MadsSteeringModeOnBrake.DISENGAGE is supported for these platforms + mads_partial_support = False + if mads_partial_support: + params.put("MadsSteeringMode", "2") + params.put_bool("MadsUnifiedEngagementMode", True) + params.remove("MadsMainCruiseAllowed") - # MADS is currently not supported in Rivian due to lack of consistent states to engage controls - # TODO-SP: To enable MADS for Rivian, identify consistent signals for MADS toggling - if CP.brand == "rivian": + # MADS Disabled - No Support + # MADS is currently not supported for these platforms due to lack of consistent states to engage controls + # TODO-SP: To enable MADS full support for Rivian/Tesla, identify consistent signals for MADS toggling + if CP.brand in ("rivian", "tesla"): params.remove("Mads") diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 38d38230a..ffe6e6efe 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -33,6 +33,7 @@ class ModularAssistiveDrivingSystem: self.active = False self.available = False self.allow_always = False + self.no_main_cruise = False self.selfdrive = selfdrive self.selfdrive.enabled_prev = False self.state_machine = StateMachine(self) @@ -159,7 +160,7 @@ class ModularAssistiveDrivingSystem: else: self.events_sp.add(EventNameSP.lkasEnable) - if not CS.cruiseState.available: + if not CS.cruiseState.available and not self.no_main_cruise: self.events.remove(EventName.buttonEnable) if self.selfdrive.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasDisable)