feat: Squash all min-features into full
This commit is contained in:
@@ -20,6 +20,24 @@ from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_I
|
||||
from openpilot.common.swaglog import cloudlog, add_file_handler
|
||||
from openpilot.system.version import get_build_metadata
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.manager.vehicle_model_collector import VehicleModelCollector
|
||||
|
||||
# rick - dynamically import panda
|
||||
import importlib
|
||||
|
||||
# Pre-register panda_main as panda before loading it
|
||||
target_mod = "panda_tici" if "TICI_DOS" in os.environ else "panda"
|
||||
|
||||
print(f"panda dir: {target_mod}")
|
||||
|
||||
_mod = importlib.import_module(target_mod)
|
||||
|
||||
# 👇 Insert alias so "from panda import ..." inside panda_main works
|
||||
sys.modules["panda"] = _mod
|
||||
|
||||
# Re-export everything
|
||||
globals().update({k: v for k, v in _mod.__dict__.items() if not k.startswith("_")})
|
||||
import time
|
||||
|
||||
|
||||
def manager_init() -> None:
|
||||
@@ -43,6 +61,7 @@ def manager_init() -> None:
|
||||
default_value = params.get_default_value(k)
|
||||
if default_value is not None and params.get(k) is None:
|
||||
params.put(k, default_value, block=True)
|
||||
params.put("dp_dev_model_list", VehicleModelCollector().get_json())
|
||||
|
||||
# Create folders needed for msgq
|
||||
try:
|
||||
@@ -68,7 +87,8 @@ def manager_init() -> None:
|
||||
if reg_res:
|
||||
dongle_id = reg_res
|
||||
else:
|
||||
raise Exception(f"Registration failed for device {serial}")
|
||||
dongle_id = "UnregisteredDevice"
|
||||
# raise Exception(f"Registration failed for device {serial}")
|
||||
os.environ['DONGLE_ID'] = dongle_id # Needed for swaglog
|
||||
os.environ['GIT_ORIGIN'] = build_metadata.openpilot.git_normalized_origin # Needed for swaglog
|
||||
os.environ['GIT_BRANCH'] = build_metadata.channel # Needed for swaglog
|
||||
@@ -125,6 +145,15 @@ def manager_thread() -> None:
|
||||
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
|
||||
started_prev = False
|
||||
|
||||
dp_dev_delay_time_started: float = 0.
|
||||
dp_dev_delay_loggerd = int(params.get('dp_dev_delay_loggerd') or 0)
|
||||
|
||||
# Dictionary of processes to be delayed [process_name: delay_seconds]
|
||||
dp_dev_delay_start_times: dict[str, float] = {
|
||||
'loggerd': dp_dev_delay_loggerd,
|
||||
'encoderd': dp_dev_delay_loggerd
|
||||
}
|
||||
ignition_prev = False
|
||||
|
||||
while True:
|
||||
@@ -145,10 +174,22 @@ def manager_thread() -> None:
|
||||
if started != started_prev:
|
||||
write_onroad_params(started, params)
|
||||
|
||||
dp_ignore: list[str] = []
|
||||
if started and not started_prev:
|
||||
dp_dev_delay_time_started = time.monotonic()
|
||||
elif not started and started_prev:
|
||||
dp_dev_delay_time_started = 0.
|
||||
|
||||
if dp_dev_delay_time_started > 0.:
|
||||
cur_time = time.monotonic()
|
||||
for name, delay_time in dp_dev_delay_start_times.items():
|
||||
if cur_time - dp_dev_delay_time_started < delay_time: # type: ignore
|
||||
dp_ignore.append(name)
|
||||
|
||||
started_prev = started
|
||||
ignition_prev = ignition
|
||||
|
||||
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=list(set(ignore) | set(dp_ignore)))
|
||||
|
||||
running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
|
||||
for p in managed_processes.values() if p.proc)
|
||||
|
||||
@@ -8,6 +8,9 @@ from openpilot.system.hardware import PC, TICI
|
||||
from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess
|
||||
|
||||
WEBCAM = os.getenv("USE_WEBCAM") is not None
|
||||
LITE = os.getenv("LITE") is not None
|
||||
|
||||
TICI_DOS = "TICI_DOS" in os.environ
|
||||
|
||||
def driverview(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started or params.get_bool("IsDriverViewEnabled")
|
||||
@@ -46,9 +49,15 @@ def lat_maneuver(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
def not_long_maneuver(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and not params.get_bool("LongitudinalManeuverMode")
|
||||
|
||||
def opview(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and params.get_bool("dp_dev_opview")
|
||||
|
||||
def qcomgps(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and not ublox_available()
|
||||
|
||||
def beep(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and params.get_bool("dp_dev_beep")
|
||||
|
||||
def always_run(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return True
|
||||
|
||||
@@ -58,6 +67,12 @@ def only_onroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
def only_offroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return not started
|
||||
|
||||
def dashy(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return params.get_bool("dp_dev_dashy")
|
||||
|
||||
def comma_connect(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return not params.get_bool("dp_dev_disable_connect")
|
||||
|
||||
def or_(*fns):
|
||||
return lambda *args: operator.or_(*(fn(*args) for fn in fns))
|
||||
|
||||
@@ -69,14 +84,14 @@ procs = [
|
||||
|
||||
NativeProcess("loggerd", "system/loggerd", ["./loggerd"], logging),
|
||||
NativeProcess("encoderd", "system/loggerd", ["./encoderd"], only_onroad),
|
||||
NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar),
|
||||
NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], or_(notcar, opview)),
|
||||
PythonProcess("logmessaged", "system.logmessaged", always_run),
|
||||
|
||||
NativeProcess("camerad", "system/camerad", ["./camerad"], driverview, enabled=not WEBCAM),
|
||||
PythonProcess("webcamerad", "tools.webcam.camerad", driverview, enabled=WEBCAM),
|
||||
PythonProcess("proclogd", "system.proclogd", only_onroad, enabled=platform.system() != "Darwin"),
|
||||
PythonProcess("journald", "system.journald", only_onroad, platform.system() != "Darwin"),
|
||||
PythonProcess("micd", "system.micd", iscar),
|
||||
PythonProcess("micd", "system.micd", iscar, enabled=not LITE),
|
||||
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
|
||||
|
||||
PythonProcess("modeld", "selfdrive.modeld.modeld", only_onroad),
|
||||
@@ -84,7 +99,8 @@ procs = [
|
||||
|
||||
PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC),
|
||||
PythonProcess("ui", "selfdrive.ui.ui", always_run, restart_if_crash=True),
|
||||
PythonProcess("soundd", "selfdrive.ui.soundd", driverview),
|
||||
PythonProcess("soundd", "selfdrive.ui.soundd", driverview, enabled=not LITE),
|
||||
PythonProcess("beepd", "dragonpilot.selfdrive.ui.beepd", beep, enabled=TICI and LITE),
|
||||
PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad),
|
||||
NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False),
|
||||
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad),
|
||||
@@ -96,7 +112,7 @@ procs = [
|
||||
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
||||
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(WEBCAM or not PC)),
|
||||
PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
|
||||
PythonProcess("pandad", "selfdrive.pandad.pandad", always_run),
|
||||
PythonProcess("pandad", "selfdrive.pandad.pandad" if not TICI_DOS else "selfdrive.pandad_tici.pandad", always_run),
|
||||
PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad),
|
||||
PythonProcess("lagd", "selfdrive.locationd.lagd", only_onroad),
|
||||
PythonProcess("ubloxd", "system.ubloxd.ubloxd", ublox, enabled=TICI),
|
||||
@@ -106,18 +122,22 @@ procs = [
|
||||
PythonProcess("lateral_maneuversd", "tools.lateral_maneuvers.lateral_maneuversd", lat_maneuver),
|
||||
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
|
||||
PythonProcess("hardwared", "system.hardware.hardwared", always_run),
|
||||
PythonProcess("modem", "system.hardware.tici.modem", always_run, enabled=TICI),
|
||||
PythonProcess("modem", "system.hardware.tici.modem", always_run, enabled=TICI and not LITE),
|
||||
PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC),
|
||||
PythonProcess("updated", "system.updated.updated", only_offroad, enabled=not PC),
|
||||
PythonProcess("uploader", "system.loggerd.uploader", always_run),
|
||||
PythonProcess("uploader", "system.loggerd.uploader", and_(comma_connect, always_run)),
|
||||
PythonProcess("statsd", "system.statsd", always_run),
|
||||
PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad),
|
||||
|
||||
# debug procs
|
||||
NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar),
|
||||
PythonProcess("webrtcd", "system.webrtc.webrtcd", notcar),
|
||||
PythonProcess("webrtcd", "system.webrtc.webrtcd", or_(notcar, opview)),
|
||||
PythonProcess("webjoystick", "tools.bodyteleop.web", notcar),
|
||||
PythonProcess("joystick", "tools.joystick.joystick_control", and_(joystick, iscar)),
|
||||
|
||||
# dashy
|
||||
PythonProcess("serverd", "dragonpilot.dashy.serverd", always_run),
|
||||
PythonProcess("dashyd", "dragonpilot.dashy.dashyd", and_(dashy, only_onroad)),
|
||||
]
|
||||
|
||||
managed_processes = {p.name: p for p in procs}
|
||||
|
||||
Executable
+169
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Copyright (c) 2025 Rick Lan
|
||||
|
||||
This software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).
|
||||
You are free to share and adapt this work for non-commercial purposes, provided you give appropriate credit and distribute any modifications under the same license.
|
||||
|
||||
To view a copy of this license, visit:
|
||||
http://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
---
|
||||
|
||||
**Commercial Licensing:**
|
||||
Use of this software for commercial purposes is strictly prohibited without a separate, paid license.
|
||||
To purchase a commercial license, please contact ricklan@gmail.com.
|
||||
"""
|
||||
|
||||
import os
|
||||
import importlib
|
||||
import json
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
# from openpilot.common.params import Params
|
||||
|
||||
|
||||
class VehicleModelCollector:
|
||||
def __init__(self):
|
||||
self.base_package = "opendbc.car"
|
||||
self.base_path = f"{BASEDIR}/opendbc/car"
|
||||
self.exclude_brands = ['body', 'mock']
|
||||
|
||||
# Define the lookup dictionary for brand-to-group mappings
|
||||
self.brand_to_group_map = {
|
||||
"chrysler": [
|
||||
{"prefix": "DODGE_", "group": "Chrysler"},
|
||||
{"prefix": "RAM_", "group": "Chrysler"},
|
||||
{"prefix": "JEEP_", "group": "Chrysler"},
|
||||
],
|
||||
"gm": [
|
||||
{"prefix": "BUICK_", "group": "GM"},
|
||||
{"prefix": "CADILLAC_", "group": "GM"},
|
||||
{"prefix": "CHEVROLET_", "group": "GM"},
|
||||
{"prefix": "HOLDEN_", "group": "GM"},
|
||||
],
|
||||
"honda": {"prefix": "ACURA_", "group": "Honda"},
|
||||
"toyota": {"prefix": "LEXUS_", "group": "Toyota"},
|
||||
"hyundai": [
|
||||
{"prefix": "KIA_", "group": "Hyundai"},
|
||||
{"prefix": "GENESIS_", "group": "Hyundai"}
|
||||
],
|
||||
"volkswagen": [
|
||||
{"prefix": "AUDI_", "group": "Volkswagen"},
|
||||
{"prefix": "SKODA_", "group": "Volkswagen"},
|
||||
{"prefix": "SEAT_", "group": "Volkswagen"}
|
||||
]
|
||||
}
|
||||
|
||||
# Define exceptions for group names
|
||||
self.group_name_exceptions = {
|
||||
"gm": "GM",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def is_car_model(car_class, attr):
|
||||
"""Check if the attribute is a car model (not callable and not a dunder attribute)"""
|
||||
return not callable(getattr(car_class, attr)) and not attr.startswith("__")
|
||||
|
||||
@staticmethod
|
||||
def move_to_proper_group(models, prefix):
|
||||
"""
|
||||
Moves models with a certain prefix to their respective group.
|
||||
Example: Models starting with 'LEXUS_' should go to 'Lexus' group.
|
||||
"""
|
||||
moved_models = []
|
||||
for model in models[:]: # Iterate over a copy to avoid modifying during iteration
|
||||
if model.startswith(prefix):
|
||||
moved_models.append(model)
|
||||
models.remove(model) # Remove from the original group
|
||||
return moved_models
|
||||
|
||||
def format_group_name(self, group_name):
|
||||
"""
|
||||
Formats group names according to the exceptions dictionary.
|
||||
Groups in the exceptions dictionary are returned in all caps, others are title cased.
|
||||
"""
|
||||
return self.group_name_exceptions.get(group_name, group_name.title())
|
||||
|
||||
def collect_models(self):
|
||||
"""Collect all car models and organize them by brand/group"""
|
||||
# List all subdirectories (car brands)
|
||||
car_brands = sorted([
|
||||
name for name in os.listdir(self.base_path)
|
||||
if os.path.isdir(os.path.join(self.base_path, name)) and not name.startswith("__")
|
||||
])
|
||||
|
||||
grouped_models = {}
|
||||
|
||||
# Import CAR from each subdirectory and group models by brand
|
||||
for brand in car_brands:
|
||||
if brand in self.exclude_brands:
|
||||
continue
|
||||
|
||||
module_name = f"{self.base_package}.{brand}.values"
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
if hasattr(module, "CAR"):
|
||||
car_class = getattr(module, "CAR")
|
||||
models = sorted([attr for attr in dir(car_class) if self.is_car_model(car_class, attr)])
|
||||
|
||||
# Check if the brand has a special group in the lookup map
|
||||
if brand in self.brand_to_group_map:
|
||||
group_info = self.brand_to_group_map[brand]
|
||||
|
||||
if isinstance(group_info, list): # If multiple prefixes for the brand
|
||||
for prefix_info in group_info:
|
||||
moved_models = self.move_to_proper_group(models, prefix_info["prefix"])
|
||||
if moved_models:
|
||||
if prefix_info["group"] not in grouped_models:
|
||||
grouped_models[prefix_info["group"]] = []
|
||||
grouped_models[prefix_info["group"]].extend(moved_models)
|
||||
else: # Single prefix for the brand
|
||||
moved_models = self.move_to_proper_group(models, group_info["prefix"])
|
||||
if moved_models:
|
||||
if group_info["group"] not in grouped_models:
|
||||
grouped_models[group_info["group"]] = []
|
||||
grouped_models[group_info["group"]].extend(moved_models)
|
||||
|
||||
# Add remaining models to the respective brand
|
||||
if models:
|
||||
grouped_models[brand] = models
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
# Merge groups that have the same formatted name (e.g., "chrysler" and "Chrysler")
|
||||
merged_grouped_models = {}
|
||||
for group_key, models_list in grouped_models.items():
|
||||
formatted_group_name = self.format_group_name(group_key)
|
||||
if formatted_group_name not in merged_grouped_models:
|
||||
merged_grouped_models[formatted_group_name] = []
|
||||
merged_grouped_models[formatted_group_name].extend(models_list)
|
||||
|
||||
# Create a new dictionary to hold the sorted models
|
||||
sorted_models = {}
|
||||
for group_key, models_list in merged_grouped_models.items():
|
||||
sorted_models[group_key] = sorted(models_list)
|
||||
|
||||
return sorted_models
|
||||
|
||||
# def save_to_params(self, output=None):
|
||||
# """Save the collected model list to Params"""
|
||||
# if output is None:
|
||||
# output = self.collect_models()
|
||||
# Params().put("dp_dev_model_list", json.dumps(output))
|
||||
# return output
|
||||
#
|
||||
# def run(self):
|
||||
# """Collect models and save to params"""
|
||||
# models = self.collect_models()
|
||||
# self.save_to_params(models)
|
||||
# return models
|
||||
|
||||
def get_json(self):
|
||||
return self.collect_models()
|
||||
|
||||
def get(self):
|
||||
return json.dumps(self.collect_models())
|
||||
|
||||
# Allow running as a script
|
||||
if __name__ == "__main__":
|
||||
collector = VehicleModelCollector()
|
||||
print(collector.get())
|
||||
Reference in New Issue
Block a user