|
|
|
@ -13,6 +13,7 @@ VisualAlert = car.CarControl.HUDControl.VisualAlert |
|
|
|
|
AudibleAlert = car.CarControl.HUDControl.AudibleAlert |
|
|
|
|
EventName = car.CarEvent.EventName |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Alert priorities |
|
|
|
|
class Priority(IntEnum): |
|
|
|
|
LOWEST = 0 |
|
|
|
@ -22,6 +23,7 @@ class Priority(IntEnum): |
|
|
|
|
HIGH = 4 |
|
|
|
|
HIGHEST = 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Event types |
|
|
|
|
class ET: |
|
|
|
|
ENABLE = 'enable' |
|
|
|
@ -33,6 +35,7 @@ class ET: |
|
|
|
|
IMMEDIATE_DISABLE = 'immediateDisable' |
|
|
|
|
PERMANENT = 'permanent' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# get event name from enum |
|
|
|
|
EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()} |
|
|
|
|
|
|
|
|
@ -56,7 +59,7 @@ class Events: |
|
|
|
|
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_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): |
|
|
|
@ -94,10 +97,11 @@ class Events: |
|
|
|
|
event = car.CarEvent.new_message() |
|
|
|
|
event.name = event_name |
|
|
|
|
for event_type in EVENTS.get(event_name, {}).keys(): |
|
|
|
|
setattr(event, event_type , True) |
|
|
|
|
setattr(event, event_type, True) |
|
|
|
|
ret.append(event) |
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Alert: |
|
|
|
|
def __init__(self, |
|
|
|
|
alert_text_1: str, |
|
|
|
@ -138,6 +142,7 @@ class 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.): |
|
|
|
@ -161,6 +166,7 @@ class ImmediateDisableAlert(Alert): |
|
|
|
|
Priority.HIGHEST, VisualAlert.steerRequired, |
|
|
|
|
AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.), |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EngagementAlert(Alert): |
|
|
|
|
def __init__(self, audible_alert=True): |
|
|
|
|
super().__init__("", "", |
|
|
|
@ -168,14 +174,15 @@ class EngagementAlert(Alert): |
|
|
|
|
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 ********** |
|
|
|
|
|
|
|
|
|
# ********** 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" |
|
|
|
@ -185,6 +192,7 @@ def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: |
|
|
|
|
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" |
|
|
|
@ -194,6 +202,7 @@ def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, met |
|
|
|
|
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( |
|
|
|
@ -202,12 +211,14 @@ def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Al |
|
|
|
|
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", |
|
|
|
@ -215,6 +226,7 @@ def startup_fuzzy_fingerprint_alert(CP: car.CarParams, sm: messaging.SubMaster, |
|
|
|
|
AlertStatus.userPrompt, AlertSize.mid, |
|
|
|
|
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, bool], Alert]]]] = { |
|
|
|
|
# ********** events with no alerts ********** |
|
|
|
|
|
|
|
|
@ -248,6 +260,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
# Car is recognized, but marked as dashcam only |
|
|
|
|
EventName.startupNoControl: { |
|
|
|
|
ET.PERMANENT: Alert( |
|
|
|
|
"Dashcam mode", |
|
|
|
@ -256,6 +269,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
# Car is not recognized |
|
|
|
|
EventName.startupNoCar: { |
|
|
|
|
ET.PERMANENT: Alert( |
|
|
|
|
"Dashcam mode for unsupported car", |
|
|
|
@ -264,6 +278,14 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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, |
|
|
|
|
}, |
|
|
|
@ -292,6 +314,9 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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( |
|
|
|
@ -301,6 +326,9 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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", |
|
|
|
@ -353,6 +381,14 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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"), |
|
|
|
@ -487,18 +523,24 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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"), |
|
|
|
|
}, |
|
|
|
@ -604,6 +646,11 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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"), |
|
|
|
@ -636,12 +683,17 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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), |
|
|
|
@ -649,19 +701,29 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
|
|
|
|
|
EventName.radarFault: { |
|
|
|
|
ET.SOFT_DISABLE: SoftDisableAlert("Radar Error: Restart the Car"), |
|
|
|
|
ET.NO_ENTRY : NoEntryAlert("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"), |
|
|
|
|
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"), |
|
|
|
@ -670,7 +732,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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", |
|
|
|
|
ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device", |
|
|
|
|
audible_alert=AudibleAlert.chimeDisengage), |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
@ -699,12 +761,18 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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( |
|
|
|
@ -745,15 +813,24 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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"), |
|
|
|
@ -766,7 +843,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
"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"), |
|
|
|
|
ET.NO_ENTRY: NoEntryAlert("No Close Lead Car"), |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
EventName.speedTooLow: { |
|
|
|
@ -777,6 +854,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo |
|
|
|
|
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", |
|
|
|
|