mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-10-01 03:43:42 +08:00
@@ -0,0 +1,58 @@
|
||||
# tools/car_porting
|
||||
|
||||
Check out [this blog post](https://blog.comma.ai/how-to-write-a-car-port-for-openpilot/) for a high-level overview of porting a car.
|
||||
|
||||
## Useful car porting utilities
|
||||
|
||||
Testing car ports in your car is very time-consuming. Check out these utilities to do basic checks on your work before running it in your car.
|
||||
|
||||
### [Cabana](/tools/cabana/README.md)
|
||||
|
||||
View your car's CAN signals through DBC files, which openpilot uses to parse and create messages that talk to the car.
|
||||
|
||||
Example:
|
||||
```bash
|
||||
> tools/cabana/cabana '1bbe6bf2d62f58a8|2022-07-14--17-11-43'
|
||||
```
|
||||
|
||||
### [tools/car_porting/auto_fingerprint.py](/tools/car_porting/auto_fingerprint.py)
|
||||
|
||||
Given a route and platform, automatically inserts FW fingerprints from the platform into the correct place in fingerprints.py
|
||||
|
||||
Example:
|
||||
```bash
|
||||
> python tools/car_porting/auto_fingerprint.py '1bbe6bf2d62f58a8|2022-07-14--17-11-43' 'SUBARU OUTBACK 6TH GEN'
|
||||
Attempting to add fw version for: SUBARU OUTBACK 6TH GEN
|
||||
```
|
||||
|
||||
### [selfdrive/car/tests/test_car_interfaces.py](/selfdrive/car/tests/test_car_interfaces.py)
|
||||
|
||||
Finds common bugs for car interfaces, without even requiring a route.
|
||||
|
||||
|
||||
#### Example: Typo in signal name
|
||||
```bash
|
||||
> pytest selfdrive/car/tests/test_car_interfaces.py -k subaru # replace with the brand you are working on
|
||||
|
||||
=====================================================================
|
||||
FAILED selfdrive/car/tests/test_car_interfaces.py::TestCarInterfaces::test_car_interfaces_165_SUBARU_LEGACY_7TH_GEN - KeyError: 'CruiseControlOOPS'
|
||||
|
||||
```
|
||||
|
||||
### [tools/car_porting/test_car_model.py](/tools/car_porting/test_car_model.py)
|
||||
|
||||
Given a route, runs most of the car interface to check for common errors like missing signals, blocked panda messages, and safety mismatches.
|
||||
|
||||
#### Example: panda safety mismatch for gasPressed
|
||||
```bash
|
||||
> python tools/car_porting/test_car_model.py '4822a427b188122a|2023-08-14--16-22-21'
|
||||
|
||||
=====================================================================
|
||||
FAIL: test_panda_safety_carstate (__main__.CarModelTestCase.test_panda_safety_carstate)
|
||||
Assert that panda safety matches openpilot's carState
|
||||
----------------------------------------------------------------------
|
||||
Traceback (most recent call last):
|
||||
File "/home/batman/xx/openpilot/openpilot/selfdrive/car/tests/test_models.py", line 380, in test_panda_safety_carstate
|
||||
self.assertFalse(len(failed_checks), f"panda safety doesn't agree with openpilot: {failed_checks}")
|
||||
AssertionError: 1 is not false : panda safety doesn't agree with openpilot: {'gasPressed': 116}
|
||||
```
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
from openpilot.selfdrive.debug.format_fingerprints import format_brand_fw_versions
|
||||
|
||||
from openpilot.tools.lib.logreader import MultiLogIterator
|
||||
from openpilot.tools.lib.route import Route
|
||||
from openpilot.selfdrive.car.fw_versions import match_fw_to_car
|
||||
from openpilot.selfdrive.car.interfaces import get_interface_attr
|
||||
|
||||
|
||||
ALL_FW_VERSIONS = get_interface_attr("FW_VERSIONS")
|
||||
ALL_CARS = get_interface_attr("CAR")
|
||||
|
||||
PLATFORM_TO_PYTHON_CAR_NAME = {brand: {car.value: car.name for car in ALL_CARS[brand]} for brand in ALL_CARS}
|
||||
BRAND_TO_PLATFORMS = {brand: [car.value for car in ALL_CARS[brand]] for brand in ALL_CARS}
|
||||
PLATFORM_TO_BRAND = dict(sum([[(platform, brand) for platform in BRAND_TO_PLATFORMS[brand]] for brand in BRAND_TO_PLATFORMS], []))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Auto fingerprint from a route")
|
||||
parser.add_argument("route", help="The route name to use")
|
||||
parser.add_argument("platform", help="The platform, or leave empty to auto-determine using fuzzy", default=None, nargs='?')
|
||||
args = parser.parse_args()
|
||||
|
||||
route = Route(args.route)
|
||||
lr = MultiLogIterator(route.qlog_paths())
|
||||
|
||||
carFw = None
|
||||
carVin = None
|
||||
carPlatform = None
|
||||
|
||||
platform: Optional[str] = None
|
||||
|
||||
for msg in lr:
|
||||
if msg.which() == "carParams":
|
||||
carFw = msg.carParams.carFw
|
||||
carVin = msg.carParams.carVin
|
||||
carPlatform = msg.carParams.carFingerprint
|
||||
break
|
||||
|
||||
if carFw is None:
|
||||
raise Exception("No fw versions in the provided route...")
|
||||
|
||||
if args.platform is None: # attempt to auto-determine platform with other fuzzy fingerprints
|
||||
_, possible_platforms = match_fw_to_car(carFw, log=False)
|
||||
|
||||
if len(possible_platforms) != 1:
|
||||
print(f"Unable to auto-determine platform, possible platforms: {possible_platforms}")
|
||||
|
||||
if carPlatform != "mock":
|
||||
print("Using platform from route")
|
||||
platform = carPlatform
|
||||
else:
|
||||
platform = None
|
||||
else:
|
||||
platform = list(possible_platforms)[0]
|
||||
else:
|
||||
platform = args.platform
|
||||
|
||||
if platform is None:
|
||||
raise Exception("unable to determine platform, try manually specifying the fingerprint.")
|
||||
|
||||
print("Attempting to add fw version for: ", platform)
|
||||
|
||||
fw_versions: dict[str, dict[tuple, list[bytes]]] = defaultdict(lambda: defaultdict(list))
|
||||
brand = PLATFORM_TO_BRAND[platform]
|
||||
|
||||
for fw in carFw:
|
||||
if fw.brand == brand and not fw.logging:
|
||||
addr = fw.address
|
||||
subAddr = None if fw.subAddress == 0 else fw.subAddress
|
||||
key = (fw.ecu.raw, addr, subAddr)
|
||||
|
||||
fw_versions[platform][key].append(fw.fwVersion)
|
||||
|
||||
format_brand_fw_versions(brand, fw_versions)
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import sys
|
||||
from typing import List
|
||||
import unittest
|
||||
|
||||
from openpilot.selfdrive.car.tests.routes import CarTestRoute
|
||||
from openpilot.selfdrive.car.tests.test_models import TestCarModel
|
||||
from openpilot.tools.lib.route import SegmentName
|
||||
|
||||
|
||||
def create_test_models_suite(routes: List[CarTestRoute], ci=False) -> unittest.TestSuite:
|
||||
test_suite = unittest.TestSuite()
|
||||
for test_route in routes:
|
||||
# create new test case and discover tests
|
||||
test_case_args = {"car_model": test_route.car_model, "test_route": test_route, "ci": ci}
|
||||
CarModelTestCase = type("CarModelTestCase", (TestCarModel,), test_case_args)
|
||||
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(CarModelTestCase))
|
||||
return test_suite
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Test any route against common issues with a new car port. " +
|
||||
"Uses selfdrive/car/tests/test_models.py")
|
||||
parser.add_argument("route_or_segment_name", help="Specify route to run tests on")
|
||||
parser.add_argument("--car", help="Specify car model for test route")
|
||||
parser.add_argument("--ci", action="store_true", help="Attempt to get logs using openpilotci, need to specify car")
|
||||
args = parser.parse_args()
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
route_or_segment_name = SegmentName(args.route_or_segment_name.strip(), allow_route_name=True)
|
||||
segment_num = route_or_segment_name.segment_num if route_or_segment_name.segment_num != -1 else None
|
||||
test_route = CarTestRoute(route_or_segment_name.route_name.canonical_name, args.car, segment=segment_num)
|
||||
test_suite = create_test_models_suite([test_route], ci=args.ci)
|
||||
|
||||
unittest.TextTestRunner().run(test_suite)
|
||||
Reference in New Issue
Block a user