# 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 `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 `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