Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Implement support for container and CAN-FD frames #151

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
13 changes: 10 additions & 3 deletions dbc2val/dbcfeeder.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def __init__(self, kuksa_client: clientwrapper.ClientWrapper,
def start(
self,
canport: str,
can_fd: bool,
dbc_file_names: List[str],
mappingfile: str,
dbc_default_file: Optional[str],
Expand Down Expand Up @@ -170,7 +171,7 @@ def start(
self._reader = j1939reader.J1939Reader(self._dbc2vss_queue, self._mapper, canport, candumpfile)
else:
log.info("Using DBC reader")
self._reader = dbcreader.DBCReader(self._dbc2vss_queue, self._mapper, canport, candumpfile)
self._reader = dbcreader.DBCReader(self._dbc2vss_queue, self._mapper, canport, can_fd, candumpfile)

if canport == 'elmcan':
log.info("Using elmcan. Trying to set up elm2can bridge")
Expand All @@ -196,7 +197,7 @@ def start(
# For now creating another bus
# Maybe support different buses for downstream/upstream in the future

self._canclient = CANClient(interface="socketcan", channel=canport)
self._canclient = CANClient(interface="socketcan", channel=canport, can_fd=can_fd)

transmitter = threading.Thread(target=self._run_transmitter)
transmitter.start()
Expand Down Expand Up @@ -451,6 +452,11 @@ def _get_command_line_args_parser() -> argparse.ArgumentParser:
action="store_true",
help="Use SocketCAN (overriding any use of --dumpfile)",
)
parser.add_argument(
Copy link
Contributor

Choose a reason for hiding this comment

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

this new parameter should probably also be documented in the README

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, and we should better discuss if we think this one need to be controllable by environment variable and config file as well, similar to how canport is managed.

if args.canport:
        canport = args.canport
    elif os.environ.get("CAN_PORT"):
        canport = os.environ.get("CAN_PORT")
    else:
        canport = config.get(CONFIG_SECTION_CAN, CONFIG_OPTION_PORT, fallback=None)

'--canfd',
action='store_true',
help="Open bus interface in CAN-FD mode"
)
parser.add_argument(
"--mapping",
metavar="FILE",
Expand Down Expand Up @@ -613,7 +619,8 @@ def signal_handler(signal_received, *_):
dbc_default_file=dbc_default,
candumpfile=candumpfile,
use_j1939=use_j1939,
use_strict_parsing=args.strict
use_strict_parsing=args.strict,
can_fd=args.canfd
)

return 0
Expand Down
106 changes: 63 additions & 43 deletions dbc2val/dbcfeederlib/canreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class CanReader(ABC):
"""
Provides means to read messages from a CAN bus.
"""
def __init__(self, rxqueue: Queue, mapper: Mapper, can_port: str, dump_file: Optional[str] = None):
def __init__(self, rxqueue: Queue, mapper: Mapper, can_port: str,
dump_file: Optional[str] = None, can_fd: bool = False):
"""
This init method is only supposed to be called by subclass' __init__ functions.
"""
Expand All @@ -44,7 +45,12 @@ def __init__(self, rxqueue: Queue, mapper: Mapper, can_port: str, dump_file: Opt

can_filters = mapper.can_frame_id_whitelist()
log.info("Using CAN frame ID whitelist=%s", can_filters)
self._can_kwargs: Dict[str, Any] = {"interface": "socketcan", "channel": can_port, "can_filters": can_filters}
self._can_kwargs: Dict[str, Any] = {
"interface": "socketcan",
"channel": can_port,
"can_filters": can_filters,
"fd": can_fd
}
if dump_file is not None:
self._can_kwargs["interface"] = "virtual"
self._can_kwargs["bitrate"] = 500000
Expand Down Expand Up @@ -83,44 +89,58 @@ def stop(self):
self._stop_can_bus_listener()

def _process_can_message(self, frame_id: int, data: Any):
try:
message_def = self._mapper.get_message_for_canid(frame_id)
if message_def is not None:
decode = message_def.decode(bytes(data), allow_truncated=True)
if log.isEnabledFor(logging.DEBUG):
log.debug("Decoded message: %s", str(decode))
rx_time = time.time()
for signal_name, raw_value in decode.items(): # type: ignore
signal: cantools.database.Signal = message_def.get_signal_by_name(signal_name)
if isinstance(raw_value, (int, float)):
# filter out signals with values out of defined range
# which is usually because the signal's value is unavailable
# (represented as e.g. "all bits set")
if signal.minimum is not None and raw_value < signal.minimum:
log.debug(
"discarding out-of-range value [signal: %s, min: %s, value: %s]",
signal_name, signal.minimum, raw_value
)
continue
if signal.maximum is not None and raw_value > signal.maximum:
log.debug(
"discarding out-of-range value [signal: %s, max: %s, value: %s]",
signal_name, signal.maximum, raw_value
)
continue
for signal_mapping in self._mapper.get_dbc2vss_mappings(signal_name):

if signal_mapping.time_condition_fulfilled(rx_time):
log.debug(
"Queueing %s, triggered by %s, raw value %s",
signal_mapping.vss_name, signal_name, raw_value
)
self._queue.put(VSSObservation(
signal_name, signal_mapping.vss_name, raw_value, rx_time))
else:
log.debug(
"Ignoring %s, triggered by %s, raw value %s",
signal_mapping.vss_name, signal_name, raw_value
)
except Exception:
log.warning("Error processing CAN message with frame ID: %#x", frame_id, exc_info=True)
message_def = self._mapper.get_message_for_canid(frame_id)
if message_def is not None:
try:
decode = message_def.decode(bytes(data), allow_truncated=True, decode_containers=True)
except Exception as e:
log.warning("Error processing CAN message with frame ID: %#x", frame_id, exc_info=True)
log.warning("Error: ", e)

if log.isEnabledFor(logging.DEBUG):
log.debug("Decoded message: %s", str(decode))
rx_time = time.time()

if isinstance(decode, dict):
# handle normal frame
self._handle_decoded_frame(message_def, decode, rx_time)
else:
# handle container frame
for tmp in decode:
if isinstance(tmp[1], bytes):
continue
self._handle_decoded_frame(tmp[0], tmp[1], rx_time)

def _handle_decoded_frame(self, message_def, decoded, rx_time):
for signal_name, raw_value in decoded.items(): # type: ignore
signal: cantools.database.Signal = message_def.get_signal_by_name(signal_name)
if isinstance(raw_value, (int, float)):
# filter out signals with values out of defined range
# which is usually because the signal's value is unavailable
# (represented as e.g. "all bits set")
if signal.minimum is not None and raw_value < signal.minimum:
log.debug(
"discarding out-of-range value [signal: %s, min: %s, value: %s]",
signal_name, signal.minimum, raw_value
)
continue
if signal.maximum is not None and raw_value > signal.maximum:
log.debug(
"discarding out-of-range value [signal: %s, max: %s, value: %s]",
signal_name, signal.maximum, raw_value
)
continue
for signal_mapping in self._mapper.get_dbc2vss_mappings(signal_name):

if signal_mapping.time_condition_fulfilled(rx_time):
log.debug(
"Queueing %s, triggered by %s, raw value %s",
signal_mapping.vss_name, signal_name, raw_value
)
self._queue.put(VSSObservation(
signal_name, signal_mapping.vss_name, raw_value, rx_time))
else:
log.debug(
"Ignoring %s, triggered by %s, raw value %s",
signal_mapping.vss_name, signal_name, raw_value
)
25 changes: 14 additions & 11 deletions dbc2val/dbcfeederlib/dbcparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self,
continue
processed_files.add(filename)
if first:
log.info("Reading definitions from DBC file %s", filename)
log.info("Reading definitions from bus description file %s", filename)
database = cantools.database.load_file(
filename,
strict=use_strict_parsing,
Expand Down Expand Up @@ -111,12 +111,14 @@ def get_canid_for_signal(self, sig_to_find: str) -> Optional[int]:
return self._signal_to_canid[sig_to_find]

for msg in self._db.messages:
for signal in msg.signals:
if signal.name == sig_to_find:
frame_id = msg.frame_id
log.debug("Found signal %s in CAN message with frame ID %#x", signal.name, frame_id)
self._signal_to_canid[sig_to_find] = frame_id
return frame_id
for inner_msg in [msg, *(msg.contained_messages or [])]:
for signal in inner_msg.signals:
if signal.name == sig_to_find:
frame_id = msg.frame_id
log.debug("Found signal %s in CAN message with frame ID %#x", signal.name, frame_id)
self._signal_to_canid[sig_to_find] = frame_id
return frame_id

log.warning("Signal %s not found in CAN message database", sig_to_find)
self._signal_to_canid[sig_to_find] = None
return None
Expand All @@ -127,10 +129,11 @@ def get_signals_for_canid(self, canid: int) -> Set[str]:
return self._canid_to_signals[canid]

names: Set[str] = set()
message = self.get_message_for_canid(canid)
if message is not None:
for signal in message.signals:
names.add(signal.name)
msg = self.get_message_for_canid(canid)
if msg is not None:
for inner_msg in [msg, *(msg.contained_messages or [])]:
for signal in inner_msg.signals:
names.add(signal.name)
self._canid_to_signals[canid] = names
return names

Expand Down
5 changes: 3 additions & 2 deletions dbc2val/dbcfeederlib/dbcreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@


class DBCReader(canreader.CanReader):
def __init__(self, rxqueue: Queue, mapper: dbc2vssmapper.Mapper, can_port: str, dump_file: Optional[str] = None):
super().__init__(rxqueue, mapper, can_port, dump_file)
def __init__(self, rxqueue: Queue, mapper: dbc2vssmapper.Mapper, can_port: str,
can_fd: bool, dump_file: Optional[str] = None):
super().__init__(rxqueue, mapper, can_port, dump_file, can_fd=can_fd)

def _rx_worker(self):

Expand Down
1 change: 0 additions & 1 deletion dds2val/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#################################################################################

import pytest
from py.xml import html


def pytest_html_report_title(report):
Expand Down
Loading