from enum import IntEnum from typing import Dict, Union, Callable, Any from cereal import log, car import cereal.messaging as messaging from common.realtime import DT_CTRL from selfdrive.config import Conversions as CV from selfdrive.locationd.calibrationd import MIN_SPEED_FILTER AlertSize = log.ControlsState.AlertSize AlertStatus = log.ControlsState.AlertStatus VisualAlert = car.CarControl.HUDControl.VisualAlert AudibleAlert = car.CarControl.HUDControl.AudibleAlert EventName = car.CarEvent.EventName # Alert priorities class Priority(IntEnum): LOWEST = 0 LOWER = 1 LOW = 2 MID = 3 HIGH = 4 HIGHEST = 5 # Event types class ET: ENABLE = 'enable' PRE_ENABLE = 'preEnable' NO_ENTRY = 'noEntry' WARNING = 'warning' USER_DISABLE = 'userDisable' SOFT_DISABLE = 'softDisable' IMMEDIATE_DISABLE = 'immediateDisable' PERMANENT = 'permanent' # get event name from enum EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()} class Events: def __init__(self): self.events = [] self.static_events = [] self.events_prev = dict.fromkeys(EVENTS.keys(), 0) @property def names(self): return self.events def __len__(self): return len(self.events) def add(self, event_name, static=False): if static: self.static_events.append(event_name) self.events.append(event_name) def clear(self): self.events_prev = {k: (v + 1 if k in self.events else 0) for k, v in self.events_prev.items()} self.events = self.static_events.copy() def any(self, event_type): for e in self.events: if event_type in EVENTS.get(e, {}).keys(): return True return False def create_alerts(self, event_types, callback_args=None): if callback_args is None: callback_args = [] ret = [] for e in self.events: types = EVENTS[e].keys() for et in event_types: if et in types: alert = EVENTS[e][et] if not isinstance(alert, Alert): alert = alert(*callback_args) if DT_CTRL * (self.events_prev[e] + 1) >= alert.creation_delay: alert.alert_type = f"{EVENT_NAME[e]}/{et}" alert.event_type = et ret.append(alert) return ret def add_from_msg(self, events): for e in events: self.events.append(e.name.raw) def to_msg(self): ret = [] for event_name in self.events: event = car.CarEvent.new_message() event.name = event_name for event_type in EVENTS.get(event_name, {}).keys(): setattr(event, event_type, True) ret.append(event) return ret class Alert: def __init__(self, alert_text_1: str, alert_text_2: str, alert_status: log.ControlsState.AlertStatus, alert_size: log.ControlsState.AlertSize, alert_priority: Priority, visual_alert: car.CarControl.HUDControl.VisualAlert, audible_alert: car.CarControl.HUDControl.AudibleAlert, duration_sound: float, duration_hud_alert: float, duration_text: float, alert_rate: float = 0., creation_delay: float = 0.): self.alert_text_1 = alert_text_1 self.alert_text_2 = alert_text_2 self.alert_status = alert_status self.alert_size = alert_size self.alert_priority = alert_priority self.visual_alert = visual_alert self.audible_alert = audible_alert self.duration_sound = duration_sound self.duration_hud_alert = duration_hud_alert self.duration_text = duration_text self.alert_rate = alert_rate self.creation_delay = creation_delay self.start_time = 0. self.alert_type = "" self.event_type = None def __str__(self) -> str: return f"{self.alert_text_1}/{self.alert_text_2} {self.alert_priority} {self.visual_alert} {self.audible_alert}" def __gt__(self, alert2) -> bool: return self.alert_priority > alert2.alert_priority class NoEntryAlert(Alert): def __init__(self, alert_text_2, audible_alert=AudibleAlert.chimeError, visual_alert=VisualAlert.none, duration_hud_alert=2.): super().__init__("openpilot Unavailable", alert_text_2, AlertStatus.normal, AlertSize.mid, Priority.LOW, visual_alert, audible_alert, .4, duration_hud_alert, 3.) class SoftDisableAlert(Alert): def __init__(self, alert_text_2): super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2, AlertStatus.critical, AlertSize.full, Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.), class ImmediateDisableAlert(Alert): def __init__(self, alert_text_2, alert_text_1="TAKE CONTROL IMMEDIATELY"): super().__init__(alert_text_1, alert_text_2, AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.), class EngagementAlert(Alert): def __init__(self, audible_alert=True): super().__init__("", "", AlertStatus.normal, AlertSize.none, Priority.MID, VisualAlert.none, audible_alert, .2, 0., 0.), class NormalPermanentAlert(Alert): def __init__(self, alert_text_1: str, alert_text_2: str, duration_text: float = 0.2): super().__init__(alert_text_1, alert_text_2, AlertStatus.normal, AlertSize.mid if len(alert_text_2) else AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., duration_text), # ********** alert callback functions ********** def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: speed = int(round(CP.minSteerSpeed * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))) unit = "km/h" if metric else "mph" return Alert( "TAKE CONTROL", "Steer Unavailable Below %d %s" % (speed, unit), AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.none, 0., 0.4, .3) def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: speed = int(MIN_SPEED_FILTER * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)) unit = "km/h" if metric else "mph" return Alert( "Calibration in Progress: %d%%" % sm['liveCalibration'].calPerc, "Drive Above %d %s" % (speed, unit), AlertStatus.normal, AlertSize.mid, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2) def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: gps_integrated = sm['pandaState'].pandaType in [log.PandaState.PandaType.uno, log.PandaState.PandaType.dos] return Alert( "Poor GPS reception", "If sky is visible, contact support" if gps_integrated else "Check GPS antenna placement", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=300.) def wrong_car_mode_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: text = "Cruise Mode Disabled" if CP.carName == "honda": text = "Main Switch Off" return NoEntryAlert(text, duration_hud_alert=0.) def startup_fuzzy_fingerprint_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: return Alert( "WARNING: No Exact Match on Car Model", f"Closest Match: {CP.carFingerprint.title()[:40]}", AlertStatus.userPrompt, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.) def joystick_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: axes = sm['testJoystick'].axes gb, steer = list(axes)[:2] if len(axes) else (0., 0.) return Alert( "Joystick Mode", f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .1) EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, bool], Alert]]]] = { # ********** events with no alerts ********** # ********** events only containing alerts displayed in all states ********** EventName.joystickDebug: { ET.WARNING: joystick_alert, ET.PERMANENT: Alert( "Joystick Mode", "", AlertStatus.normal, AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 0.1), }, EventName.controlsInitializing: { ET.NO_ENTRY: NoEntryAlert("Controls Initializing"), }, EventName.startup: { ET.PERMANENT: Alert( "Be ready to take over at any time", "Always keep hands on wheel and eyes on road", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, EventName.startupMaster: { ET.PERMANENT: Alert( "WARNING: This branch is not tested", "Always keep hands on wheel and eyes on road", AlertStatus.userPrompt, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, # Car is recognized, but marked as dashcam only EventName.startupNoControl: { ET.PERMANENT: Alert( "Dashcam mode", "Always keep hands on wheel and eyes on road", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, # Car is not recognized EventName.startupNoCar: { ET.PERMANENT: Alert( "Dashcam mode for unsupported car", "Always keep hands on wheel and eyes on road", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, # openpilot uses the version strings from various ECUs to detect the correct car model. # Usually all ECUs are recognized and an exact match to a car model can be made. Sometimes # one or two ECUs have unrecognized versions, but the others are present in the database. # If openpilot is confident about the match to a car model, it fingerprints anyway. # In this case an alert is thrown since there is a small chance the wrong car was detected # and the user should pay extra attention. # This alert can be prevented by adding all ECU firmware version to openpilot: # https://github.com/commaai/openpilot/wiki/Fingerprinting EventName.startupFuzzyFingerprint: { ET.PERMANENT: startup_fuzzy_fingerprint_alert, }, EventName.startupNoFw: { ET.PERMANENT: Alert( "Car Unrecognized", "Check All Connections", AlertStatus.userPrompt, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, EventName.dashcamMode: { ET.PERMANENT: Alert( "Dashcam Mode", "", AlertStatus.normal, AlertSize.small, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, EventName.invalidLkasSetting: { ET.PERMANENT: Alert( "Stock LKAS is turned on", "Turn off stock LKAS to engage", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, # Some features or cars are marked as community features. If openpilot # detects the use of a community feature it switches to dashcam mode # until these features are allowed using a toggle in settings. EventName.communityFeatureDisallowed: { # LOW priority to overcome Cruise Error ET.PERMANENT: Alert( "openpilot Not Available", "Enable Community Features in Settings to Engage", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, # openpilot doesn't recognize the car. This switches openpilot into a # read-only mode. This can be solved by adding your fingerprint. # See https://github.com/commaai/openpilot/wiki/Fingerprinting for more information EventName.carUnrecognized: { ET.PERMANENT: Alert( "Dashcam Mode", "Car Unrecognized", AlertStatus.normal, AlertSize.mid, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, EventName.stockAeb: { ET.PERMANENT: Alert( "BRAKE!", "Stock AEB: Risk of Collision", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 1., 2., 2.), ET.NO_ENTRY: NoEntryAlert("Stock AEB: Risk of Collision"), }, EventName.stockFcw: { ET.PERMANENT: Alert( "BRAKE!", "Stock FCW: Risk of Collision", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 1., 2., 2.), ET.NO_ENTRY: NoEntryAlert("Stock FCW: Risk of Collision"), }, EventName.fcw: { ET.PERMANENT: Alert( "BRAKE!", "Risk of Collision", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.chimeWarningRepeat, 1., 2., 2.), }, EventName.ldw: { ET.PERMANENT: Alert( "TAKE CONTROL", "Lane Departure Detected", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.ldw, AudibleAlert.chimePrompt, 1., 2., 3.), }, # ********** events only containing alerts that display while engaged ********** EventName.gasPressed: { ET.PRE_ENABLE: Alert( "openpilot will not brake while gas pressed", "", AlertStatus.normal, AlertSize.small, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .0, .0, .1, creation_delay=1.), }, # openpilot tries to learn certain parameters about your car by observing # how the car behaves to steering inputs from both human and openpilot driving. # This includes: # - steer ratio: gear ratio of the steering rack. Steering angle divided by tire angle # - tire stiffness: how much grip your tires have # - angle offset: most steering angle sensors are offset and measure a non zero angle when driving straight # This alert is thrown when any of these values exceed a sanity check. This can be caused by # bad alignment or bad sensor data. If this happens consistently consider creating an issue on GitHub EventName.vehicleModelInvalid: { ET.NO_ENTRY: NoEntryAlert("Vehicle Parameter Identification Failed"), ET.SOFT_DISABLE: SoftDisableAlert("Vehicle Parameter Identification Failed"), ET.WARNING: Alert( "Vehicle Parameter Identification Failed", "", AlertStatus.normal, AlertSize.small, Priority.LOWEST, VisualAlert.steerRequired, AudibleAlert.none, .0, .0, .1), }, EventName.steerTempUnavailableUserOverride: { ET.WARNING: Alert( "Steering Temporarily Unavailable", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 1., 1.), }, EventName.preDriverDistracted: { ET.WARNING: Alert( "KEEP EYES ON ROAD: Driver Distracted", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1), }, EventName.promptDriverDistracted: { ET.WARNING: Alert( "KEEP EYES ON ROAD", "Driver Distracted", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, .1, .1, .1), }, EventName.driverDistracted: { ET.WARNING: Alert( "DISENGAGE IMMEDIATELY", "Driver Distracted", AlertStatus.critical, AlertSize.full, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1), }, EventName.preDriverUnresponsive: { ET.WARNING: Alert( "TOUCH STEERING WHEEL: No Face Detected", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75), }, EventName.promptDriverUnresponsive: { ET.WARNING: Alert( "TOUCH STEERING WHEEL", "Driver Unresponsive", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, .1, .1, .1), }, EventName.driverUnresponsive: { ET.WARNING: Alert( "DISENGAGE IMMEDIATELY", "Driver Unresponsive", AlertStatus.critical, AlertSize.full, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1), }, EventName.driverMonitorLowAcc: { ET.WARNING: Alert( "CHECK DRIVER FACE VISIBILITY", "Driver Monitoring Uncertain", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .4, 0., 1.5), }, EventName.manualRestart: { ET.WARNING: Alert( "TAKE CONTROL", "Resume Driving Manually", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, EventName.resumeRequired: { ET.WARNING: Alert( "STOPPED", "Press Resume to Move", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, EventName.belowSteerSpeed: { ET.WARNING: below_steer_speed_alert, }, EventName.preLaneChangeLeft: { ET.WARNING: Alert( "Steer Left to Start Lane Change", "Monitor Other Vehicles", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75), }, EventName.preLaneChangeRight: { ET.WARNING: Alert( "Steer Right to Start Lane Change", "Monitor Other Vehicles", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75), }, EventName.laneChangeBlocked: { ET.WARNING: Alert( "Car Detected in Blindspot", "Monitor Other Vehicles", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, .1, .1, .1), }, EventName.laneChange: { ET.WARNING: Alert( "Changing Lane", "Monitor Other Vehicles", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1), }, EventName.steerSaturated: { ET.WARNING: Alert( "TAKE CONTROL", "Turn Exceeds Steering Limit", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 1., 1.), }, # Thrown when the fan is driven at >50% but is not rotating EventName.fanMalfunction: { ET.PERMANENT: NormalPermanentAlert("Fan Malfunction", "Contact Support"), }, # Camera is not outputting frames at a constant framerate EventName.cameraMalfunction: { ET.PERMANENT: NormalPermanentAlert("Camera Malfunction", "Contact Support"), }, # Unused EventName.gpsMalfunction: { ET.PERMANENT: NormalPermanentAlert("GPS Malfunction", "Contact Support"), }, # When the GPS position and localizer diverge the localizer is reset to the # current GPS position. This alert is thrown when the localizer is reset # more often than expected. EventName.localizerMalfunction: { ET.PERMANENT: NormalPermanentAlert("Localizer unstable", "Contact Support"), }, # ********** events that affect controls state transitions ********** EventName.pcmEnable: { ET.ENABLE: EngagementAlert(AudibleAlert.chimeEngage), }, EventName.buttonEnable: { ET.ENABLE: EngagementAlert(AudibleAlert.chimeEngage), }, EventName.pcmDisable: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), }, EventName.buttonCancel: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), }, EventName.brakeHold: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"), }, EventName.parkBrake: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.NO_ENTRY: NoEntryAlert("Park Brake Engaged"), }, EventName.pedalPressed: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.NO_ENTRY: NoEntryAlert("Pedal Pressed During Attempt", visual_alert=VisualAlert.brakePressed), }, EventName.wrongCarMode: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.NO_ENTRY: wrong_car_mode_alert, }, EventName.wrongCruiseMode: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.NO_ENTRY: NoEntryAlert("Enable Adaptive Cruise"), }, EventName.steerTempUnavailable: { ET.SOFT_DISABLE: SoftDisableAlert("Steering Temporarily Unavailable"), ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable", duration_hud_alert=0.), }, EventName.outOfSpace: { ET.PERMANENT: Alert( "Out of Storage", "", AlertStatus.normal, AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), ET.NO_ENTRY: NoEntryAlert("Out of Storage Space", duration_hud_alert=0.), }, EventName.belowEngageSpeed: { ET.NO_ENTRY: NoEntryAlert("Speed Too Low"), }, EventName.sensorDataInvalid: { ET.PERMANENT: Alert( "No Data from Device Sensors", "Reboot your Device", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("No Data from Device Sensors"), }, EventName.noGps: { ET.PERMANENT: no_gps_alert, }, EventName.soundsUnavailable: { ET.PERMANENT: NormalPermanentAlert("Speaker not found", "Reboot your Device"), ET.NO_ENTRY: NoEntryAlert("Speaker not found"), }, EventName.tooDistracted: { ET.NO_ENTRY: NoEntryAlert("Distraction Level Too High"), }, EventName.overheat: { ET.PERMANENT: Alert( "System Overheated", "", AlertStatus.normal, AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), ET.SOFT_DISABLE: SoftDisableAlert("System Overheated"), ET.NO_ENTRY: NoEntryAlert("System Overheated"), }, EventName.wrongGear: { ET.SOFT_DISABLE: SoftDisableAlert("Gear not D"), ET.NO_ENTRY: NoEntryAlert("Gear not D"), }, # This alert is thrown when the calibration angles are outside of the acceptable range. # For example if the device is pointed too much to the left or the right. # Usually this can only be solved by removing the mount from the windshield completely, # and attaching while making sure the device is pointed straight forward and is level. # See https://comma.ai/setup for more information EventName.calibrationInvalid: { ET.PERMANENT: NormalPermanentAlert("Calibration Invalid", "Remount Device and Recalibrate"), ET.SOFT_DISABLE: SoftDisableAlert("Calibration Invalid: Remount Device & Recalibrate"), ET.NO_ENTRY: NoEntryAlert("Calibration Invalid: Remount Device & Recalibrate"), }, EventName.calibrationIncomplete: { ET.PERMANENT: calibration_incomplete_alert, ET.SOFT_DISABLE: SoftDisableAlert("Calibration in Progress"), ET.NO_ENTRY: NoEntryAlert("Calibration in Progress"), }, EventName.doorOpen: { ET.SOFT_DISABLE: SoftDisableAlert("Door Open"), ET.NO_ENTRY: NoEntryAlert("Door Open"), }, EventName.seatbeltNotLatched: { ET.SOFT_DISABLE: SoftDisableAlert("Seatbelt Unlatched"), ET.NO_ENTRY: NoEntryAlert("Seatbelt Unlatched"), }, EventName.espDisabled: { ET.SOFT_DISABLE: SoftDisableAlert("ESP Off"), ET.NO_ENTRY: NoEntryAlert("ESP Off"), }, EventName.lowBattery: { ET.SOFT_DISABLE: SoftDisableAlert("Low Battery"), ET.NO_ENTRY: NoEntryAlert("Low Battery"), }, # Different openpilot services communicate between each other at a certain # interval. If communication does not follow the regular schedule this alert # is thrown. This can mean a service crashed, did not broadcast a message for # ten times the regular interval, or the average interval is more than 10% too high. EventName.commIssue: { ET.SOFT_DISABLE: SoftDisableAlert("Communication Issue between Processes"), ET.NO_ENTRY: NoEntryAlert("Communication Issue between Processes", audible_alert=AudibleAlert.chimeDisengage), }, # Thrown when manager detects a service exited unexpectedly while driving EventName.processNotRunning: { ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device", audible_alert=AudibleAlert.chimeDisengage), }, EventName.radarFault: { ET.SOFT_DISABLE: SoftDisableAlert("Radar Error: Restart the Car"), ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"), }, # Every frame from the camera should be processed by the model. If modeld # is not processing frames fast enough they have to be dropped. This alert is # thrown when over 20% of frames are dropped. EventName.modeldLagging: { ET.SOFT_DISABLE: SoftDisableAlert("Driving model lagging"), ET.NO_ENTRY: NoEntryAlert("Driving model lagging"), }, # Besides predicting the path, lane lines and lead car data the model also # predicts the current velocity and rotation speed of the car. If the model is # very uncertain about the current velocity while the car is moving, this # usually means the model has trouble understanding the scene. This is used # as a heuristic to warn the driver. EventName.posenetInvalid: { ET.SOFT_DISABLE: SoftDisableAlert("Model Output Uncertain"), ET.NO_ENTRY: NoEntryAlert("Model Output Uncertain"), }, # When the localizer detects an acceleration of more than 40 m/s^2 (~4G) we # alert the driver the device might have fallen from the windshield. EventName.deviceFalling: { ET.SOFT_DISABLE: SoftDisableAlert("Device Fell Off Mount"), ET.NO_ENTRY: NoEntryAlert("Device Fell Off Mount"), }, EventName.lowMemory: { ET.SOFT_DISABLE: SoftDisableAlert("Low Memory: Reboot Your Device"), ET.PERMANENT: NormalPermanentAlert("Low Memory", "Reboot your Device"), ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device", audible_alert=AudibleAlert.chimeDisengage), }, EventName.accFaulted: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Faulted"), ET.PERMANENT: NormalPermanentAlert("Cruise Faulted", ""), ET.NO_ENTRY: NoEntryAlert("Cruise Faulted"), }, EventName.controlsMismatch: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch"), }, EventName.roadCameraError: { ET.PERMANENT: NormalPermanentAlert("Road Camera Error", "", duration_text=10.), }, EventName.driverCameraError: { ET.PERMANENT: NormalPermanentAlert("Driver Camera Error", "", duration_text=10.), }, EventName.wideRoadCameraError: { ET.PERMANENT: NormalPermanentAlert("Wide Road Camera Error", "", duration_text=10.), }, # Sometimes the USB stack on the device can get into a bad state # causing the connection to the panda to be lost EventName.usbError: { ET.SOFT_DISABLE: SoftDisableAlert("USB Error: Reboot Your Device"), ET.PERMANENT: NormalPermanentAlert("USB Error: Reboot Your Device", ""), ET.NO_ENTRY: NoEntryAlert("USB Error: Reboot Your Device"), }, # This alert can be thrown for the following reasons: # - No CAN data received at all # - CAN data is received, but some message are not received at the right frequency # If you're not writing a new car port, this is usually cause by faulty wiring EventName.canError: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("CAN Error: Check Connections"), ET.PERMANENT: Alert( "CAN Error: Check Connections", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("CAN Error: Check Connections"), }, EventName.steerUnavailable: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("LKAS Fault: Restart the Car"), ET.PERMANENT: Alert( "LKAS Fault: Restart the car to engage", "", AlertStatus.normal, AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), ET.NO_ENTRY: NoEntryAlert("LKAS Fault: Restart the Car"), }, EventName.brakeUnavailable: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Fault: Restart the Car"), ET.PERMANENT: Alert( "Cruise Fault: Restart the car to engage", "", AlertStatus.normal, AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"), }, EventName.reverseGear: { ET.PERMANENT: Alert( "Reverse\nGear", "", AlertStatus.normal, AlertSize.full, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=0.5), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Reverse Gear"), ET.NO_ENTRY: NoEntryAlert("Reverse Gear"), }, # On cars that use stock ACC the car can decide to cancel ACC for various reasons. # When this happens we can no long control the car so the user needs to be warned immediately. EventName.cruiseDisabled: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Is Off"), }, # For planning the trajectory Model Predictive Control (MPC) is used. This is # an optimization algorithm that is not guaranteed to find a feasible solution. # If no solution is found or the solution has a very high cost this alert is thrown. EventName.plannerError: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Planner Solution Error"), ET.NO_ENTRY: NoEntryAlert("Planner Solution Error"), }, # When the relay in the harness box opens the CAN bus between the LKAS camera # and the rest of the car is separated. When messages from the LKAS camera # are received on the car side this usually means the relay hasn't opened correctly # and this alert is thrown. EventName.relayMalfunction: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Harness Malfunction"), ET.PERMANENT: NormalPermanentAlert("Harness Malfunction", "Check Hardware"), ET.NO_ENTRY: NoEntryAlert("Harness Malfunction"), }, EventName.noTarget: { ET.IMMEDIATE_DISABLE: Alert( "openpilot Canceled", "No close lead car", AlertStatus.normal, AlertSize.mid, Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.), ET.NO_ENTRY: NoEntryAlert("No Close Lead Car"), }, EventName.speedTooLow: { ET.IMMEDIATE_DISABLE: Alert( "openpilot Canceled", "Speed too low", AlertStatus.normal, AlertSize.mid, Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.), }, # When the car is driving faster than most cars in the training data the model outputs can be unpredictable EventName.speedTooHigh: { ET.WARNING: Alert( "Speed Too High", "Model uncertain at this speed", AlertStatus.normal, AlertSize.mid, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.), ET.NO_ENTRY: Alert( "Speed Too High", "Slow down to engage", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.), }, EventName.lowSpeedLockout: { ET.PERMANENT: Alert( "Cruise Fault: Restart the car to engage", "", AlertStatus.normal, AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"), }, }