Examine the Contents of a Database

# examine_db.py
"""

This script uses canlib.kvadblib to parse a database and print its contents.

"""
import argparse
import sys

from canlib import kvadblib

INDENT = ' ' * 4


def print_db(db):
    print('DATABASE')
    print(db.name)
    for line in db_lines(db):
        print(INDENT + line)


def adef_lines(adef):
    yield 'type: ' + type(adef).__name__
    yield 'definition: ' + str(adef.definition)
    yield 'owner: ' + str(adef.owner)


def attr_lines(attrib):
    yield str(attrib.name) + ' = ' + str(attrib.value)


def db_lines(db):
    yield 'flags: ' + str(db.flags)
    yield 'protocol: ' + str(db.protocol)
    yield ''

    yield 'ATTRIBUTE DEFINITIONS'
    for adef in db.attribute_definitions():
        yield str(adef.name)
        for line in adef_lines(adef):
            yield INDENT + line
    yield ''

    yield 'MESSAGES'
    for message in db:
        yield str(message.name)
        for line in msg_lines(message):
            yield INDENT + line
    yield ''


def enum_lines(enums):
    for name, val in enums.items():
        yield str(name) + ' = ' + str(val)


def msg_lines(message):
    yield 'id: ' + str(message.id)
    yield 'flags: ' + str(message.flags)
    yield 'dlc: ' + str(message.dlc)
    yield 'comment: ' + str(message.comment)
    yield ''

    yield 'ATTRIBUTES'
    for attr in message.attributes():
        for line in attr_lines(attr):
            yield line
    yield ''

    yield 'SIGNALS'
    for signal in message:
        yield str(signal.name)
        for line in sig_lines(signal):
            yield INDENT + line
    yield ''


def sig_lines(signal):
    for name in ('type', 'byte_order', 'mode', 'size', 'scaling', 'limits', 'unit', 'comment'):
        yield name + ': ' + str(getattr(signal, name))
    yield ''

    try:
        enums = signal.enums
    except AttributeError:
        pass
    else:
        yield 'ENUMERATIONS'
        for line in enum_lines(enums):
            yield line
        yield ''

    yield 'ATTRIBUTES'
    for attr in signal.attributes():
        for line in attr_lines(attr):
            yield line
    yield ''


def examine_database(db_name):
    with kvadblib.Dbc(filename=db_name) as db:
        print_db(db)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__)
    parser.add_argument(
        'db', type=str, metavar='<database.dbc>', help='The dbc database file to examine.'
    )
    args = parser.parse_args()

    examine_database(args.db)

Description

The script is structured into several generator functions that take a canlib.kvadblib object and yield lines of information about it. This allows one function to add indentation to any other functions it uses.

Generally each function first yields information in the following order:

  1. Any information about the object itself (e.g. db.flags and db.protocol)

  2. An empty string

  3. For each type of sub-object (e.g. attribute definitions):

    1. A heading (e.g. 'ATTRIBUTE_DEFINTIONS')

    2. For each object of that type (e.g. iterating through canlib.kvadblib.Dbc.attribute_definitions):

      1. The objects name

      2. All lines from the *_lines function for the object type (e.g. adef_lines), with added indentation

    3. An empty string

Sample Output

Running this script on the database created by Create a Database gives the following:

DATABASE
engine_example
    flags: 0
    protocol: ProtocolType.CAN

    ATTRIBUTE DEFINITIONS
    BusType
        type: StringDefinition
        definition: DefaultDefinition(default='')
        owner: AttributeOwner.DB

    MESSAGES
    EngineData
        id: 100
        flags: MessageFlag.0
        dlc: 8
        comment:

        ATTRIBUTES

        SIGNALS
        PetrolLevel
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: -1
            size: ValueSize(startbit=24, length=8)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=255.0)
            unit: l
            comment:

            ATTRIBUTES

        EngPower
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: -1
            size: ValueSize(startbit=48, length=16)
            scaling: ValueScaling(factor=0.01, offset=0.0)
            limits: ValueLimits(min=0.0, max=150.0)
            unit: kW
            comment:

            ATTRIBUTES

        EngForce
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: -1
            size: ValueSize(startbit=32, length=16)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=0.0)
            unit: N
            comment:

            ATTRIBUTES

        IdleRunning
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: 0
            size: ValueSize(startbit=23, length=1)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=0.0)
            unit:
            comment:

            ENUMERATIONS
            Running = 0
            Idle = 1

            ATTRIBUTES

        EngTemp
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: -1
            size: ValueSize(startbit=16, length=7)
            scaling: ValueScaling(factor=2.0, offset=-50.0)
            limits: ValueLimits(min=-50.0, max=150.0)
            unit: °C
            comment:

            ATTRIBUTES

        EngSpeed
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: -1
            size: ValueSize(startbit=0, length=16)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=8000.0)
            unit: rpm
            comment:

            ATTRIBUTES


    GearBoxInfo
        id: 1020
        flags: MessageFlag.0
        dlc: 1
        comment:

        ATTRIBUTES

        SIGNALS
        EcoMode
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: -1
            size: ValueSize(startbit=6, length=2)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=1.0)
            unit:
            comment:

            ATTRIBUTES

        ShiftRequest
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: 0
            size: ValueSize(startbit=3, length=1)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=0.0)
            unit:
            comment:

            ENUMERATIONS
            Shift_Request_On = 1
            Shift_Request_Off = 0

            ATTRIBUTES

        Gear
            type: SignalType.UNSIGNED
            byte_order: SignalByteOrder.INTEL
            mode: 0
            size: ValueSize(startbit=0, length=3)
            scaling: ValueScaling(factor=1.0, offset=0.0)
            limits: ValueLimits(min=0.0, max=5.0)
            unit:
            comment:

            ENUMERATIONS
            Gear_5 = 5
            Gear_1 = 1
            Gear_3 = 3
            Idle = 0
            Gear_4 = 4
            Gear_2 = 2

            ATTRIBUTES