auto-generate CARS.md

pull/23762/head
Shane Smiskol 4 years ago
parent a8f8e3eeae
commit fdd8bdd076
  1. BIN
      docs/assets/icon-star-empty.png
  2. BIN
      docs/assets/icon-star-full.png
  3. BIN
      docs/assets/icon-star-half.png
  4. BIN
      docs/assets/icon-star-old.png
  5. 126
      docs/cars.py
  6. 22
      selfdrive/car/car_helpers.py
  7. 8
      selfdrive/car/mock/values.py
  8. 20
      selfdrive/car/volkswagen/values.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from enum import Enum from enum import Enum
import os
from selfdrive.car import CarInfo from common.basedir import BASEDIR
from selfdrive.car.car_helpers import interfaces from selfdrive.car.car_helpers import interfaces, get_interface_attr
from selfdrive.car.fingerprints import get_attr_from_cars, all_known_cars from selfdrive.test.test_routes import non_tested_cars
from selfdrive.config import Conversions as CV from collections import defaultdict, namedtuple
class Tier(Enum): class Tier(Enum):
@ -14,56 +15,95 @@ class Tier(Enum):
class Car: class Car:
def __init__(self, CP, car_info): def __init__(self, car_info, CP):
self.CP = CP self.make, self.model = car_info.name.split(' ', 1)
self.info = car_info
self.make, self.model = self.info.name.split(' ', 1) assert len(car_info.years), 'Model {} has no years listed'.format(CP.carFingerprint)
# TODO: properly format model years
years = ' ' + str(max(car_info.years))
self.model_string = "{}{}".format(self.model, years)
self.package = car_info.package
self.stars = Stars(
CP.openpilotLongitudinalControl,
CP.minEnableSpeed <= 1e-3, # TODO: 0 is probably okay
CP.minSteerSpeed <= 1e-3,
CP.carName in MAKES_GOOD_STEERING_TORQUE,
# TODO: make sure this check is complete
CP.carFingerprint not in non_tested_cars,
)
def format_stars(self):
# TODO: exceptions and half stars
return [STAR_ICON_FULL if cat else STAR_ICON_EMPTY for cat in self.stars]
@property @property
def tier(self): def tier(self):
if self.CP.openpilotLongitudinalControl: return {5: Tier.GOLD, 4: Tier.SILVER}.get(sum(self.stars), Tier.BRONZE)
return Tier.GOLD
return Tier.BRONZE
@property
def years(self):
years =
for year in self.info.years:
def make_row(columns):
return "|{}|".format("|".join(columns))
def __str__(self):
min_alc = int(max(0, self.CP.minSteerSpeed) * CV.MS_TO_MPH)
min_acc = int(max(0, self.CP.minEnableSpeed) * CV.MS_TO_MPH)
acc = "openpilot" if self.CP.openpilotLongitudinalControl else "Stock"
years = {}
return f"| {self.make} | {self.model} | {self.info.package} | {acc} | {min_acc}mph | {min_alc}mph |"
# TODO: unify with column names below?
Stars = namedtuple("Stars", ["op_long", "fsr_long", "fsr_lat", "steering_torque", "well_supported"])
STAR_ICON_FULL = '<img src="assets/icon-star-full.png" width="18" />'
STAR_ICON_HALF = '<img src="assets/icon-star-half.png" width="18" />'
STAR_ICON_EMPTY = '<img src="assets/icon-star-empty.png" width="18" />'
CARS_MD_OUT = os.path.join(BASEDIR, "docs", "CARS_generated.md")
CAR_TABLE_COLUMNS = make_row(['Make', 'Model (US Market Reference)', 'Supported Package', 'openpilot Longitudinal',
'FSR Longitudinal', 'FSR Steering', 'Steering Torque', 'Actively Maintained'])
CAR_TABLE_HEADER = make_row(["---"] * 3 + [":---:"] * 5) # first three aren't centered
CAR_ROW_TEMPLATE = make_row(["{}"] * 8)
# TODO: which other makes?
MAKES_GOOD_STEERING_TORQUE = ["toyota", "hyundai", "volkswagen"]
if __name__ == "__main__":
print("# Supported Cars")
print("Cars are sorted into three tiers:\n")
print(" Gold - a full openpilot experience\n")
print(" Silver - a pretty good, albeit limited experience\n")
print(" Bronze - significantly limited\n")
for t in Tier:
print(f"## {t.value} Cars")
print("| Make | Model (US Market Reference) | Supported Package | ACC | No ACC accel below | No ALC below |")
print("| ----------| ------------------------------| ------------------| -----------------| -------------------| ------------------|")
for fingerprint, car_info in sorted(get_attr_from_cars('CAR_INFO').items(), key=lambda x: x[0]):
# print(car_info)
CI, _, _ = interfaces[fingerprint]
CP = CI.get_params(fingerprint)
def generate_cars_md():
tiered_cars = defaultdict(list)
for _, models in get_interface_attr("CAR_INFO").items():
for model, car_info in models.items():
CP = interfaces[model][0].get_params(model)
# Skip community supported # Skip community supported
if CP.dashcamOnly: if CP.dashcamOnly:
continue continue
if isinstance(car_info, CarInfo): # Some candidates have multiple variants
car_info = (car_info,) if not isinstance(car_info, list):
car_info = [car_info]
for _car_info in car_info:
car = Car(_car_info, CP)
tiered_cars[car.tier].append(car)
# Build CARS.md
cars_md_doc = []
for tier in Tier:
# Sort by make, model name, and year
cars = sorted(tiered_cars[tier], key=lambda car: car.make + car.model_string)
cars_md_doc.append("## {} Cars\n".format(tier.name.title()))
cars_md_doc.append(CAR_TABLE_COLUMNS)
cars_md_doc.append(CAR_TABLE_HEADER)
for car in cars:
line = CAR_ROW_TEMPLATE.format(car.make,
car.model_string,
car.package,
*car.format_stars())
cars_md_doc.append(line)
cars_md_doc.append("") # newline
return '\n'.join(cars_md_doc)
if __name__ == "__main__":
# TODO: add argparse for generating json or html (undecided)
for info in car_info: with open(CARS_MD_OUT, 'w') as f:
car = Car(CP, info) f.write(generate_cars_md())
if car.tier == t:
print(car)

@ -58,19 +58,27 @@ def load_interfaces(brand_names):
return ret return ret
def _get_interface_names(): def get_interface_attr(attr):
# read all the folders in selfdrive/car and return a dict where:
# - keys are all the car names that which we have an interface for
# - values are lists of spefic car models for a given car
brand_names = {} brand_names = {}
for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]: for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]:
try: try:
brand_name = car_folder.split('/')[-1] brand_name = car_folder.split('/')[-1]
model_names = __import__(f'selfdrive.car.{brand_name}.values', fromlist=['CAR']).CAR attr_data = getattr(__import__(f'selfdrive.car.{brand_name}.values', fromlist=[attr]), attr)
model_names = [getattr(model_names, c) for c in model_names.__dict__.keys() if not c.startswith("__")] brand_names[brand_name] = attr_data
brand_names[brand_name] = model_names
except (ImportError, OSError): except (ImportError, OSError):
pass pass
return brand_names
def _get_interface_names():
# read all the folders in selfdrive/car and return a dict where:
# - keys are all the car names that which we have an interface for
# - values are lists of specific car models for a given brand
brand_names = {}
for brand_name, model_names in get_interface_attr("CAR").items():
model_names = [getattr(model_names, c) for c in model_names.__dict__.keys() if not c.startswith("__")]
brand_names[brand_name] = model_names
return brand_names return brand_names

@ -1,2 +1,10 @@
from typing import Dict
from selfdrive.car import CarInfo
class CAR: class CAR:
MOCK = 'mock' MOCK = 'mock'
CAR_INFO: Dict[int, CarInfo] = {}

@ -91,7 +91,7 @@ class CAR:
CAR_INFO = { CAR_INFO = {
CAR.ARTEON_MK1: CarInfo("Volkswagen Arteon", {2018, 2021}, "Driver Assistance"), CAR.ARTEON_MK1: CarInfo("Volkswagen Arteon", {2018, 2021}, "Driver Assistance"),
CAR.ATLAS_MK1: CarInfo("Volkswagen Atlas", {2018, 2019, 2022}, "Driver Assistance"), CAR.ATLAS_MK1: CarInfo("Volkswagen Atlas", {2018, 2019, 2022}, "Driver Assistance"),
CAR.GOLF_MK7: ( CAR.GOLF_MK7: [
CarInfo("Volkswagen e-Golf", {2014, 2019, 2020}, "Driver Assistance"), CarInfo("Volkswagen e-Golf", {2014, 2019, 2020}, "Driver Assistance"),
CarInfo("Volkswagen Golf", {2015, 2016, 2017, 2018, 2019, 2020}, "Driver Assistance"), CarInfo("Volkswagen Golf", {2015, 2016, 2017, 2018, 2019, 2020}, "Driver Assistance"),
CarInfo("Volkswagen Golf Alltrack", {2017, 2018}, "Driver Assistance"), CarInfo("Volkswagen Golf Alltrack", {2017, 2018}, "Driver Assistance"),
@ -100,26 +100,26 @@ CAR_INFO = {
CarInfo("Volkswagen Golf R", {2016, 2017, 2018, 2019}, "Driver Assistance"), CarInfo("Volkswagen Golf R", {2016, 2017, 2018, 2019}, "Driver Assistance"),
CarInfo("Volkswagen Golf SportsVan", {2016}, "Driver Assistance"), CarInfo("Volkswagen Golf SportsVan", {2016}, "Driver Assistance"),
CarInfo("Volkswagen Golf SportWagen", {2016}, "Driver Assistance"), CarInfo("Volkswagen Golf SportWagen", {2016}, "Driver Assistance"),
), ],
CAR.JETTA_MK7: ( CAR.JETTA_MK7: [
CarInfo("Volkswagen Jetta", {2018, 2019, 2020}, "Driver Assistance"), CarInfo("Volkswagen Jetta", {2018, 2019, 2020}, "Driver Assistance"),
CarInfo("Volkswagen Jetta GLI", {2021}, "Driver Assistance"), CarInfo("Volkswagen Jetta GLI", {2021}, "Driver Assistance"),
), ],
CAR.PASSAT_MK8: CarInfo("Volkswagen Passat", {2016, 2017, 2018}, "Driver Assistance"), CAR.PASSAT_MK8: CarInfo("Volkswagen Passat", {2016, 2017, 2018}, "Driver Assistance"),
CAR.POLO_MK6: CarInfo("Volkswagen Polo", {2020}, "Driver Assistance"), CAR.POLO_MK6: CarInfo("Volkswagen Polo", {2020}, "Driver Assistance"),
CAR.TAOS_MK1: CarInfo("Volkswagen Taos", {2022}, "Driver Assistance"), CAR.TAOS_MK1: CarInfo("Volkswagen Taos", {2022}, "Driver Assistance"),
CAR.TCROSS_MK1: CarInfo("Volkswagen T-Cross", {2021}, "Driver Assistance"), CAR.TCROSS_MK1: CarInfo("Volkswagen T-Cross", {2021}, "Driver Assistance"),
CAR.TIGUAN_MK2: CarInfo("Volkswagen Tiguan", {2020}, "Driver Assistance"), CAR.TIGUAN_MK2: CarInfo("Volkswagen Tiguan", {2020}, "Driver Assistance"),
CAR.TOURAN_MK2: CarInfo("Volkswagen Touran", {2017}, "Driver Assistance"), CAR.TOURAN_MK2: CarInfo("Volkswagen Touran", {2017}, "Driver Assistance"),
CAR.TRANSPORTER_T61: ( CAR.TRANSPORTER_T61: [
CarInfo("Volkswagen Caravelle", {2020}, "Driver Assistance"), CarInfo("Volkswagen Caravelle", {2020}, "Driver Assistance"),
CarInfo("Volkswagen California", {2021}, "Driver Assistance"), CarInfo("Volkswagen California", {2021}, "Driver Assistance"),
), ],
CAR.TROC_MK1: CarInfo("Volkswagen T-Roc", {2021}, "Driver Assistance"), CAR.TROC_MK1: CarInfo("Volkswagen T-Roc", {2021}, "Driver Assistance"),
CAR.AUDI_A3_MK3: ( CAR.AUDI_A3_MK3: [
CarInfo("Audi A3", {2014, 2015, 2016, 2017, 2018, 2019}, "ACC + Lane Assist"), CarInfo("Audi A3", {2014, 2015, 2016, 2017, 2018, 2019}, "ACC + Lane Assist"),
CarInfo("Audi A3 Sportback e-tron", {2017, 2018}, "ACC + Lane Assist"), CarInfo("Audi A3 Sportback e-tron", {2017, 2018}, "ACC + Lane Assist"),
), ],
CAR.AUDI_Q2_MK1: CarInfo("Audi Q2", {2018}, "ACC + Lane Assist"), CAR.AUDI_Q2_MK1: CarInfo("Audi Q2", {2018}, "ACC + Lane Assist"),
CAR.AUDI_Q3_MK2: CarInfo("Audi Q3", {2020, 2021}, "ACC + Lane Assist"), CAR.AUDI_Q3_MK2: CarInfo("Audi Q3", {2020, 2021}, "ACC + Lane Assist"),
CAR.SEAT_ATECA_MK1: CarInfo("SEAT Ateca", {2018}, "Driver Assistance"), CAR.SEAT_ATECA_MK1: CarInfo("SEAT Ateca", {2018}, "Driver Assistance"),
@ -129,10 +129,10 @@ CAR_INFO = {
CAR.SKODA_KODIAQ_MK1: CarInfo("Škoda Kodiaq", {2018, 2019}, "Driver Assistance"), CAR.SKODA_KODIAQ_MK1: CarInfo("Škoda Kodiaq", {2018, 2019}, "Driver Assistance"),
CAR.SKODA_SCALA_MK1: CarInfo("Škoda Scala", {2020}, "Driver Assistance"), CAR.SKODA_SCALA_MK1: CarInfo("Škoda Scala", {2020}, "Driver Assistance"),
CAR.SKODA_SUPERB_MK3: CarInfo("Škoda Superb", {2015, 2017, 2018}, "Driver Assistance"), CAR.SKODA_SUPERB_MK3: CarInfo("Škoda Superb", {2015, 2017, 2018}, "Driver Assistance"),
CAR.SKODA_OCTAVIA_MK3: ( CAR.SKODA_OCTAVIA_MK3: [
CarInfo("Škoda Octavia", {2015, 2018, 2019}, "Driver Assistance"), CarInfo("Škoda Octavia", {2015, 2018, 2019}, "Driver Assistance"),
CarInfo("Škoda Octavia RS", {2016}, "Driver Assistance"), CarInfo("Škoda Octavia RS", {2016}, "Driver Assistance"),
), ],
} }
# All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars # All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars

Loading…
Cancel
Save