""" Defines common socketcan functions. """ from typing import cast, Iterable, Optional import can.typechecking as typechecking import logging import os import errno import struct import subprocess import re from can.interfaces.socketcan.constants import CAN_EFF_FLAG log = logging.getLogger(__name__) def pack_filters(can_filters: Optional[typechecking.CanFilters] = None) -> bytes: if can_filters is None: # Pass all messages can_filters = [{"can_id": 0, "can_mask": 0}] can_filter_fmt = "={}I".format(2 * len(can_filters)) filter_data = [] for can_filter in can_filters: can_id = can_filter["can_id"] can_mask = can_filter["can_mask"] if "extended" in can_filter: can_filter = cast(typechecking.CanFilterExtended, can_filter) # Match on either 11-bit OR 29-bit messages instead of both can_mask |= CAN_EFF_FLAG if can_filter["extended"]: can_id |= CAN_EFF_FLAG filter_data.append(can_id) filter_data.append(can_mask) return struct.pack(can_filter_fmt, *filter_data) _PATTERN_CAN_INTERFACE = re.compile(r"v?can\d+") def find_available_interfaces() -> Iterable[str]: """Returns the names of all open can/vcan interfaces using the ``ip link list`` command. If the lookup fails, an error is logged to the console and an empty list is returned. :rtype: an iterable of :class:`str` """ try: # it might be good to add "type vcan", but that might (?) exclude physical can devices command = ["ip", "-o", "link", "list", "up"] output = subprocess.check_output(command, universal_newlines=True) except Exception as e: # subprocess.CalledProcessError was too specific log.error("failed to fetch opened can devices: %s", e) return [] else: # log.debug("find_available_interfaces(): output=\n%s", output) # output contains some lines like "1: vcan42: ..." # extract the "vcan42" of each line interface_names = [line.split(": ", 3)[1] for line in output.splitlines()] log.debug("find_available_interfaces(): detected: %s", interface_names) return filter(_PATTERN_CAN_INTERFACE.match, interface_names) def error_code_to_str(code: int) -> str: """ Converts a given error code (errno) to a useful and human readable string. :param int code: a possibly invalid/unknown error code :rtype: str :returns: a string explaining and containing the given error code, or a string explaining that the errorcode is unknown if that is the case """ try: name = errno.errorcode[code] except KeyError: name = "UNKNOWN" try: description = os.strerror(code) except ValueError: description = "no description available" return "{} (errno {}): {}".format(name, code, description)