#!/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.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("警告:此分支未经测试", 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"请将时速提高至 {get_display_speed(CP.minEnableSpeed, metric)} 来启用") 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"转向在 {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 = '重新校准' if sm['liveCalibration'].calStatus == log.LiveCalibrationData.Status.recalibrating else '校准' return Alert( f"{first_word}进行中:{sm['liveCalibration'].calPerc:.0f}%", f"请将时速提高至 {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("存储空间不足", f"已用 {full_perc}%") 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"速度误差: {err:.1f} 米/秒" return NoEntryAlert(msg, alert_text_1="Posenet速度无效") 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="进程未运行") 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="进程间通信问题") 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("摄像头故障", ', '.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"请重新安装设备 (俯仰角: {pitch:.1f}°, 偏航角: {yaw:.1f}°)" return NormalPermanentAlert("校准无效", 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 = "转向系统未对准" text = f"角度偏移过大 (偏移量: {angle_offset_deg:.1f}°)" elif not sm['liveParameters'].steerRatioValid: steer_ratio = sm['liveParameters'].steerRatio title = "转向传动比不匹配" text = f"转向齿条几何可能不正确 (传动比: {steer_ratio:.1f})" elif not sm['liveParameters'].stiffnessFactorValid: stiffness_factor = sm['liveParameters'].stiffnessFactor title = "轮胎刚度异常" text = f"请检查轮胎、胎压或定位 (系数: {stiffness_factor:.1f})" else: return NoEntryAlert("paramsd 临时错误") 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("系统过热", 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("内存不足", f"已用 {sm['deviceState'].memoryUsagePercent}%") 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("CPU使用率过高", f"已用 {x}%") def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: return NormalPermanentAlert("驾驶模型滞后", f"已丢帧 {sm['modelV2'].frameDropPerc:.1f}%") 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 vals = f"油门: {round(gb * 100.)}%, 转向: {round(steer * 100.)}%" return NormalPermanentAlert("操纵杆模式", 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() personality_en = "" if personality == "Aggressive": personality_en = "激进" elif personality == "Standard": personality_en = "标准" elif personality == "Relaxed": personality_en = "舒适" return NormalPermanentAlert(f"驾驶风格: {personality_en}", 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 = "请切换原厂LKAS状态以启用" if CP.brand == "tesla": text = "请切换到交通感知巡航控制以启用" elif CP.brand == "mazda": text = "请启用您的车辆LKAS以启用" elif CP.brand == "nissan": text = "请禁用您的车辆原厂LKAS以启用" return NormalPermanentAlert("无效的LKAS设置", 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("操纵杆模式"), }, EventName.longitudinalManeuver: { ET.WARNING: longitudinal_maneuver_alert, ET.PERMANENT: NormalPermanentAlert("纵向操作模式", "确保前方道路畅通"), }, EventName.selfdriveInitializing: { ET.NO_ENTRY: NoEntryAlert("系统初始化中"), }, EventName.startup: { ET.PERMANENT: StartupAlert("请随时准备接管您的车辆控制权") }, EventName.startupMaster: { ET.PERMANENT: startup_master_alert, }, EventName.startupNoControl: { ET.PERMANENT: StartupAlert("仅行车记录仪模式"), ET.NO_ENTRY: NoEntryAlert("仅行车记录仪模式"), }, EventName.startupNoCar: { ET.PERMANENT: StartupAlert("不支持车辆的行车记录仪模式"), }, EventName.startupNoSecOcKey: { ET.PERMANENT: NormalPermanentAlert("仅行车记录仪模式", "安全密钥不可用", priority=Priority.HIGH), }, EventName.dashcamMode: { ET.PERMANENT: NormalPermanentAlert("行车记录仪模式", priority=Priority.LOWEST), }, EventName.invalidLkasSetting: { ET.PERMANENT: invalid_lkas_setting_alert, ET.NO_ENTRY: NoEntryAlert("车道保持辅助系统(LKAS)设置无效"), }, 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("行车记录仪模式", "车辆未识别", priority=Priority.LOWEST), }, EventName.aeb: { ET.PERMANENT: Alert( "刹车!", "紧急制动:可能发生碰撞", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 2.), ET.NO_ENTRY: NoEntryAlert("AEB:可能发生碰撞"), }, EventName.stockAeb: { ET.PERMANENT: Alert( "刹车!", "原厂AEB:可能发生碰撞", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 2.), ET.NO_ENTRY: NoEntryAlert("原厂AEB:可能发生碰撞"), }, EventName.fcw: { ET.PERMANENT: Alert( "刹车!", "可能发生碰撞", AlertStatus.critical, AlertSize.full, Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.warningSoft, 2.), }, EventName.ldw: { ET.PERMANENT: Alert( "监测偏离车道", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.ldw, AudibleAlert.prompt, 3.), }, # ********** events only containing alerts that display while engaged ********** EventName.steerTempUnavailableSilent: { ET.WARNING: Alert( "转向暂时不可用", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8), }, EventName.preDriverDistracted: { ET.PERMANENT: Alert( "请注意", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.promptDriverDistracted: { ET.PERMANENT: Alert( "请注意", "驾驶员分心", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1), }, EventName.driverDistracted: { ET.PERMANENT: Alert( "立即解除控制", "驾驶员分心", AlertStatus.critical, AlertSize.full, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1), }, EventName.preDriverUnresponsive: { ET.PERMANENT: Alert( "触摸方向盘:未检测到面部", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .1), }, EventName.promptDriverUnresponsive: { ET.PERMANENT: Alert( "触摸方向盘", "驾驶员无响应", AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1), }, EventName.driverUnresponsive: { ET.PERMANENT: Alert( "立即解除控制", "驾驶员无响应", AlertStatus.critical, AlertSize.full, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1), }, EventName.manualRestart: { ET.WARNING: Alert( "接管控制", "请手动继续驾驶", AlertStatus.userPrompt, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.resumeRequired: { ET.WARNING: Alert( "按恢复键以解除停止状态", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.belowSteerSpeed: { ET.WARNING: below_steer_speed_alert, }, EventName.preLaneChangeLeft: { ET.WARNING: Alert( "请确认安全后进行左转变道", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.preLaneChangeRight: { ET.WARNING: Alert( "请确认安全后进行右转变道", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.laneChangeBlocked: { ET.WARNING: Alert( "盲点检测到车辆", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.prompt, .1), }, EventName.laneChange: { ET.WARNING: Alert( "正在变道", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .1), }, EventName.steerSaturated: { ET.WARNING: Alert( "请接管控制", "转向超出限制", 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("风扇故障", "可能是硬件问题"), }, # Camera is not outputting frames EventName.cameraMalfunction: { ET.PERMANENT: camera_malfunction_alert, ET.SOFT_DISABLE: soft_disable_alert("摄像头故障"), ET.NO_ENTRY: NoEntryAlert("摄像头故障:请重启设备"), }, # Camera framerate too low EventName.cameraFrameRate: { ET.PERMANENT: NormalPermanentAlert("摄像头帧率低", "请重启设备"), ET.SOFT_DISABLE: soft_disable_alert("摄像头帧率低"), ET.NO_ENTRY: NoEntryAlert("摄像头帧率低:请重启设备"), }, # Unused EventName.locationdTemporaryError: { ET.NO_ENTRY: NoEntryAlert("locationd临时错误"), ET.SOFT_DISABLE: soft_disable_alert("locationd临时错误"), }, EventName.locationdPermanentError: { ET.NO_ENTRY: NoEntryAlert("locationd永久错误"), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("locationd永久错误"), ET.PERMANENT: NormalPermanentAlert("locationd永久错误"), }, # 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 临时错误"), }, EventName.paramsdPermanentError: { ET.NO_ENTRY: NoEntryAlert("paramsd永久错误"), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("paramsd永久错误"), ET.PERMANENT: NormalPermanentAlert("paramsd永久错误"), }, # ********** 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("取消按钮被按下"), }, EventName.brakeHold: { ET.WARNING: Alert( "按恢复键以解除制动保持", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), }, EventName.parkBrake: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("停车制动已启用"), }, EventName.pedalPressed: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("踏板被按下", visual_alert=VisualAlert.brakePressed), }, EventName.steerDisengage: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("方向盘被转动"), }, EventName.preEnableStandstill: { ET.PRE_ENABLE: Alert( "释放制动以启用", "", 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("请按设定键以启用"), }, EventName.wrongCruiseMode: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("自适应巡航已禁用"), }, EventName.steerTempUnavailable: { ET.SOFT_DISABLE: soft_disable_alert("转向暂时不可用"), ET.NO_ENTRY: NoEntryAlert("转向暂时不可用"), }, EventName.steerTimeLimit: { ET.SOFT_DISABLE: soft_disable_alert("车辆转向时间限制"), ET.NO_ENTRY: NoEntryAlert("车辆转向时间限制"), }, EventName.outOfSpace: { ET.PERMANENT: out_of_space_alert, ET.NO_ENTRY: NoEntryAlert("出库"), }, EventName.belowEngageSpeed: { ET.NO_ENTRY: below_engage_speed_alert, }, EventName.sensorDataInvalid: { ET.PERMANENT: Alert( "传感器数据无效", "可能是硬件问题", AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("传感器数据无效"), ET.SOFT_DISABLE: soft_disable_alert("传感器数据无效"), }, EventName.noGps: { }, EventName.tooDistracted: { ET.NO_ENTRY: NoEntryAlert("注意力分散程度过高"), }, 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("系统过热"), ET.NO_ENTRY: NoEntryAlert("系统过热"), }, EventName.wrongGear: { ET.SOFT_DISABLE: user_soft_disable_alert("挡位不在D挡"), ET.NO_ENTRY: NoEntryAlert("挡位不在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("校准无效:重新安装设备并重新校准"), ET.NO_ENTRY: NoEntryAlert("校准无效:重新安装设备并重新校准"), }, EventName.calibrationIncomplete: { ET.PERMANENT: calibration_incomplete_alert, ET.SOFT_DISABLE: soft_disable_alert("校准未完成"), ET.NO_ENTRY: NoEntryAlert("校准进行中"), }, EventName.calibrationRecalibrating: { ET.PERMANENT: calibration_incomplete_alert, ET.SOFT_DISABLE: soft_disable_alert("设备重新安装检测到:重新校准中"), ET.NO_ENTRY: NoEntryAlert("设备重新安装检测到:重新校准中"), }, EventName.doorOpen: { ET.SOFT_DISABLE: user_soft_disable_alert("车门开启"), ET.NO_ENTRY: NoEntryAlert("车门开启"), }, EventName.seatbeltNotLatched: { ET.SOFT_DISABLE: user_soft_disable_alert("安全带未系"), ET.NO_ENTRY: NoEntryAlert("安全带未系"), }, EventName.espDisabled: { ET.SOFT_DISABLE: soft_disable_alert("电子稳定控制系统已禁用"), ET.NO_ENTRY: NoEntryAlert("电子稳定控制系统已禁用"), }, EventName.lowBattery: { ET.SOFT_DISABLE: soft_disable_alert("电池电量低"), ET.NO_ENTRY: NoEntryAlert("电池电量低"), }, # 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("进程间通信问题"), ET.NO_ENTRY: comm_issue_alert, }, EventName.commIssueAvgFreq: { ET.SOFT_DISABLE: soft_disable_alert("进程间通信速率低"), ET.NO_ENTRY: NoEntryAlert("进程间通信速率低"), }, EventName.selfdrivedLagging: { ET.SOFT_DISABLE: soft_disable_alert("系统滞后"), ET.NO_ENTRY: NoEntryAlert("自驾车进程滞后:请重启设备"), }, # 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("进程未运行"), }, EventName.radarFault: { ET.SOFT_DISABLE: soft_disable_alert("雷达错误:请重启车辆"), ET.NO_ENTRY: NoEntryAlert("雷达错误:请重启车辆"), }, EventName.radarTempUnavailable: { ET.SOFT_DISABLE: soft_disable_alert("雷达暂时不可用"), ET.NO_ENTRY: NoEntryAlert("雷达暂时不可用"), }, # 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("驾驶模型滞后"), ET.NO_ENTRY: NoEntryAlert("驾驶模型滞后"), 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速度无效"), 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("设备从支架掉落"), ET.NO_ENTRY: NoEntryAlert("设备从支架掉落"), }, EventName.lowMemory: { ET.SOFT_DISABLE: soft_disable_alert("内存不足:请重启设备"), ET.PERMANENT: low_memory_alert, ET.NO_ENTRY: NoEntryAlert("内存不足:请重启设备"), }, EventName.accFaulted: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("巡航故障:请重启车辆"), ET.PERMANENT: NormalPermanentAlert("巡航故障:重启车辆以启用"), ET.NO_ENTRY: NoEntryAlert("巡航故障:请重启车辆"), }, EventName.espActive: { ET.SOFT_DISABLE: soft_disable_alert("电子稳定控制系统激活中"), ET.NO_ENTRY: NoEntryAlert("电子稳定控制系统激活中"), }, EventName.controlsMismatch: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("控制不匹配"), ET.NO_ENTRY: NoEntryAlert("控制不匹配"), }, # 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错误:请重启设备"), ET.PERMANENT: NormalPermanentAlert("USB错误:请重启设备"), ET.NO_ENTRY: NoEntryAlert("USB错误:请重启设备"), }, # 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总线错误"), ET.PERMANENT: Alert( "CAN总线错误:请检查连接", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, 1., creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("CAN总线错误:请检查连接"), }, EventName.canBusMissing: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("CAN总线断开连接"), ET.PERMANENT: Alert( "CAN总线断开连接:可能电缆故障", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.none, 1., creation_delay=1.), ET.NO_ENTRY: NoEntryAlert("CAN总线断开连接:请检查连接"), }, EventName.steerUnavailable: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("LKAS故障:请重启车辆"), ET.PERMANENT: NormalPermanentAlert("LKAS故障:重启车辆以启用"), ET.NO_ENTRY: NoEntryAlert("LKAS故障:请重启车辆"), }, EventName.reverseGear: { ET.PERMANENT: Alert( "倒车中", "", AlertStatus.normal, AlertSize.full, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5), ET.USER_DISABLE: ImmediateDisableAlert("倒档"), ET.NO_ENTRY: NoEntryAlert("倒档"), }, # 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("巡航已关闭"), }, # 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("线束继电器故障"), ET.PERMANENT: NormalPermanentAlert("线束继电器故障", "检查硬件"), ET.NO_ENTRY: NoEntryAlert("线束继电器故障"), }, EventName.speedTooLow: { ET.IMMEDIATE_DISABLE: Alert( "openpilot已取消", "速度过低", 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( "速度过高", "在此速度下模型不稳定", AlertStatus.userPrompt, AlertSize.mid, Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.promptRepeat, 4.), ET.NO_ENTRY: NoEntryAlert("减速以进行接合"), }, EventName.vehicleSensorsInvalid: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("车辆传感器无效"), ET.PERMANENT: NormalPermanentAlert("车辆传感器校准中", "行驶以校准"), ET.NO_ENTRY: NoEntryAlert("车辆传感器校准中"), }, EventName.personalityChanged: { ET.WARNING: personality_changed_alert, }, EventName.userBookmark: { ET.PERMANENT: NormalPermanentAlert("书签已保存", duration=1.5), }, EventName.audioFeedback: { ET.PERMANENT: audio_feedback_alert, }, } 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 callable(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")