#!/usr/bin/env python3 import math import os from cereal import log, car import cereal.messaging as messaging from openpilot.common.constants import CV from openpilot.common.git import get_short_branch from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.locationd.calibrationd import MIN_SPEED_FILTER from openpilot.system.micd import SAMPLE_RATE, SAMPLE_BUFFER from openpilot.selfdrive.ui.feedback.feedbackd import FEEDBACK_MAX_DURATION from openpilot.system.hardware import HARDWARE from openpilot.sunnypilot.selfdrive.selfdrived.events_base import EventsBase, Priority, ET, Alert, \ NoEntryAlert, SoftDisableAlert, UserSoftDisableAlert, ImmediateDisableAlert, EngagementAlert, NormalPermanentAlert, \ StartupAlert, AlertCallbackType, wrong_car_mode_alert AlertSize = log.SelfdriveState.AlertSize AlertStatus = log.SelfdriveState.AlertStatus VisualAlert = car.CarControl.HUDControl.VisualAlert AudibleAlert = car.CarControl.HUDControl.AudibleAlert EventName = log.OnroadEvent.EventName # get event name from enum EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()} class Events(EventsBase): def __init__(self): super().__init__() self.event_counters = dict.fromkeys(EVENTS.keys(), 0) def get_events_mapping(self) -> dict[int, dict[str, Alert | AlertCallbackType]]: return EVENTS def get_event_name(self, event: int): return EVENT_NAME[event] def get_event_msg_type(self): return log.OnroadEvent # ********** helper functions ********** def get_display_speed(speed_ms: float, metric: bool) -> str: speed = int(round(speed_ms * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))) unit = 'km/h' if metric else 'mph' return f"{speed} {unit}" # ********** alert callback functions ********** def soft_disable_alert(alert_text_2: str) -> AlertCallbackType: def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: if soft_disable_time < int(0.5 / DT_CTRL): return ImmediateDisableAlert(alert_text_2) return SoftDisableAlert(alert_text_2) return func def user_soft_disable_alert(alert_text_2: str) -> AlertCallbackType: def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: if soft_disable_time < int(0.5 / DT_CTRL): return ImmediateDisableAlert(alert_text_2) return UserSoftDisableAlert(alert_text_2) return func def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: branch = get_short_branch() # Ensure get_short_branch is cached to avoid lags on startup if "REPLAY" in os.environ: branch = "replay" return StartupAlert("WARNING: This branch is not tested", branch, alert_status=AlertStatus.userPrompt) def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: return NoEntryAlert(f"Drive above {get_display_speed(CP.minEnableSpeed, metric)} to engage") def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: return Alert( f"Steer Assist Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 0.4) def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: first_word = 'Recalibrating' if sm['liveCalibration'].calStatus == log.LiveCalibrationData.Status.recalibrating else 'Calibrating' return Alert( f"{first_word}: {sm['liveCalibration'].calPerc:.0f}%", f"Drive Above {get_display_speed(MIN_SPEED_FILTER, metric)}", AlertStatus.normal, AlertSize.mid, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2) def audio_feedback_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: duration = FEEDBACK_MAX_DURATION - ((sm['audioFeedback'].blockNum + 1) * SAMPLE_BUFFER / SAMPLE_RATE) return NormalPermanentAlert( "Recording Audio Feedback", f"{round(duration)} second{'s' if round(duration) != 1 else ''} remaining. Press again to save early.", priority=Priority.LOW) # *** debug alerts *** def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: full_perc = round(100. - sm['deviceState'].freeSpacePercent) return NormalPermanentAlert("Out of Storage", f"{full_perc}% full") def posenet_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: mdl = sm['modelV2'].velocity.x[0] if len(sm['modelV2'].velocity.x) else math.nan err = CS.vEgo - mdl msg = f"Speed Error: {err:.1f} m/s" return NoEntryAlert(msg, alert_text_1="Posenet Speed Invalid") def process_not_running_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: not_running = [p.name for p in sm['managerState'].processes if not p.running and p.shouldBeRunning] msg = ', '.join(not_running) return NoEntryAlert(msg, alert_text_1="Process Not Running") def comm_issue_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: bs = [s for s in sm.data.keys() if not sm.all_checks([s, ])] msg = ', '.join(bs[:4]) # can't fit too many on one line return NoEntryAlert(msg, alert_text_1="Communication Issue Between Processes") def camera_malfunction_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: all_cams = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') bad_cams = [s.replace('State', '') for s in all_cams if s in sm.data.keys() and not sm.all_checks([s, ])] return NormalPermanentAlert("Camera Malfunction", ', '.join(bad_cams)) def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: rpy = sm['liveCalibration'].rpyCalib yaw = math.degrees(rpy[2] if len(rpy) == 3 else math.nan) pitch = math.degrees(rpy[1] if len(rpy) == 3 else math.nan) angles = f"Remount Device (Pitch: {pitch:.1f}°, Yaw: {yaw:.1f}°)" return NormalPermanentAlert("Calibration Invalid", angles) def paramsd_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: if not sm['liveParameters'].angleOffsetValid: angle_offset_deg = sm['liveParameters'].angleOffsetDeg title = "Steering misalignment detected" text = f"Angle offset too high (Offset: {angle_offset_deg:.1f}°)" elif not sm['liveParameters'].steerRatioValid: steer_ratio = sm['liveParameters'].steerRatio title = "Steer ratio mismatch" text = f"Steering rack geometry may be off (Ratio: {steer_ratio:.1f})" elif not sm['liveParameters'].stiffnessFactorValid: stiffness_factor = sm['liveParameters'].stiffnessFactor title = "Abnormal tire stiffness" text = f"Check tires, pressure, or alignment (Factor: {stiffness_factor:.1f})" else: return NoEntryAlert("paramsd Temporary Error") return NoEntryAlert(alert_text_1=title, alert_text_2=text) def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: cpu = max(sm['deviceState'].cpuTempC, default=0.) gpu = max(sm['deviceState'].gpuTempC, default=0.) temp = max((cpu, gpu, sm['deviceState'].memoryTempC)) return NormalPermanentAlert("System Overheated", f"{temp:.0f} °C") def low_memory_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: return NormalPermanentAlert("Low Memory", f"{sm['deviceState'].memoryUsagePercent}% used") def high_cpu_usage_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: x = max(sm['deviceState'].cpuUsagePercent, default=0.) return NormalPermanentAlert("High CPU Usage", f"{x}% used") def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: return NormalPermanentAlert("Driving Model Lagging", f"{sm['modelV2'].frameDropPerc:.1f}% frames dropped") def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: gb = sm['carControl'].actuators.accel / 4. steer = sm['carControl'].actuators.torque damp = sm['carControl'].actuators.dampFactor damp_pct = round((damp - 3) / (200 - 3) * 100.) if damp > 0 else 50 vals = f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%\nDamp: {damp_pct}%" return NormalPermanentAlert("Joystick Mode", vals) def longitudinal_maneuver_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: ad = sm['alertDebug'] audible_alert = AudibleAlert.prompt if 'Active' in ad.alertText1 else AudibleAlert.none alert_status = AlertStatus.userPrompt if 'Active' in ad.alertText1 else AlertStatus.normal alert_size = AlertSize.mid if ad.alertText2 else AlertSize.small return Alert(ad.alertText1, ad.alertText2, alert_status, alert_size, Priority.LOW, VisualAlert.none, audible_alert, 0.2) def personality_changed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: personality = str(personality).title() return NormalPermanentAlert(f"Driving Personality: {personality}", duration=1.5) def invalid_lkas_setting_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: text = "Toggle stock LKAS on or off to engage" if CP.brand == "tesla": text = "Switch to Traffic-Aware Cruise Control to engage" elif CP.brand == "mazda": text = "Enable your car's LKAS to engage" elif CP.brand == "nissan": text = "Disable your car's stock LKAS to engage" return NormalPermanentAlert("Invalid LKAS setting", text) EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # ********** events with no alerts ********** EventName.stockFcw: {}, EventName.actuatorsApiUnavailable: {}, # ********** events only containing alerts displayed in all states ********** EventName.joystickDebug: { ET.WARNING: joystick_alert, ET.PERMANENT: NormalPermanentAlert("Joystick Mode"), }, EventName.longitudinalManeuver: { ET.WARNING: longitudinal_maneuver_alert, ET.PERMANENT: NormalPermanentAlert("Longitudinal Maneuver Mode", "Ensure road ahead is clear"), }, EventName.selfdriveInitializing: { ET.NO_ENTRY: NoEntryAlert("System Initializing"), }, EventName.startup: { ET.PERMANENT: StartupAlert("Be ready to take over at any time") }, EventName.startupMaster: { ET.PERMANENT: startup_master_alert, }, EventName.startupNoControl: { ET.PERMANENT: StartupAlert("Dashcam mode"), ET.NO_ENTRY: NoEntryAlert("Dashcam mode"), }, EventName.startupNoCar: { ET.PERMANENT: StartupAlert("Dashcam mode for unsupported car"), }, EventName.startupNoSecOcKey: { ET.PERMANENT: NormalPermanentAlert("Dashcam Mode", "Security Key Not Available", priority=Priority.HIGH), }, EventName.dashcamMode: { ET.PERMANENT: NormalPermanentAlert("Dashcam Mode", priority=Priority.LOWEST), }, EventName.invalidLkasSetting: { ET.PERMANENT: invalid_lkas_setting_alert, ET.NO_ENTRY: NoEntryAlert("Invalid LKAS setting"), }, EventName.cruiseMismatch: { #ET.PERMANENT: ImmediateDisableAlert("openpilot failed to cancel cruise"), }, # 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: NormalPermanentAlert("Dashcam Mode", "Car Unrecognized", priority=Priority.LOWEST), }, EventName.aeb: { ET.PERMANENT: Alert( "BRAKE!", "Emergency Braking: Risk of Collision", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 2.), ET.NO_ENTRY: NoEntryAlert("AEB: Risk of Collision"), }, EventName.stockAeb: { ET.PERMANENT: Alert( "BRAKE!", "Stock AEB: Risk of Collision", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 2.), ET.NO_ENTRY: NoEntryAlert("Stock AEB: Risk of Collision"), }, EventName.stockLkas: { ET.NO_ENTRY: NoEntryAlert("Stock LKAS: Lane Departure Detected"), }, EventName.fcw: { ET.PERMANENT: Alert( "BRAKE!", "Risk of Collision", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.warningSoft, 2.), }, EventName.ldw: { ET.PERMANENT: Alert( "Lane Departure Detected", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.ldw, AudibleAlert.prompt, 3.), }, # ********** events only containing alerts that display while engaged ********** EventName.steerTempUnavailableSilent: { ET.WARNING: Alert( "Steering Assist Temporarily Unavailable", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8), }, EventName.preDriverDistracted: { ET.PERMANENT: Alert( "Pay Attention", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.promptDriverDistracted: { ET.PERMANENT: Alert( "Pay Attention", "Driver Distracted", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1), }, EventName.driverDistracted: { ET.PERMANENT: Alert( "DISENGAGE IMMEDIATELY", "Driver Distracted", AlertStatus.critical, AlertSize.full, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1), }, EventName.preDriverUnresponsive: { ET.PERMANENT: Alert( "Touch Steering Wheel: No Face Detected", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .1), }, EventName.promptDriverUnresponsive: { ET.PERMANENT: Alert( "Touch Steering Wheel", "Driver Unresponsive", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1), }, EventName.driverUnresponsive: { ET.PERMANENT: Alert( "DISENGAGE IMMEDIATELY", "Driver Unresponsive", AlertStatus.critical, AlertSize.full, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1), }, EventName.manualRestart: { ET.WARNING: Alert( "TAKE CONTROL", "Resume Driving Manually", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.resumeRequired: { ET.WARNING: Alert( "Press Resume to Exit Standstill", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.belowSteerSpeed: { ET.WARNING: below_steer_speed_alert, }, EventName.preLaneChangeLeft: { ET.WARNING: Alert( "Steer Left to Start Lane Change Once Safe", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.preLaneChangeRight: { ET.WARNING: Alert( "Steer Right to Start Lane Change Once Safe", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.laneChangeBlocked: { ET.WARNING: Alert( "Car Detected in Blindspot", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.prompt, .1), }, EventName.laneChange: { ET.WARNING: Alert( "Changing Lanes", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.steerSaturated: { ET.WARNING: Alert( "Take Control", "Turn Exceeds Steering Limit", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.promptRepeat, 2.), }, # Thrown when the fan is driven at >50% but is not rotating EventName.fanMalfunction: { ET.PERMANENT: NormalPermanentAlert("Fan Malfunction", "Likely Hardware Issue"), }, # Camera is not outputting frames EventName.cameraMalfunction: { ET.PERMANENT: camera_malfunction_alert, ET.SOFT_DISABLE: soft_disable_alert("Camera Malfunction"), ET.NO_ENTRY: NoEntryAlert("Camera Malfunction: Reboot Your Device"), }, # Camera framerate too low EventName.cameraFrameRate: { ET.PERMANENT: NormalPermanentAlert("Camera Frame Rate Low", "Reboot your Device"), ET.SOFT_DISABLE: soft_disable_alert("Camera Frame Rate Low"), ET.NO_ENTRY: NoEntryAlert("Camera Frame Rate Low: Reboot Your Device"), }, # Unused EventName.locationdTemporaryError: { ET.NO_ENTRY: NoEntryAlert("locationd Temporary Error"), ET.SOFT_DISABLE: soft_disable_alert("locationd Temporary Error"), }, EventName.locationdPermanentError: { ET.NO_ENTRY: NoEntryAlert("locationd Permanent Error"), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("locationd Permanent Error"), ET.PERMANENT: NormalPermanentAlert("locationd Permanent Error"), }, # 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.paramsdTemporaryError: { ET.NO_ENTRY: paramsd_invalid_alert, ET.SOFT_DISABLE: soft_disable_alert("paramsd Temporary Error"), }, EventName.paramsdPermanentError: { ET.NO_ENTRY: NoEntryAlert("paramsd Permanent Error"), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("paramsd Permanent Error"), ET.PERMANENT: NormalPermanentAlert("paramsd Permanent Error"), }, # ********** events that affect controls state transitions ********** EventName.pcmEnable: { ET.ENABLE: EngagementAlert(AudibleAlert.engage), }, EventName.buttonEnable: { ET.ENABLE: EngagementAlert(AudibleAlert.engage), }, EventName.pcmDisable: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), }, EventName.buttonCancel: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Cancel Pressed"), }, EventName.brakeHold: { ET.WARNING: Alert( "Press Resume to Exit Brake Hold", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.parkBrake: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Parking Brake Engaged"), }, EventName.pedalPressed: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Pedal Pressed", visual_alert=VisualAlert.brakePressed), }, EventName.steerDisengage: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Steering Pressed"), }, EventName.preEnableStandstill: { ET.PRE_ENABLE: Alert( "Release Brake to Engage", "", AlertStatus.normal, AlertSize.small, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .1, creation_delay=1.), }, EventName.gasPressedOverride: { ET.OVERRIDE_LONGITUDINAL: Alert( "", "", AlertStatus.normal, AlertSize.none, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .1), }, EventName.steerOverride: { ET.OVERRIDE_LATERAL: Alert( "", "", AlertStatus.normal, AlertSize.none, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .1), }, EventName.wrongCarMode: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: wrong_car_mode_alert, }, EventName.resumeBlocked: { ET.NO_ENTRY: NoEntryAlert("Press Set to Engage"), }, EventName.wrongCruiseMode: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Adaptive Cruise Disabled"), }, EventName.steerTempUnavailable: { ET.SOFT_DISABLE: soft_disable_alert("Steering Assist Temporarily Unavailable"), ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"), }, EventName.steerTimeLimit: { ET.SOFT_DISABLE: soft_disable_alert("Vehicle Steering Time Limit"), ET.NO_ENTRY: NoEntryAlert("Vehicle Steering Time Limit"), }, EventName.outOfSpace: { ET.PERMANENT: out_of_space_alert, ET.NO_ENTRY: NoEntryAlert("Out of Storage"), }, EventName.belowEngageSpeed: { ET.NO_ENTRY: below_engage_speed_alert, }, EventName.sensorDataInvalid: { ET.PERMANENT: Alert( "Sensor Data Invalid", "Possible Hardware Issue", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("Sensor Data Invalid"), ET.SOFT_DISABLE: soft_disable_alert("Sensor Data Invalid"), }, EventName.noGps: { }, EventName.tooDistracted: { ET.NO_ENTRY: NoEntryAlert("Distraction Level Too High"), }, EventName.excessiveActuation: { ET.SOFT_DISABLE: soft_disable_alert("Excessive Actuation"), ET.NO_ENTRY: NoEntryAlert("Excessive Actuation"), }, EventName.overheat: { ET.PERMANENT: overheat_alert, ET.SOFT_DISABLE: soft_disable_alert("System Overheated"), ET.NO_ENTRY: NoEntryAlert("System Overheated"), }, EventName.wrongGear: { ET.SOFT_DISABLE: user_soft_disable_alert("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: calibration_invalid_alert, ET.SOFT_DISABLE: soft_disable_alert("Calibration Invalid: Remount Device & Recalibrate"), ET.NO_ENTRY: NoEntryAlert("Calibration Invalid: Remount Device & Recalibrate"), }, EventName.calibrationIncomplete: { ET.PERMANENT: calibration_incomplete_alert, ET.SOFT_DISABLE: soft_disable_alert("Calibration Incomplete"), ET.NO_ENTRY: NoEntryAlert("Calibration in Progress"), }, EventName.calibrationRecalibrating: { ET.PERMANENT: calibration_incomplete_alert, ET.SOFT_DISABLE: soft_disable_alert("Device Remount Detected: Recalibrating"), ET.NO_ENTRY: NoEntryAlert("Remount Detected: Recalibrating"), }, EventName.doorOpen: { ET.SOFT_DISABLE: user_soft_disable_alert("Door Open"), ET.NO_ENTRY: NoEntryAlert("Door Open"), }, EventName.seatbeltNotLatched: { ET.SOFT_DISABLE: user_soft_disable_alert("Seatbelt Unlatched"), ET.NO_ENTRY: NoEntryAlert("Seatbelt Unlatched"), }, EventName.espDisabled: { ET.SOFT_DISABLE: soft_disable_alert("Electronic Stability Control Disabled"), ET.NO_ENTRY: NoEntryAlert("Electronic Stability Control Disabled"), }, EventName.lowBattery: { ET.SOFT_DISABLE: soft_disable_alert("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: soft_disable_alert("Communication Issue Between Processes"), ET.NO_ENTRY: comm_issue_alert, }, EventName.commIssueAvgFreq: { ET.SOFT_DISABLE: soft_disable_alert("Low Communication Rate Between Processes"), ET.NO_ENTRY: NoEntryAlert("Low Communication Rate Between Processes"), }, EventName.selfdrivedLagging: { ET.SOFT_DISABLE: soft_disable_alert("System Lagging"), ET.NO_ENTRY: NoEntryAlert("Selfdrive Process Lagging: Reboot Your Device"), }, # Thrown when manager detects a service exited unexpectedly while driving EventName.processNotRunning: { ET.NO_ENTRY: process_not_running_alert, ET.SOFT_DISABLE: soft_disable_alert("Process Not Running"), }, EventName.radarFault: { ET.SOFT_DISABLE: soft_disable_alert("Radar Error: Restart the Car"), ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"), }, EventName.radarTempUnavailable: { ET.SOFT_DISABLE: soft_disable_alert("Radar Temporarily Unavailable"), ET.NO_ENTRY: NoEntryAlert("Radar Temporarily Unavailable"), }, # 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: soft_disable_alert("Driving Model Lagging"), ET.NO_ENTRY: NoEntryAlert("Driving Model Lagging"), ET.PERMANENT: modeld_lagging_alert, }, # 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: soft_disable_alert("Posenet Speed Invalid"), ET.NO_ENTRY: posenet_invalid_alert, }, # 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: soft_disable_alert("Device Fell Off Mount"), ET.NO_ENTRY: NoEntryAlert("Device Fell Off Mount"), }, EventName.lowMemory: { ET.SOFT_DISABLE: soft_disable_alert("Low Memory: Reboot Your Device"), ET.PERMANENT: low_memory_alert, ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device"), }, EventName.accFaulted: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Fault: Restart the Car"), ET.PERMANENT: NormalPermanentAlert("Cruise Fault: Restart the car to engage"), ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"), }, EventName.espActive: { ET.SOFT_DISABLE: soft_disable_alert("Electronic Stability Control Active"), ET.NO_ENTRY: NoEntryAlert("Electronic Stability Control Active"), }, EventName.controlsMismatch: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch"), ET.NO_ENTRY: NoEntryAlert("Controls Mismatch"), }, # 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: soft_disable_alert("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("Unknown Vehicle Variant"), ET.PERMANENT: Alert( "Unknown Vehicle Variant", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, 1., creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("Unknown Vehicle Variant"), }, EventName.canBusMissing: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("CAN Bus Disconnected"), ET.PERMANENT: Alert( "CAN Bus Disconnected: Likely Faulty Cable", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, 1., creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("CAN Bus Disconnected: Check Connections"), }, EventName.steerUnavailable: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("LKAS Fault: Restart the Car"), ET.PERMANENT: NormalPermanentAlert("LKAS Fault: Restart the car to engage"), ET.NO_ENTRY: NoEntryAlert("LKAS Fault: Restart the Car"), }, EventName.reverseGear: { ET.PERMANENT: Alert( "Reverse\nGear", "", AlertStatus.normal, AlertSize.full, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5), ET.USER_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"), }, # 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 Relay Malfunction"), ET.PERMANENT: NormalPermanentAlert("Harness Relay Malfunction", "Check Hardware"), ET.NO_ENTRY: NoEntryAlert("Harness Relay Malfunction"), }, EventName.speedTooLow: { ET.IMMEDIATE_DISABLE: Alert( "openpilot Canceled", "Speed too low", AlertStatus.normal, AlertSize.mid, Priority.HIGH, VisualAlert.none, AudibleAlert.disengage, 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.userPrompt, AlertSize.mid, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.promptRepeat, 4.), ET.NO_ENTRY: NoEntryAlert("Slow down to engage"), }, EventName.vehicleSensorsInvalid: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Vehicle Sensors Invalid"), ET.PERMANENT: NormalPermanentAlert("Vehicle Sensors Calibrating", "Drive to Calibrate"), ET.NO_ENTRY: NoEntryAlert("Vehicle Sensors Calibrating"), }, EventName.personalityChanged: { ET.WARNING: personality_changed_alert, }, EventName.userBookmark: { ET.PERMANENT: NormalPermanentAlert("Bookmark Saved", duration=1.5), }, EventName.audioFeedback: { ET.PERMANENT: audio_feedback_alert, }, } if HARDWARE.get_device_type() == 'mici': EVENTS.update({ EventName.preDriverDistracted: { ET.PERMANENT: Alert( "Pay Attention", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, 2), }, EventName.promptDriverDistracted: { ET.PERMANENT: Alert( "Pay Attention", "Driver Distracted", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, 1), }, EventName.resumeRequired: { ET.WARNING: Alert( "Press Resume", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.preLaneChangeLeft: { ET.WARNING: Alert( "Steer Left", "Confirm Lane Change", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.preLaneChangeRight: { ET.WARNING: Alert( "Steer Right", "Confirm Lane Change", AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.laneChangeBlocked: { ET.WARNING: Alert( "Car in Blindspot", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.prompt, .1), }, EventName.steerSaturated: { ET.WARNING: Alert( "take control", "turn exceeds limit", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.promptRepeat, 2.), }, EventName.calibrationIncomplete: { ET.PERMANENT: calibration_incomplete_alert, ET.SOFT_DISABLE: soft_disable_alert("Calibration Incomplete"), ET.NO_ENTRY: NoEntryAlert("Calibrating"), }, EventName.reverseGear: { ET.PERMANENT: Alert( "Reverse", "", AlertStatus.normal, AlertSize.full, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5), ET.USER_DISABLE: ImmediateDisableAlert("Reverse"), ET.NO_ENTRY: NoEntryAlert("Reverse"), }, }) if __name__ == '__main__': # print all alerts by type and priority from cereal.services import SERVICE_LIST from collections import defaultdict event_names = {v: k for k, v in EventName.schema.enumerants.items()} alerts_by_type: dict[str, dict[Priority, list[str]]] = defaultdict(lambda: defaultdict(list)) CP = car.CarParams.new_message() CS = car.CarState.new_message() sm = messaging.SubMaster(list(SERVICE_LIST.keys())) for i, alerts in EVENTS.items(): for et, alert in alerts.items(): if not isinstance(alert, Alert): alert = alert(CP, CS, sm, False, 1, log.LongitudinalPersonality.standard) alerts_by_type[et][alert.priority].append(event_names[i]) all_alerts: dict[str, list[tuple[Priority, list[str]]]] = {} for et, priority_alerts in alerts_by_type.items(): all_alerts[et] = sorted(priority_alerts.items(), key=lambda x: x[0], reverse=True) for status, evs in sorted(all_alerts.items(), key=lambda x: x[0]): print(f"**** {status} ****") for p, alert_list in evs: print(f" {repr(p)}:") print(" ", ', '.join(alert_list), "\n")