""" logger.py logs CAN traffic to the terminal and to a file on disk. logger.py can0 See candump in the can-utils package for a C implementation. Efficient filtering has been implemented for the socketcan backend. For example the command logger.py can0 F03000:FFF000 Will filter for can frames with a can_id containing XXF03XXX. Dynamic Controls 2010 """ import sys import argparse import socket from datetime import datetime import can from can import Bus, BusState, Logger def main(): parser = argparse.ArgumentParser( "python -m can.logger", description="Log CAN traffic, printing messages to stdout or to a given file.", ) parser.add_argument( "-f", "--file_name", dest="log_file", help="""Path and base log filename, for supported types see can.Logger.""", default=None, ) parser.add_argument( "-v", action="count", dest="verbosity", help="""How much information do you want to see at the command line? You can add several of these e.g., -vv is DEBUG""", default=2, ) parser.add_argument( "-c", "--channel", help='''Most backend interfaces require some sort of channel. For example with the serial interface the channel might be a rfcomm device: "/dev/rfcomm0" With the socketcan interfaces valid channel examples include: "can0", "vcan0"''', ) parser.add_argument( "-i", "--interface", dest="interface", help="""Specify the backend CAN interface to use. If left blank, fall back to reading from configuration files.""", choices=can.VALID_INTERFACES, ) parser.add_argument( "--filter", help="""Comma separated filters can be specified for the given CAN interface: : (matches when & mask == can_id & mask) ~ (matches when & mask != can_id & mask) """, nargs=argparse.REMAINDER, default="", ) parser.add_argument( "-b", "--bitrate", type=int, help="""Bitrate to use for the CAN bus.""" ) parser.add_argument("--fd", help="Activate CAN-FD support", action="store_true") parser.add_argument( "--data_bitrate", type=int, help="""Bitrate to use for the data phase in case of CAN-FD.""", ) state_group = parser.add_mutually_exclusive_group(required=False) state_group.add_argument( "--active", help="Start the bus as active, this is applied by default.", action="store_true", ) state_group.add_argument( "--passive", help="Start the bus as passive.", action="store_true" ) # print help message when no arguments wre given if len(sys.argv) < 2: parser.print_help(sys.stderr) import errno raise SystemExit(errno.EINVAL) results = parser.parse_args() verbosity = results.verbosity logging_level_name = ["critical", "error", "warning", "info", "debug", "subdebug"][ min(5, verbosity) ] can.set_logging_level(logging_level_name) can_filters = [] if results.filter: print(f"Adding filter(s): {results.filter}") for filt in results.filter: if ":" in filt: _ = filt.split(":") can_id, can_mask = int(_[0], base=16), int(_[1], base=16) elif "~" in filt: can_id, can_mask = filt.split("~") can_id = int(can_id, base=16) | 0x20000000 # CAN_INV_FILTER can_mask = int(can_mask, base=16) & socket.CAN_ERR_FLAG can_filters.append({"can_id": can_id, "can_mask": can_mask}) config = {"can_filters": can_filters, "single_handle": True} if results.interface: config["interface"] = results.interface if results.bitrate: config["bitrate"] = results.bitrate if results.fd: config["fd"] = True if results.data_bitrate: config["data_bitrate"] = results.data_bitrate bus = Bus(results.channel, **config) if results.active: bus.state = BusState.ACTIVE elif results.passive: bus.state = BusState.PASSIVE print(f"Connected to {bus.__class__.__name__}: {bus.channel_info}") print(f"Can Logger (Started on {datetime.now()})") logger = Logger(results.log_file) try: while True: msg = bus.recv(1) if msg is not None: logger(msg) except KeyboardInterrupt: pass finally: bus.shutdown() logger.stop() if __name__ == "__main__": main()