Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: CARS.md 2.1 #1338

Merged
merged 27 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
523bdf9
there is no spoon
jyoung8607 Oct 3, 2024
c7e501e
he's starting to believe
jyoung8607 Oct 3, 2024
620cda3
i know crappy derivative kung-fu
jyoung8607 Oct 4, 2024
97888e5
fix broken links
jyoung8607 Oct 4, 2024
d60029c
regen CARS.md with fixed links
jyoung8607 Oct 4, 2024
d411db0
handle dashcam cars
jyoung8607 Oct 4, 2024
fc732b3
fix dashcam inline links
jyoung8607 Oct 4, 2024
d4420ce
support under-review, move relevant Fords
jyoung8607 Oct 4, 2024
86c2d6c
garbage collect completed TODO
jyoung8607 Oct 4, 2024
e9b566c
collect docs support stuff in docs.py
jyoung8607 Oct 4, 2024
ade3ac0
whitespace diff reduction
jyoung8607 Oct 4, 2024
f316e39
cleanup CarParams fetching
jyoung8607 Oct 4, 2024
9a5fbb3
tight tight tight
jyoung8607 Oct 4, 2024
7053994
garbage collect another completed TODO
jyoung8607 Oct 4, 2024
4d1b214
propitiate type checker
jyoung8607 Oct 4, 2024
0ced347
template wordsmithing
jyoung8607 Oct 4, 2024
a92c3ed
regen CARS.md
jyoung8607 Oct 4, 2024
42dff93
prep for custom install state
jyoung8607 Oct 4, 2024
41353ca
cleanup
jyoung8607 Oct 4, 2024
31d798e
cleanup
jyoung8607 Oct 4, 2024
1126e5d
whitespace diff reduction
jyoung8607 Oct 4, 2024
6d5316e
template wordsmithing
jyoung8607 Oct 4, 2024
9b38e9a
regen CARS.md
jyoung8607 Oct 4, 2024
19c1942
LOL how did I miss model-years
jyoung8607 Oct 4, 2024
d3df456
Merge branch 'master' of https://github.com/commaai/opendbc into extr…
jyoung8607 Oct 4, 2024
05103e3
more template wordsmithing
jyoung8607 Oct 4, 2024
4fe781f
propitiate type checker
jyoung8607 Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
393 changes: 393 additions & 0 deletions docs/CARS.md

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions opendbc/car/CARS_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->

# Support Information for {{car_docs_with_extras | length}} Known Cars

|{{ExtraCarsColumn | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|---|---|---|{% for _ in range((ExtraCarsColumn | length) - 3) %}{{':---:|'}}{% endfor +%}
{% for car_docs in car_docs_with_extras %}
|{% for column in ExtraCarsColumn %}{{car_docs.get_extra_cars_column(column)}}|{% endfor %}

{% endfor %}

# Types of Support

**opendbc can support many more cars than it currently does.** There are a few reasons your car may not be supported.
If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported!
We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car
support comes from users like you!

## Upstream

A supported vehicle is one that just works when you install a comma device. All supported cars provide a better
experience than any stock system. Supported vehicles reference the US market unless otherwise specified.

## Under Review

A vehicle under review is one for which software support has been merged into upstream openpilot, but hasn't yet been
tested for drive quality and conformance with [comma safety guidelines](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md).
This is a normal part of the development and quality assurance process. This vehicle will not work when upstream
openpilot is installed, but custom forks may allow their use.

## Custom

Vehicles in this category are not considered plug-and-play. Software support is included in upstream openpilot, but
these vehicles might not have a harness in the comma store, or the physical install might be at an unusual or cumbersome
location, or they might need unusual configuration after install.

## Dashcam

Dashcam vehicles have software support in upstream openpilot, but will go into "dashcam mode" at startup and will not
engage. This may be due to known issues with driving safety or quality, or it may be a work in progress that isn't yet
ready for safety and quality review.

## Community

Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community
Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).

## Incompatible

### CAN Bus Security

Vehicles with CAN security measures, such as AUTOSAR Secure Onboard Communication (SecOC) are not usable with openpilot
unless the owner can recover the message signing key and implement CAN message signing. Examples include certain newer
Toyota, and the GM Global B platform.

### FlexRay

All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a
CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following
manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars
may one day be supported, but we have no immediate plans to support FlexRay.
24 changes: 19 additions & 5 deletions opendbc/car/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# functions common among cars
import logging
from collections import namedtuple
from dataclasses import dataclass
from dataclasses import dataclass, field
from enum import IntFlag, ReprEnum, EnumType
from dataclasses import replace

from panda import uds
from opendbc.car import structs
from opendbc.car.can_definitions import CanData
from opendbc.car.docs_definitions import CarDocs
from opendbc.car.docs_definitions import CarDocs, ExtraCarDocs
from opendbc.car.common.numpy_fast import clip, interp

# set up logging
Expand Down Expand Up @@ -270,8 +270,8 @@ def __setattr__(self, *args, **kwargs):


@dataclass(order=True)
class PlatformConfig(Freezable):
car_docs: list[CarDocs]
class PlatformConfigBase(Freezable):
car_docs: list[CarDocs] | list[ExtraCarDocs]
specs: CarSpecs

dbc_dict: DbcDict
Expand All @@ -293,6 +293,20 @@ def __post_init__(self):
self.init()


@dataclass(order=True)
class PlatformConfig(PlatformConfigBase):
car_docs: list[CarDocs]
specs: CarSpecs
dbc_dict: DbcDict


@dataclass(order=True)
class ExtraPlatformConfig(PlatformConfigBase):
car_docs: list[ExtraCarDocs]
specs: CarSpecs = CarSpecs(mass=0., wheelbase=0., steerRatio=0.)
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('unknown', None))


class PlatformsType(EnumType):
def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **kwds):
for key in classdict._member_names.keys():
Expand All @@ -303,7 +317,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k


class Platforms(str, ReprEnum, metaclass=PlatformsType):
config: PlatformConfig
config: PlatformConfigBase

def __new__(cls, platform_config: PlatformConfig):
member = str.__new__(cls, platform_config.platform_str)
Expand Down
78 changes: 66 additions & 12 deletions opendbc/car/docs.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
#!/usr/bin/env python3
import argparse
import os
from typing import get_args

from collections import defaultdict
import jinja2
from enum import Enum
from natsort import natsorted

from opendbc.car.common.basedir import BASEDIR
from opendbc.car import gen_empty_fingerprint
from opendbc.car.structs import CarParams
from opendbc.car.docs_definitions import CarDocs, Column, CommonFootnote, PartType
from opendbc.car.docs_definitions import CarDocs, ExtraCarDocs, Column, ExtraCarsColumn, CommonFootnote, PartType
from opendbc.car.car_helpers import interfaces, get_interface_attr
from opendbc.car.values import PLATFORMS
from opendbc.car.values import Platform, PLATFORMS
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.extra_cars import CAR as EXTRA


EXTRA_CARS_MD_OUT = os.path.join(BASEDIR, "../", "../", "docs", "CARS.md")
EXTRA_CARS_MD_TEMPLATE = os.path.join(BASEDIR, "CARS_template.md")

ExtraPlatform = Platform | EXTRA
EXTRA_BRANDS = get_args(ExtraPlatform)
EXTRA_PLATFORMS: dict[str, ExtraPlatform] = {str(platform): platform for brand in EXTRA_BRANDS for platform in brand}


def get_params_for_docs(model, platform) -> CarParams:
cp_model, cp_platform = (model, platform) if model in interfaces else ("MOCK", MOCK.MOCK)
CP: CarParams = interfaces[cp_model][0].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
experimental_long=True, docs=True)
return CP


def get_all_footnotes() -> dict[Enum, int]:
Expand All @@ -17,30 +41,38 @@ def get_all_footnotes() -> dict[Enum, int]:
return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}


def get_all_car_docs() -> list[CarDocs]:
all_car_docs: list[CarDocs] = []
footnotes = get_all_footnotes()
for model, platform in PLATFORMS.items():
def build_sorted_car_docs_list(platforms, footnotes=None, include_dashcam=False, include_custom=False):
collected_car_docs: list[CarDocs | ExtraCarDocs] = []
for model, platform in platforms.items():
car_docs = platform.config.car_docs
# If available, uses experimental longitudinal limits for the docs
CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)], experimental_long=True, docs=True)
CP = get_params_for_docs(model, platform)

if CP.dashcamOnly or not len(car_docs):
if (CP.dashcamOnly and not include_dashcam) or not len(car_docs):
continue

# A platform can include multiple car models
for _car_docs in car_docs:
if not hasattr(_car_docs, "row"):
_car_docs.init_make(CP)
_car_docs.init(CP, footnotes)
all_car_docs.append(_car_docs)
collected_car_docs.append(_car_docs)

# Sort cars by make and model + year
sorted_cars: list[CarDocs] = natsorted(all_car_docs, key=lambda car: car.name.lower())
sorted_cars = natsorted(collected_car_docs, key=lambda car: car.name.lower())
return sorted_cars


def get_all_car_docs() -> list[CarDocs]:
collected_footnotes = get_all_footnotes()
sorted_list: list[CarDocs] = build_sorted_car_docs_list(PLATFORMS, footnotes=collected_footnotes)
return sorted_list


def get_car_docs_with_extras() -> list[CarDocs | ExtraCarDocs]:
sorted_list: list[CarDocs] = build_sorted_car_docs_list(EXTRA_PLATFORMS, include_custom=True, include_dashcam=True)
return sorted_list


def group_by_make(all_car_docs: list[CarDocs]) -> dict[str, list[CarDocs]]:
sorted_car_docs = defaultdict(list)
for car_docs in all_car_docs:
Expand All @@ -57,3 +89,25 @@ def generate_cars_md(all_car_docs: list[CarDocs], template_fn: str) -> str:
group_by_make=group_by_make, footnotes=footnotes,
Column=Column)
return cars_md


def generate_cars_md_with_extras(car_docs_with_extras: list[CarDocs | ExtraCarDocs], template_fn: str) -> str:
with open(template_fn) as f:
template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True)

cars_md: str = template.render(car_docs_with_extras=car_docs_with_extras, PartType=PartType,
group_by_make=group_by_make, ExtraCarsColumn=ExtraCarsColumn)
return cars_md


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Auto generates supportability info docs for all known cars",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument("--template", default=EXTRA_CARS_MD_TEMPLATE, help="Override default template filename")
parser.add_argument("--out", default=EXTRA_CARS_MD_OUT, help="Override default generated filename")
args = parser.parse_args()

with open(args.out, 'w') as f:
f.write(generate_cars_md_with_extras(get_car_docs_with_extras(), args.template))
print(f"Generated and written to {args.out}")
56 changes: 55 additions & 1 deletion opendbc/car/docs_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ class Column(Enum):
VIDEO = "Video"


class ExtraCarsColumn(Enum):
MAKE = "Make"
MODEL = "Model"
PACKAGE = "Package"
SUPPORT = "Support Level"


class SupportType(Enum):
UPSTREAM = "Upstream" # Actively maintained by comma, plug-and-play in release versions of openpilot
REVIEW = "Under review" # Dashcam, but planned for official support after safety validation
DASHCAM = "Dashcam mode" # Dashcam, but may be drivable in a community fork
COMMUNITY = "Community" # Not upstream, but available in a custom community fork, not validated by comma
CUSTOM = "Custom" # Upstream, but don't have a harness available or need an unusual custom install
INCOMPATIBLE = "Not compatible" # Known fundamental incompatibility such as Flexray or hydraulic power steering


class Star(Enum):
FULL = "full"
HALF = "half"
Expand Down Expand Up @@ -246,14 +262,25 @@ class CarDocs:
# all the parts needed for the supported car
car_parts: CarParts = field(default_factory=CarParts)

merged: bool = True
support_type: SupportType = SupportType.UPSTREAM
support_link: str | None = "#upstream"

def __post_init__(self):
self.make, self.model, self.years = split_name(self.name)
self.year_list = get_year_list(self.years)

def init(self, CP: CarParams, all_footnotes: dict[Enum, int]):
def init(self, CP: CarParams, all_footnotes=None):
self.car_name = CP.carName
self.car_fingerprint = CP.carFingerprint

if self.merged and CP.dashcamOnly:
if self.support_type != SupportType.REVIEW:
self.support_type = SupportType.DASHCAM
self.support_link = "#dashcam"
else:
self.support_link = "#under-review"

# longitudinal column
op_long = "Stock"
if CP.experimentalLongitudinalAvailable or CP.enableDsu:
Expand Down Expand Up @@ -308,6 +335,18 @@ def display_func(parts):
Column.VIDEO: self.video_link if self.video_link is not None else "", # replaced with an image and link from template in get_column
}

if self.support_link is not None:
support_info = f"[{self.support_type.value}]({self.support_link})"
else:
support_info = self.support_type.value

self.extra_cars_row: dict[Enum, str] = {
ExtraCarsColumn.MAKE: self.make,
ExtraCarsColumn.MODEL: self.model,
ExtraCarsColumn.PACKAGE: self.package,
ExtraCarsColumn.SUPPORT: support_info,
}

# Set steering torque star from max lateral acceleration
assert CP.maxLateralAccel > 0.1
if CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD:
Expand Down Expand Up @@ -368,3 +407,18 @@ def get_column(self, column: Column, star_icon: str, video_icon: str, footnote_t
item += footnote_tag.format(f'{",".join(map(str, sups))}')

return item

def get_extra_cars_column(self, column: ExtraCarsColumn) -> str:
item: str = self.extra_cars_row[column]
if column == ExtraCarsColumn.MODEL and len(self.years):
item += f" {self.years}"

return item


@dataclass
class ExtraCarDocs(CarDocs):
package: str = "Any"
merged: bool = False
support_type: SupportType = SupportType.INCOMPATIBLE
support_link: str | None = "#incompatible"
62 changes: 62 additions & 0 deletions opendbc/car/extra_cars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from dataclasses import dataclass

from opendbc.car import structs, Platforms, ExtraPlatformConfig
from opendbc.car.docs_definitions import ExtraCarDocs, SupportType


@dataclass
class CommunityCarDocs(ExtraCarDocs):
def init_make(self, CP: structs.CarParams):
self.support_type = SupportType.COMMUNITY
self.support_link = "#community"


@dataclass
class ToyotaSecurityCarDocs(ExtraCarDocs):
def init_make(self, CP: structs.CarParams):
self.support_type = SupportType.INCOMPATIBLE
self.support_link = "#can-bus-security"


@dataclass
class FlexRayCarDocs(ExtraCarDocs):
def init_make(self, CP: structs.CarParams):
self.support_type = SupportType.INCOMPATIBLE
self.support_link = "#flexray"


class CAR(Platforms):
config: ExtraPlatformConfig

HYUNDAI_PALISADE_FACELIFT = ExtraPlatformConfig(
[
CommunityCarDocs("Hyundai Palisade 2023-24", package="HDA2"),
CommunityCarDocs("Kia Telluride 2023-24", package="HDA2"),
],
)

TOYOTA_SECURITY_CARS = ExtraPlatformConfig(
[
ToyotaSecurityCarDocs("Subaru Solterra 2023-25"),
ToyotaSecurityCarDocs("Lexus NS 2022-25"),
ToyotaSecurityCarDocs("Toyota bZ4x 2023-25"),
ToyotaSecurityCarDocs("Toyota Camry 2025"),
ToyotaSecurityCarDocs("Toyota Corolla Cross 2022-25"),
ToyotaSecurityCarDocs("Toyota Highlander 2025"),
CommunityCarDocs("Toyota RAV4 Prime 2021-23"),
ToyotaSecurityCarDocs("Toyota RAV4 Prime 2024-25"),
ToyotaSecurityCarDocs("Toyota Sequoia 2023-25"),
CommunityCarDocs("Toyota Sienna 2021-23"),
ToyotaSecurityCarDocs("Toyota Sienna 2024-25"),
ToyotaSecurityCarDocs("Toyota Tundra 2022-25"),
ToyotaSecurityCarDocs("Toyota Venza 2021-25"),
],
)

AUDI_FLEXRAY = ExtraPlatformConfig(
[
FlexRayCarDocs("Audi A4 2016-24", package="All"),
FlexRayCarDocs("Audi A5 2016-24", package="All"),
FlexRayCarDocs("Audi Q5 2017-24", package="All"),
],
)
Loading
Loading