from cereal import car
from collections import namedtuple
from dataclasses import dataclass
from enum import Enum
from typing import Dict , List , Optional , Union , no_type_check
class Tier ( Enum ) :
GOLD = " The best openpilot experience. Great highway driving and beyond. "
SILVER = " A solid highway driving experience, but is limited by stock longitudinal. May be upgraded in the future. "
BRONZE = " A good highway experience, but may have limited performance in traffic and on sharp turns. "
class Column ( Enum ) :
MAKE = " Make "
MODEL = " Model "
PACKAGE = " Supported Package "
LONGITUDINAL = " openpilot ACC "
FSR_LONGITUDINAL = " Stop and Go "
FSR_STEERING = " Steer to 0 "
STEERING_TORQUE = " Steering Torque "
MAINTAINED = " Actively Maintained "
class Star ( Enum ) :
FULL = " full "
HALF = " half "
EMPTY = " empty "
StarColumns = list ( Column ) [ 3 : ]
CarFootnote = namedtuple ( " CarFootnote " , [ " text " , " column " , " star " ] , defaults = [ None ] )
def get_footnote ( footnotes : Optional [ List [ Enum ] ] , column : Column ) - > Optional [ Enum ] :
# Returns applicable footnote given current column
if footnotes is not None :
for fn in footnotes :
if fn . value . column == column :
return fn
return None
@dataclass
class CarInfo :
name : str
package : str
video_link : Optional [ str ] = None
footnotes : Optional [ List [ Enum ] ] = None
min_steer_speed : Optional [ float ] = None
min_enable_speed : Optional [ float ] = None
good_torque : bool = False
def init ( self , CP : car . CarParams , non_tested_cars : List [ str ] , all_footnotes : Dict [ Enum , int ] ) :
# TODO: set all the min steer speeds in carParams and remove this
min_steer_speed = CP . minSteerSpeed
if self . min_steer_speed is not None :
min_steer_speed = self . min_steer_speed
assert CP . minSteerSpeed == 0 , f " Minimum steer speed set in both CarInfo and CarParams for { CP . carFingerprint } "
# TODO: set all the min enable speeds in carParams correctly and remove this
min_enable_speed = CP . minEnableSpeed
if self . min_enable_speed is not None :
min_enable_speed = self . min_enable_speed
self . make , self . model = self . name . split ( ' ' , 1 )
self . row = {
Column . MAKE : self . make ,
Column . MODEL : self . model ,
Column . PACKAGE : self . package ,
# StarColumns
Column . LONGITUDINAL : CP . openpilotLongitudinalControl and not CP . radarOffCan ,
Column . FSR_LONGITUDINAL : min_enable_speed < = 0. ,
Column . FSR_STEERING : min_steer_speed < = 0. ,
Column . STEERING_TORQUE : self . good_torque ,
Column . MAINTAINED : CP . carFingerprint not in non_tested_cars ,
}
if self . name == " comma body " :
self . row [ Column . LONGITUDINAL ] = True
self . all_footnotes = all_footnotes
for column in StarColumns :
self . row [ column ] = Star . FULL if self . row [ column ] else Star . EMPTY
# Demote if footnote specifies a star
footnote = get_footnote ( self . footnotes , column )
if footnote is not None and footnote . value . star is not None :
self . row [ column ] = footnote . value . star
self . tier = { 5 : Tier . GOLD , 4 : Tier . SILVER } . get ( list ( self . row . values ( ) ) . count ( Star . FULL ) , Tier . BRONZE )
@no_type_check
def get_column ( self , column : Column , star_icon : str , footnote_tag : str ) - > str :
item : Union [ str , Star ] = self . row [ column ]
if column in StarColumns :
item = star_icon . format ( item . value )
footnote = get_footnote ( self . footnotes , column )
if footnote is not None :
item + = footnote_tag . format ( self . all_footnotes [ footnote ] )
return item