Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
49 changes: 43 additions & 6 deletions opendbc/can/dbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,37 @@ class Val:
VAL_SPLIT_RE = re.compile(r'["]+')


# Module-level cache for parsed DBC files
_DBC_CACHE: dict[str, "DBC"] = {}


def _resolve_dbc_path(name: str) -> str:
"""Resolve a DBC name or path to an absolute path."""
dbc_path = name
if not os.path.exists(dbc_path):
candidate = os.path.join(DBC_PATH, name + ".dbc")
if os.path.exists(candidate):
dbc_path = candidate
else:
raise FileNotFoundError(f"DBC file not found: {name}")
return os.path.abspath(dbc_path)


def get_dbc(name: str) -> "DBC":
"""Get or create a cached DBC instance.

Args:
name: DBC file name (without .dbc extension) or full path to DBC file

Returns:
Cached DBC instance for the resolved path
"""
dbc_path = _resolve_dbc_path(name)
if dbc_path not in _DBC_CACHE:
_DBC_CACHE[dbc_path] = DBC._from_file(dbc_path)
return _DBC_CACHE[dbc_path]


@dataclass
class DBC:
name: str
Expand All @@ -79,12 +110,18 @@ class DBC:
name_to_msg: dict[str, Msg]
vals: list[Val]

def __init__(self, name: str):
dbc_path = name
if not os.path.exists(dbc_path):
dbc_path = os.path.join(DBC_PATH, name + ".dbc")

self._parse(dbc_path)
@classmethod
def _from_file(cls, path: str) -> "DBC":
"""Internal method to create a DBC instance from a file path."""
dbc = cls(
name="",
msgs={},
addr_to_msg={},
name_to_msg={},
vals=[]
)
dbc._parse(path)
return dbc

def _parse(self, path: str):
self.name = os.path.basename(path).replace(".dbc", "")
Expand Down
4 changes: 2 additions & 2 deletions opendbc/can/packer.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import math

from opendbc.car.carlog import carlog
from opendbc.can.dbc import DBC, Signal, SignalType
from opendbc.can.dbc import get_dbc, Signal, SignalType


class CANPacker:
def __init__(self, dbc_name: str):
self.dbc = DBC(dbc_name)
self.dbc = get_dbc(dbc_name)
self.counters: dict[int, int] = {}

def pack(self, address: int, values: dict[str, float]) -> bytearray:
Expand Down
9 changes: 6 additions & 3 deletions opendbc/can/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dataclasses import dataclass, field

from opendbc.car.carlog import carlog
from opendbc.can.dbc import DBC, Signal
from opendbc.can.dbc import get_dbc, Signal


MAX_BAD_COUNTER = 5
Expand Down Expand Up @@ -129,7 +129,7 @@ class CANParser:
def __init__(self, dbc_name: str, messages: list[tuple[str | int, int]], bus: int):
self.dbc_name: str = dbc_name
self.bus: int = bus
self.dbc: DBC = DBC(dbc_name)
self.dbc = get_dbc(dbc_name)

self.vl: dict[int | str, dict[str, float]] = VLDict(self)
self.vl_all: dict[int | str, dict[str, list[float]]] = {}
Expand Down Expand Up @@ -214,6 +214,9 @@ def can_valid(self) -> bool:
return self.can_invalid_cnt < CAN_INVALID_CNT and counters_valid

def update(self, strings, sendcan: bool = False):
if not strings:
return set()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? Is it necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change handles the case where strings could be None, preventing a potential TypeError when trying to check strings[0] on line 220. This makes the code more robust against unexpected inputs.

Current tests pass empty lists, other code paths might pass None at a later time, and this prevents runtime errors

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's type hint strings to make it clear then

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this is a separate, unrelated change to this PR? you can open a new PR for it

if strings and not isinstance(strings[0], list | tuple):
strings = [strings]

Expand Down Expand Up @@ -255,7 +258,7 @@ def update(self, strings, sendcan: bool = False):

class CANDefine:
def __init__(self, dbc_name: str):
dbc = DBC(dbc_name)
dbc = get_dbc(dbc_name)

dv = defaultdict(dict)
for val in dbc.vals:
Expand Down