""" 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 enum import Enum 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 from openpilot.system.ui.lib.application import gui_app OpenpilotState = log.SelfdriveState.OpenpilotState MADSState = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState ONROAD_BRIGHTNESS_TIMER_PAUSED = -1 class OnroadTimerStatus(Enum): NONE = 0 PAUSE = 1 RESUME = 2 class UIStateSP: def __init__(self): self.params = Params() self.CP_SP: custom.CarParamsSP | None = None self.has_icbm: bool = False self.is_sp_release: bool = self.params.get_bool("IsReleaseSpBranch") self.sm_services_ext = [ "modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP", "gpsLocation", "liveTorqueParameters", "carStateSP", "liveMapDataSP", "carParamsSP", "liveDelay" ] self.sunnylink_state = SunnylinkState() self.onroad_brightness_timer: int = 0 self.custom_interactive_timeout: int = 0 self._sp_initialized: bool = False def update(self) -> None: if self.sunnylink_enabled: self.sunnylink_state.start() else: self.sunnylink_state.stop() def onroad_brightness_handle_alerts(self, _ui_state, alert): if _ui_state.sm.recv_frame["carState"] < _ui_state.started_frame: return has_alert = _ui_state.started and self.onroad_brightness != OnroadBrightness.AUTO and alert is not None self.update_onroad_brightness(has_alert) if has_alert: self.reset_onroad_sleep_timer() def update_onroad_brightness(self, has_alert: bool) -> None: if has_alert: return if self.onroad_brightness_timer > 0: self.onroad_brightness_timer -= 1 def reset_onroad_sleep_timer(self, timer_status: OnroadTimerStatus = OnroadTimerStatus.NONE) -> None: # Toggling from active state to inactive if timer_status == OnroadTimerStatus.PAUSE and self.onroad_brightness_timer != ONROAD_BRIGHTNESS_TIMER_PAUSED: self.onroad_brightness_timer = ONROAD_BRIGHTNESS_TIMER_PAUSED # Toggling from a previously inactive state or resetting an active timer elif (self.onroad_brightness_timer_param >= 0 and self.onroad_brightness != OnroadBrightness.AUTO and self.onroad_brightness_timer != ONROAD_BRIGHTNESS_TIMER_PAUSED) or timer_status == OnroadTimerStatus.RESUME: if self.onroad_brightness == OnroadBrightness.AUTO_DARK: self.onroad_brightness_timer = 15 * gui_app.target_fps else: self.onroad_brightness_timer = self.onroad_brightness_timer_param * gui_app.target_fps @property def onroad_brightness_timer_expired(self) -> bool: return self.onroad_brightness != OnroadBrightness.AUTO and self.onroad_brightness_timer == 0 @property def auto_onroad_brightness(self) -> bool: return self.onroad_brightness in (OnroadBrightness.AUTO, OnroadBrightness.AUTO_DARK) @staticmethod def update_status(ss, ss_sp, onroad_evt) -> str: state = ss.state mads = ss_sp.mads mads_state = mads.state if state == OpenpilotState.preEnabled: return "override" if state == OpenpilotState.overriding: if not mads.available: return "override" if any(e.overrideLongitudinal for e in onroad_evt): return "override" if mads_state in (MADSState.paused, MADSState.overriding): return "override" # MADS specific statuses if not mads.available: return "engaged" if ss.enabled else "disengaged" if not mads.enabled and not ss.enabled: return "disengaged" if mads.enabled and ss.enabled: return "engaged" if mads.enabled: return "lat_only" if ss.enabled: return "long_only" return "disengaged" def update_params(self) -> None: 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._enforce_constraints() self.active_bundle = self.params.get("ModelManager_ActiveBundle") self.blindspot = self.params.get_bool("BlindSpot") self.chevron_metrics = self.params.get("ChevronInfo") self.custom_interactive_timeout = self.params.get("InteractivityTimeout", return_default=True) self.developer_ui = self.params.get("DevUIInfo") self.hide_v_ego_ui = self.params.get_bool("HideVEgoUI") self.onroad_brightness = int(float(self.params.get("OnroadScreenOffBrightness", return_default=True))) self.onroad_brightness_timer_param = self.params.get("OnroadScreenOffTimer", return_default=True) self.rainbow_path = self.params.get_bool("RainbowMode") self.road_name_toggle = self.params.get_bool("RoadNameToggle") self.rocket_fuel = self.params.get_bool("RocketFuel") self.speed_limit_mode = self.params.get("SpeedLimitMode", return_default=True) self.standstill_timer = self.params.get_bool("StandstillTimer") self.sunnylink_enabled = self.params.get_bool("SunnylinkEnabled") self.torque_bar = self.params.get_bool("TorqueBar") self.enforce_torque_control = self.params.get_bool("EnforceTorqueControl") self.custom_torque_params = self.params.get_bool("CustomTorqueParams") self.torque_override_enabled = self.params.get_bool("TorqueParamsOverrideEnabled") self.torque_override_lat_accel_factor = float(self.params.get("TorqueParamsOverrideLatAccelFactor", return_default=True)) self.torque_override_friction = float(self.params.get("TorqueParamsOverrideFriction", return_default=True)) 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) self.always_offroad = self.params.get_bool("OffroadMode") if not self._sp_initialized: self._sp_initialized = True self.reset_onroad_sleep_timer() def _enforce_constraints(self) -> None: has_long = self.has_longitudinal_control CP = self.CP 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 if not CP.alphaLongitudinalAvailable: 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 or DEC if not has_long: self.params.remove("ExperimentalMode") self.params.remove("DynamicExperimentalControl") # 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") self.has_icbm = False else: self.params.remove("IntelligentCruiseButtonManagement") self.has_icbm = False # Cruise features requiring longitudinal or ICBM if not (has_long or self.has_icbm): self.params.remove("CustomAccIncrementsEnabled") self.params.remove("SmartCruiseControlVision") self.params.remove("SmartCruiseControlMap") class DeviceSP: @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: if not awake or not _ui_state.started: return cur_brightness if _ui_state.onroad_brightness_timer != 0: if _ui_state.onroad_brightness == OnroadBrightness.AUTO_DARK: return max(30.0, cur_brightness) return cur_brightness # 0: Auto (Default), 1: Auto (Dark), 2: Screen Off if _ui_state.onroad_brightness == OnroadBrightness.AUTO: return cur_brightness if _ui_state.onroad_brightness == OnroadBrightness.AUTO_DARK: return cur_brightness if _ui_state.onroad_brightness == OnroadBrightness.SCREEN_OFF: return 0.0 # 3-22: 5% - 100% return float((_ui_state.onroad_brightness - 2) * 5) @staticmethod def set_min_onroad_brightness(_ui_state, min_brightness: int) -> int: if _ui_state.onroad_brightness == OnroadBrightness.AUTO_DARK: min_brightness = 10 return min_brightness @staticmethod def wake_from_dimmed_onroad_brightness(_ui_state, evs) -> None: if _ui_state.started and (_ui_state.onroad_brightness_timer_expired or _ui_state.onroad_brightness == OnroadBrightness.AUTO_DARK): if any(ev.left_down for ev in evs): if _ui_state.onroad_brightness_timer_expired: gui_app.mouse_events.clear() _ui_state.reset_onroad_sleep_timer()