Reading signals from log files

  • February 22, 2018
  • Magnus Carlsson

We have seen in earlier blog articles how to create a DBC database and how to use signals from the database while sending and receiving CAN traffic.1 This time we’ll take a look at how we can use the DBC database to show signals from logged data.


Configure the logger

In order to capture our signals we need to first configure our logger. I have a Kvaser Memorator Pro 5xHS with EAN 00778-9 connected to my PC, you should change the EAN number to match your own device.

from canlib import kvDevice
from canlib import kvamemolibxml

# Use .strip() to remove first newline , otherwise we get the error :
# XML declaration allowed only at the start of the document
CONFIG_XML = ”””
<?xml version=”1.0” ?>
<!DOCTYPE KVASER>
<KVASER>
  <VERSION>2.0</VERSION>
  <BINARY_VERSION>6.0</BINARY_VERSION>
  <SETTINGS>
    <MODE fifo_mode=”NO” log_all=”YES”/>
  </SETTINGS>
  <CAN_BUS>
    <PARAMETERS bitrate=”1000000” channel=”0” silent=”YES” sjw=”1” tseg1=”5”
         tseg2=”2”/>
    <PARAMETERS bitrate=”1000000” channel=”1” silent=”YES” sjw=”1” tseg1=”5”
         tseg2=”2”/>
  </CAN_BUS>
  <TRIGGERBLOCK/>
  <SCRIPTS/>
</KVASER>
”””.strip()


def init_card(dev):
    ”””Format disk on device”””
    # Open the device
    dev .memoOpen()

    # Initialize the SD card with default values
    print(’Formatting Card on:\n%s’ % dev)
    dev .memo. deviceFormatDisk()

    # Convert the XML configuration to a binary configuration
    config_lif = kvamemolibxml. kvaXmlToBuffer(CONFIG_XML)

    # write the configuration
    dev.memo.kmfWriteConfig(config_lif)

    # Close the device
    dev.memoClose()


def main():
    # Connect to our Kvaser Memorator Pro 5xHS with EAN 00778-9
    dev = kvDevice.kvDevice(ean=”73-30130-00778-9”)
    init_card(dev)


if __name__ == ’__main__’:
     main()

Listing 2: Basic configuration to log everything on channel one and two at one Mbit/s using a Kvaser Memorator Pro 5xHS.


Creating the sample data

Now we need data so why not generate some random data on signals available in our sample DBC database db_histogram.dbc.2 In the DBC database we have four messages defined, each containing a single signal.

# declare messages and one signal in each message
SIGNALS = { ’LIM_002’ : ’Load ’ ,
            ’ECM_004’ : ’Fuel ’ ,
            ’ECM_003’ : ’EngineTemp ’ ,
            ’ECM_001’ : ’EngineSpeed ’}

Given the database DB, we now want to create a random signal with random content. We do this by first randomly selecting a message from the SIGNALS defined above. Next, we find the correct signal we would like to use, also by referencing our SIGNALS. Now, to send the signal, we actually need to allocate space for the data and also set ID and DLC according to the message specification in the database. We do this by “binding” the signal.

# pick a message name at random
msg_name = random.choice(list(SIGNALS))

# get the message from our database
msg = db.get_message_by_name(msg_name)

# get the signal contained in the message and
# create a BoundSignal, containing an empty frame
# with correct ID and DLC
sig_name = SIGNALS[msg_name]
bsig = msg.get_signal_by_name(sig_name).bind()

To fill the signal with valid data, we retrieve the signal’s specified limits and create a random value that is within these limits. We also round the value off based on the type of the signal and finally set the signal’s physical value.3

# get the signal, which holds the definitions
sig = bsig . signal

# get the signal’s limits
limits = sig.limits

# create a value (with many decimals) within the limits
value = random.uniform(limits.min, limits.max)

# round value depending on type...
if sig.type is kvadblib.SignalType.UNSIGNED:
    # ...remove decimals if the signal was of type unsigned
    value = int(value)
else:
    # ...otherwise, round to get only one decimal
    value = round(value, 1)

# Now we would like to set the physical value
bsig.phys = value

We put all of this into a nice function which we call get_random_bound_signal.

def get_random_bound_signal(db) :
    ”””Create a bmsg, randomly choosing a message/signal pair from SIGNALS”””
    # pick a message name at random
    msg_name = random.choice(list(SIGNALS))

    # get the message from our database
    msg = db.get_message_by_name(msg_name)

    # get the signal contained in the message and
    # create a BoundSignal, containing an empty frame
    # with correct ID and DLC
    sig_name = SIGNALS[msg_name]
    bsig = msg.get_signal_by_name(sig_name).bind()
    # get the signal, which holds the definitions
    sig = bsig.signal

    # get the signal’s limits
    limits = sig.limits

    # create a value (with many decimals) within the limits
    value = random.uniform( limits.min, limits.max)

    # round value depending on type...
    if sig.type is kvadblib.SignalType.UNSIGNED:
        # ...remove decimals if the signal was of type unsigned
        value = int (value)
    else:
        # ...otherwise, round to get only one decimal
        value = round(value, 1)

    # Now we would like to set the physical value
    bsig.phys = value

    return bsig

Later on, we would like to print the contents of a bound signal so let us write a function for this as well while we are at it.

def bsignal_str ( bsignal ) :
    ”””Create a nice looking string of the contents of the bound signal ”””
    value = bsignal.phys
    if bsignal.signal.type i s kvadblib.SignalType.UNSIGNED:
        # remove decimals if the signal was of type unsigned
        value = int(value)
    else:
        # otherwise, round to get only one decimal
        value = round(value, 1)

    # create adjusted strings for prettier printing
    name = str(bsignal.name).rjust(11)
    val = str(value).rjust(5)
    return ’%s: %s %s’ % (name, val, bsignal.unit)

The next step is to use our previously generated bound signal, print the contents and send it on our transmit channel (ch_tx). We also read it as an incoming frame from our receiving channel (ch_rx) and prints the contents to see that it matches what we sent.

bsig0 = get_random_bound_signal(db)
print (’Sending %s ’ % bsignal_str(bsig0))

# send frame
ch_tx.write(bsig0.frame)

# read frame
frame = ch_rx.read(timeout=100)
bmsg1 = db.interpret(frame=frame)
print(’Receiving %s’ % bmessage_str(bmsg1))

To print a bound message we actually just loop through all contained bound signals and print those.

def bmessage_str(bmsg):
    "””Create a nice looking string of the contents of the bound message”””
    txt = ’’
    for bsig in bmsg:
        txt += ’%s’ % bsignal_str(bsig)
    return txt

The only thing we have to add now is some configuration of the channels. Let us take a look at our complete program before running it.

import random
from canlib import canlib
from canlib import kvadblib


# declare messages and one signal in each message
SIGNALS = { ’LIM_002’ : ’Load ’ ,
            ’ECM_004’ : ’Fuel ’ ,
            ’ECM_003’ : ’EngineTemp ’ ,
            ’ECM_001’ : ’EngineSpeed ’}


def open_channel(channel) :
    ”””Open a new channel , set bitrate 1Mbit/s and go bus on. ”””
    ch = canlib . openChannel(channel , canlib .canOPEN_ACCEPT_VIRTUAL)
    ch . setBusOutputControl( canlib .canDRIVER_NORMAL)
    ch . setBusParams( canlib .canBITRATE_1M)
    ch .busOn()
    return ch


def close_channel(ch) :
    ”””Go bus off and close channel . ”””
    ch . busOff ()
    ch . close ()


def get_random_bound_signal(db) :
    ”””Create a bmsg, randomly choosing a message/signal pair from SIGNALS”””
    # pick a message name at random
    msg_name = random. choice ( l i s t (SIGNALS) )

    # get the message from our database
    msg = db.get_message_by_name(msg_name)

    # get the signal contained in the message and
    # create a BoundSignal , containing an empty frame
    # with correct ID and DLC
    sig_name = SIGNALS[msg_name]
    bsig = msg.get_signal_by_name(sig_name) . bind ()
    # get the signal , which holds the definitions
    sig = bsig . signal

    # get the signal ’ s limits
    limits = sig . limits

    # create a value (with many decimals) within the limits
    value = random. uniform( limits .min, limits .max)

    # round value depending on type . . .
    if sig . type i s kvadblib . SignalType .UNSIGNED:
        # . . . remove decimals i f the signal was of type unsigned
        value = int (value)
    else:
        # . . . otherwise , round to get only one decimal
        value = round(value , 1)

    # Now we would like to set the physical value
    bsig . phys = value

    return bsig


def bsignal_str ( bsignal ) :
    ”””Create a nice looking string of the contents of the bound signal ”””
    value = bsignal . phys
    if bsignal . signal . type i s kvadblib . SignalType .UNSIGNED:
        # remove decimals i f the signal was of type unsigned
        value = int (value)
    else:
        # otherwise , round to get only one decimal
        value = round(value , 1)

    # create adjusted strings for prettier printing
    name = str ( bsignal .name) . rjust (11)
    val = str (value) . rjust (5)
    return ’%s : %s %s ’ % (name, val , bsignal . unit )


def bmessage_str(bmsg) :
    ”””Create a nice looking string of the contents of the bound message”””
    txt = ’ ’
    for bsig in bmsg:
    txt += ’%s ’ % bsignal_str ( bsig )
    return txt


def send_receive_using_bound_signals(db, quantity , ch_tx, ch_rx) :
    for i in range(quantity) :
        bsig0 = get_random_bound_signal(db)
        print ( ’Sending %s ’ % bsignal_str ( bsig0 ) )

        # send frame
        ch_tx. write ( bsig0 . frame)

        # read frame
        frame = ch_rx. read(timeout=100)
        bmsg1 = db. interpret (frame=frame)
        print ( ’ Receiving %s ’ % bmessage_str(bmsg1) )


def main() :
    # open the database we would like to use
    db = kvadblib .Dbc( filename=’db_histogram . dbc ’ )

    # Open and setup channels
    ch0 = open_channel(0)
    ch1 = open_channel(1)

    send_receive_using_bound_signals(db=db, quantity=10,
ch_tx=ch0 , ch_rx=ch1)

    close_channel(ch0)
    close_channel(ch1)


if __name__ == ’__main__’ :
    main()

Listing 3: Program used to create, send and receive random signals based on DBC database.

After arming our logger device by applying CAN power and disconnecting USB, we can now finally run the code in Listing 3 on a second device (I used a Kvaser USBcan Pro) and note that the sent and received signals are identical!

Sending   EngineSpeed: 5929 rpm
Receiving EngineSpeed: 5929 rpm
Sending   EngineSpeed: 234 rpm
Receiving EngineSpeed: 234 rpm
Sending     Fuel: 207.7 l/100 km
Receiving   Fuel: 207.7 l/100 km
Sending     Load: 67.1 metric ton
Receiving   Load: 67.1 metric ton
Sending     Load: 7.6 metric ton
Receiving   Load: 7.6 metric ton
Sending     Fuel: 46.0 l/100 km
Receiving   Fuel: 46.0 l/100 km
Sending   EngineTemp: 180.0 Celsius
Receiving EngineTemp: 180.0 Celsius
Sending     Fuel: 218.1 l/100 km
Receiving   Fuel: 218.1 l/100 km
Sending     Load: 4.4 metric ton
Receiving   Load: 4.4 metric ton
Sending     Fuel: 12.3 l/100 km
Receiving   Fuel: 12.3 l/100 km


Examining the logged data

Now that we have logged data on our Kvaser Memorator Pro, let us examine what is in there. We have gone through this in earlier blog articles4 so let us here just recap the essential part were we do the printing (full code listing will follow). When we write the raw frame contents, we use canlib’s built in string conversion

def read_raw_events(dev , fileIndx ) :
    # read events from l o g f i l e number indicated by fileIndx
    myEvents = dev .memoReadEvents( fileIndx )
    for event in myEvents:
        # print event with built in string conversion
        print (event)
    print (”\n”)

Using the read_raw_events function results in the following output:

Found 1 file on card:
*t:          - EAN:73-30130-00778-9 s/n:1023 FW:v3.8.384 LIO:v0.0
t:    0.242162 DateTime: 2018-01-18 07:46:58
t:    0.242162 Log Trigger Event(type: 0x1, trigno: 0x00, pre-trigger: 0, post-trigger: -1)

t:    9.7245066 DateTime: 2018-01-18 07:47:07
t:   20.224502362 DateTime: 2018-01-18 07:47:18
t:   30.724500212 DateTime: 2018-01-18 07:47:28
t:   32.003407612 ch:1 f: 2 id: 1f5 dlc: 8 d:29 17 00 00 00 00 00 00
t:   32.003407625 ch:0 f: 2 id: 1f5 dlc: 8 d:29 17 00 00 00 00 00 00
t:   32.006923737 ch:1 f: 2 id: 1f5 dlc: 8 d:ea 00 00 00 00 00 00 00
t:     32.00692375 ch:0 f: 2 id: 1f5 dlc: 8 d:ea 00 00 00 00 00 00 00
t:   32.007795862 ch:1 f: 2 id: 1f8 dlc: 8 d:33 b3 4f 43 00 00 00 00
t:   32.007795875 ch:0 f: 2 id: 1f8 dlc: 8 d:33 b3 4f 43 00 00 00 00
t:   32.008372987 ch:1 f: 2 id: 192 dlc: 8 d:33 33 86 42 00 00 00 00
t:    32.008373 ch:0 f: 2 id: 192 dlc: 8 d:33 33 86 42 00 00 00 00
t:   32.009127112 ch:1 f: 2 id: 192 dlc: 8 d:33 33 f3 40 00 00 00 00
t:   32.009127125 ch:0 f: 2 id: 192 dlc: 8 d:33 33 f3 40 00 00 00 00
t:   32.009925237 ch:1 f: 2 id: 1f8 dlc: 8 d:00 00 38 42 00 00 00 00
t:    32.00992525 ch:0 f: 2 id: 1f8 dlc: 8 d:00 00 38 42 00 00 00 00
t:   32.010670362 ch:1 f: 2 id: 1f7 dlc: 8 d:00 00 34 43 00 00 00 00
t:   32.010670375 ch:0 f: 2 id: 1f7 dlc: 8 d:00 00 34 43 00 00 00 00
t:   32.011413487 ch:1 f: 2 id: 1f8 dlc: 8 d:9a 19 5a 43 00 00 00 00
t:    32.0114135 ch:0 f: 2 id: 1f8 dlc: 8 d:9a 19 5a 43 00 00 00 00
t:   32.012167612 ch:1 f: 2 id: 192 dlc: 8 d:cd cc 8c 40 00 00 00 00
t:   32.012167625 ch:0 f: 2 id: 192 dlc: 8 d:cd cc 8c 40 00 00 00 00
t:   32.015623737 ch:1 f: 2 id: 1f8 dlc: 8 d:cd cc 44 41 00 00 00 00
t:    32.01562375 ch:0 f: 2 id: 1f8 dlc: 8 d:cd cc 44 41 00 00 00 00
t:   41.22449675 DateTime: 2018-01-18 07:47:39
t:   51.724494037 DateTime: 2018-01-18 07:47:49
t:   62.224491675 DateTime: 2018-01-18 07:48:00
t:    72.7244916 DateTime: 2018-01-18 07:48:10
t:   83.224489825 DateTime: 2018-01-18 07:48:21
t:   93.724481225 DateTime: 2018-01-18 07:48:31

Now we would like to use the database to extract our signals from those message events and replace the printed output with the name and value of our signals. This is done by converting the read message event into a frame and then let the database interpret the frame which results in a bound message that we can iterate over to get the actual signals. The last step is to adjust the printing to fit the built in string formatting.

def read_signal_events(dev , fileIndx ) :
    # open the database we would like to use
    db = kvadblib .Dbc( filename=’db_histogram . dbc ’ )

    myEvents = dev .memoReadEvents( fileIndx )
    for event in myEvents:
        # only message events have a data frame
        if type(event) i s kvmlib . events . MessageEvent :
            # convert the message event into a frame
            frame = event . asframe ()
            # let the database interpret the frame , a bound message will be
            # returned
            bmsg1 = db. interpret (frame=frame)
            # generate a nice looking string from the bound message , and
            # print on a format matching the built in string conversion
            # used for other types of events
            timestamp = event . asframe () . timestamp / 1000000000
            print ( ’ t:%14s Signal %s ’ % (timestamp , bmessage_str(bmsg1) ) )
        else :
            print (event)
    print (”\n”)

Using the read_signal_events function results in the following output:

Found 1 file on card:
*t:          - EAN:73-30130-00778-9 s/n:1023 FW:v3.8.384 LIO:v0.0
t:    0.242162 DateTime: 2018-01-18 07:46:58
t:    0.242162 Log Trigger Event (type: 0x1, trigno: 0x00, pre-trigger: 0, post-trigger: -1)

t:   9.7245066 DateTime: 2018-01-18 07:47:07
t: 20.224502362 DateTime: 2018-01-18 07:47:18
t: 30.724500212 DateTime: 2018-01-18 07:47:28
t: 32.003407612 Signal EngineSpeed: 5929 rpm
t: 32.003407625 Signal EngineSpeed: 5929 rpm
t: 32.006923737 Signal EngineSpeed: 234 rpm
t:  32.00692375 Signal EngineSpeed: 234 rpm
t: 32.007795862 Signal Fuel: 207.7 l/100 km
t: 32.007795875 Signal Fuel: 207.7 l/100 km
t: 32.008372987 Signal Load: 67.1 metric ton
t:   32.008373 Signal Load: 67.1 metric ton
t: 32.009127112 Signal Load: 7.6 metric ton
t: 32.009127125 Signal Load: 7.6 metric ton
t: 32.009925237 Signal Fuel: 46.0 l/100 km
t:  32.00992525 Signal Fuel: 46.0 l/100 km
t: 32.010670362 Signal EngineTemp: 180.0 Celsius
t: 32.010670375 Signal EngineTemp: 180.0 Celsius
t: 32.011413487 Signal Fuel: 218.1 l/100 km
t:   32.0114135 Signal Fuel: 218.1 l/100 km
t: 32.012167612 Signal Load: 4.4 metric ton
t: 32.012167625 Signal Load: 4.4 metric ton
t: 32.015623737 Signal Fuel: 12.3 l/100 km
t:  32.01562375 Signal Fuel: 12.3 l/100 km
t: 41.22449675 DateTime: 2018-01-18 07:47:39
t: 51.724494037 DateTime: 2018-01-18 07:47:49
t: 62.224491675 DateTime: 2018-01-18 07:48:00
t:  72.7244916 DateTime: 2018-01-18 07:48:10
t: 83.224489825 DateTime: 2018-01-18 07:48:21
t: 93.724481225 DateTime: 2018-01-18 07:48:31

Finally, here is the complete listing of our program.

from canlib import kvDevice
from canlib import kvadblib
from canlib import kvmlib


def read_raw_events(dev , fileIndx ) :
    # read events from l o g f i l e number indicated by fileIndx
    myEvents = dev .memoReadEvents( fileIndx )
    for event in myEvents:
        # print event with built in string conversion
        print (event)
    print (”\n”)


def bsignal_str ( bsignal ) :
    ”””Create a nice looking string of the contents of the bound signal ”””
    value = bsignal . phys
    if bsignal . signal . type i s kvadblib . SignalType .UNSIGNED:
        # remove decimals i f the signal was of type unsigned
        value = int (value)
    else :
        # otherwise , round to get only one decimal
        value = round(value , 1)

# create adjusted strings for prettier printing
    name = str ( bsignal .name) . rjust (11)
    val = str (value) . rjust (5)
    return ’%s : %s %s ’ % (name, val , bsignal . unit )


def bmessage_str(bmsg) :
    ”””Create a nice looking string of the contents of the bound message”””
    txt = ’ ’
    for bsig in bmsg:
        txt += ’%s ’ % bsignal_str ( bsig )
    return txt


def read_signal_events(dev , fileIndx ) :
    # open the database we would like to use
    db = kvadblib .Dbc( filename=’db_histogram . dbc ’ )

    myEvents = dev .memoReadEvents( fileIndx )
    for event in myEvents:
    # only message events have a data frame
    if type(event) i s kvmlib . events . MessageEvent :
        # convert the message event into a frame
        frame = event . asframe ()
        # let the database interpret the frame , a bound message will be
        # returned
        bmsg1 = db. interpret (frame=frame)
        # generate a nice looking string from the bound message , and
        # print on a format matching the built in string conversion
        # used for other types of events
        timestamp = event . asframe () . timestamp / 1000000000
        print ( ’ t:%14s Signal %s ’ % (timestamp , bmessage_str(bmsg1) ) )
    else:
        print (event)
    print (”\n”)


def read_log(dev , read_function) :
    # Connect to our Kvaser Memorator Pro 5xHS with EAN 00778-9
    dev = kvDevice . kvDevice(ean=”73-30130-00778-9”)
    dev . open()
    dev .memoOpen()
    fileCount = dev .memo. logFileGetCount ()
    print (”Found %d f i l e%s on card : ” % ( fileCount , ”s” i f fileCount > 1 else
        ””) )

    # Loop through a l l l o g f i l e s and write their contents to stdout
    for fileIndx in range( fileCount ) :
        read_function(dev , fileIndx )

    # Delete a l l l o g f i l e s
    #dev .memo. logFileDeleteAll ()

    # Close device
    dev .memoClose()
    dev . close ()


def main() :
    # connect to our Kvaser Memorator Pro 5xHS with EAN 00778-9
    dev = kvDevice . kvDevice(ean=”73-30130-00778-9”)

    # read and print raw events from the device
    read_log(dev , read_raw_events)
    # read and print signals from the device
    read_log(dev , read_signal_events)


if __name__ == ’__main__’ :
    main()


Convert logged data to a signal based format

If we already have extracted our logged data from our Kvaser Memorator into e.g. a .kme50 file, we can still expose our signals by feeding the database to the converter library and convert our data to a signal based format. Here we will convert our .kme50 file into the csv signal format.5

# set output format
fmt = kvlclib . WriterFormat( kvlclib .FILE_FORMAT_CSV_SIGNAL)
# the name of the formatter i s fetched using kvlcGetWriterName() internally
print (”Output format i s ’%s ’” % fmt .name)

# set resulting output filename taking advantage of the extension defined in
# the format . (Uses kvlcGetWriterExtension () under the hood . )
outfile = ”myresult . ” + fmt . extension
print (”Output filename i s ’%s ’” % outfile )

# create converter
kc = kvlclib . Converter( outfile , fmt)

# add database f i l e
channel_mask = kvlclib .ChannelMask.ONE | kvlclib .ChannelMask.TWO
kc . addDatabaseFile(”db_histogram . dbc” , channel_mask)

# Set input filename and format
inputfile = ”histogram .kme50”
print (”Input filename i s ’%s ’” % inputfile )
kc . setInputFile ( inputfile , file_format=kvlclib .FILE_FORMAT_KME50)

The full listing of our programs is shown in Listing 4.

from canlib import kvlclib


def trySetProperty( converter , property , value=None) :
    # Check i f the format supports the given property
    if converter . format . isPropertySupported(property) :

        # If a value was specified , set the property to this value
        if value i s not None:
            converter . setProperty (property , value)

        # get the property ’ s default value
        default = converter . format . getPropertyDefault(property)
        print (” PROPERTY_%s i s supported ( Default : %s )” %
                (property [ ’name’ ] , default ) )

        # get the property ’ s current value
        value = converter . getProperty(property)
        print (” Current value : %s” % value)
    else :
        print (” PROPERTY %s i s not supported” %
                (property [ ’name’ ] ) )


def convertEvents(kc) :
    # Get estimated number of remaining events in the input f i l e . This can be
    # useful for displaying progress during conversion .
    total = kc . eventCount()
    print (”Converting about %d events . . . ” % total )
    while True:
        try :
            # Convert events from input f i l e one by one until EOF i s reached
            kc . convertEvent ()
            if kc . isOutputFilenameNew() :
                print (”New output filename : %s” % kc . getOutputFilename() )
                print (”About %d events l e f t to convert . . . ” % kc . eventCount() )
        except kvlclib . KvlcEndOfFile :
            if kc . isOverrunActive () :
                157 print (”NOTE! The extracted data contained overrun . ”)
                kc . resetOverrunActive ()
            if kc . isDataTruncated () :
                print (”NOTE! The extracted data was truncated . ”)
                kc . resetStatusTruncated ()
            break

# set output format
fmt = kvlclib . WriterFormat( kvlclib .FILE_FORMAT_CSV_SIGNAL)
# the name of the formatter i s fetched using kvlcGetWriterName() internally
print (”Output format i s ’%s ’” % fmt .name)

# set resulting output filename taking advantage of the extension defined in
# the format . (Uses kvlcGetWriterExtension () under the hood . )
outfile = ”myresult . ” + fmt . extension
print (”Output filename i s ’%s ’” % outfile )

# create converter
kc = kvlclib . Converter( outfile , fmt)

# add database f i l e
channel_mask = kvlclib .ChannelMask.ONE | kvlclib .ChannelMask.TWO
kc . addDatabaseFile(”db_histogram . dbc” , channel_mask)

# Set input filename and format
inputfile = ”histogram .kme50”
print (”Input filename i s ’%s ’” % inputfile )
kc . setInputFile ( inputfile , file_format=kvlclib .FILE_FORMAT_KME50)

# allow output f i l e to overwrite existing f i l e s
trySetProperty(kc , kvlclib .PROPERTY_OVERWRITE, 1)

# add nice header to the output f i l e
trySetProperty(kc , kvlclib .PROPERTY_WRITE_HEADER, 1)

# we are converting CAN t r a f f i c with max 8 bytes , so we can minimize the
# width of the data output to 8 bytes
trySetProperty(kc , kvlclib .PROPERTY_LIMIT_DATA_BYTES, 8)

# convert a l l events
convertEvents(kc)

Listing 4: A program to convert from .kme50 to csv signal.

Running the program from Listing 4 reveals that the property “LIMIT_DATA_BYTES” was obviously not available in the “CSV Signal” format we choose, but we were saved by our trySetProperty function.

Output format is ’CSV Signal’
Output filename is ’myresult.csv’
Input filename is ’histogram.kme50’
 PROPERTY_OVERWRITE is supported (Default: 0)
    Current value: 1
 PROPERTY_WRITE_HEADER is supported (Default: 0)
    Current value: 1
 PROPERTY LIMIT_DATA_BYTES is not supported
Converting about 42 events...
New output filename: myresult.csv
About 41 events left to convert...

Looking in the resulting file myresult.csv shown in Figure 2 we can see the signals as we expected.

Figure 2: Our resulting csv file reveals the now familiar signal values.

This ends the tour on how to use a DBC database to show signals and values from events and logged data.


Footnotes

1 We created a database in the blog entry Handling CAN databases in Python, and sent and received signals in Send and receive database signals.

2 We created the DBC file db_histogram.dbc from scratch in the blog article Handling CAN databases in Python. The database can also be found in the CANlib SDK under the folder Samples\tScript\Learn more\Misc\histogram.

3 A signal can be accessed through either its physical or raw value. The raw value is a representation of the actual bits that will be sent in the CAN frame. The physical representation takes the raw bits and applies the scale and offset, as specified in the signals definition.

4 We read logged messages when we started using kvmlib in https://www.kvaser.com/ developer-blog/getting-started-with-kvmlib/.

5 This code was introduced in a blog series about the Converter Library (kvlclib) https: //www.kvaser.com/developer-blog/converting-to-plain-ascii/.

Author Image

Magnus Carlsson

Magnus Carlsson is a Software Developer for Kvaser AB and has developed firmware and software for Kv...