Developer Blog

17/09/2018 by Magnus Carlsson

Adding script and triggers to a configuration

This is the second post in a 4-part series about configuring and reading logged data using a Kvaser Memorator 2nd Generation device through kvmlib:

1 Getting started with kvmlib

2 Adding script and triggers to a configuration

3 Digging deeper into kvmlib

4 Configure an SD card using kvmlib

In the first part we showed how to use kvmlib to configure a device using a simple configuration that logged everything. Let us now increase the complexity of our configuration by adding a t program and some triggers. Full program listings are available on GitHub.

2.1 Prepare a t program to be included in the configuration

Let’s take a t program that generates some CAN traffic and add that to the configuration for testing purposes. This script is actually part of a test where we verify that start and stop of logging can be controlled using triggers. The script itself sends a CAN message every second, starting at 0.5 s from the time the script was started. The CAN message id is incremented by one for each sent CAN message. In total, 11 CAN messages are sent on the CAN bus.

// This script is intended to be used to check a configuration set as:
//
// [ ] Log everything
// [ ] FIFO mode
// Power settings: 5 Time to live after CAN power disconnected, in seconds
//
// CAN 1 should be connected to CAN 2
//
// Triggers:
// CAN1 Trigger1 Timer [4s] : Start logging, post-trigger=0
// CAN1 Trigger2 Timer [7s] : Stop logging, post-trigger=3000
//
// Script sends msg id:
// Id 1 at 0.5s
// Id 2 at 1.5s
// Id 3 at 2.5s
// Id 4 at 3.5s
// Id 5 at 4.5s
// ... and so on until
// Id 11 at 10.5s

variables {
  // Base channel. CAN messages will be sent on channel ch and received on
  // channel ch + 1
  const int ch = 0;

  // The singleshot timer is used to get a delay before the first CAN message
  // is sent.
  Timer singleshot;

  // The periodic timer is then used between each CAN message
  Timer periodic;

  // The message id of the sent CAN messages, will be incremented
  int msgId = 1;

  // The CAN message to be sent
  CanMessage msg;
}

on Timer singleshot {
  // Start the periodic timer to send 10 more CAN messages
  timerStart(periodic, 10);

  // After using the current CAN message id, increment before next use
  msg.id = msgId++;
  msg.flags = 0;
  msg.dlc = 8;
  msg.data = "\x12\x21\x13\x31\x22\x34\x41\x15";

  // Send CAN message
  canWrite(ch, msg);
  printf("Single shot MsgId: %d\n", msg.id);
}

on Timer periodic {
  // After using the current CAN message id, increment before next use
  msg.id = msgId++;

  // Send CAN message
  canWrite(ch, msg);
  printf("Periodic MsgId: %d\n", msg.id);
  if (!timerIsPending(periodic)) {
    printf("Timer done!");
  }
}

on start {
  printf("Starting testlogger companion script\n");

  // Setup the two CAN channels and go bus on.
  // This will override the settings in the binary configuration,
  // most notably the channels will no longer be in silent mode.
  canBusOff(ch);
  canBusOff(ch + 1);
  canSetBitrate(ch, canBITRATE_1M);
  canSetBitrate(ch + 1, canBITRATE_1M);
  canSetBusOutputControl(ch, canDRIVER_NORMAL);
  canSetBusOutputControl(ch + 1, canDRIVER_NORMAL);
  canBusOn(ch);
  canBusOn(ch + 1);

  singleshot.timeout = 500; // Wait half a second
  periodic.timeout = 1000; // One second period

  // Start the singleshot timer to send the first CAN message
  timerStart(singleshot);
  printf("Start periodic transmission\n");
}

on stop {
  printf("Stopping script\n");
  canBusOff(ch);
  canBusOff(ch + 1);
}

Listing 5: A sample t program that generates CAN messages.

The t program needs to be compiled before being added to the configuration. The t compiler is called `scc.exe’ and is normally placed at `C:\Program Files (x86)\Kvaser\Canlib\Bin\scc.exe’ when CANlib SDK is installed. The usage help will be printed if invoked without any arguments.

scc.exe
No input file found.
Script compiler for Kvaser t-script.
This is version 3.3.305 built on Sun Dec 13 19:38:53 2015.
Usage:
scc [-v -verbose] [-c] [-g] [-dx] [-devlines]
[-addsrc] [-comment=XXX]
[-pkey=N] [-skey=K]
@<filename>] [-dbase=<filename>] <filename>
where
-c Compile only. No output is produced.
-dbase=XXX.dbc Use the database XXX.dbc to define CAN messages
in the script.
-devlines Supress line numbers in generated code. Generates faster and
smaller code, but line numbers cannot be reported in exceptions.
-g Generate symbol tables for debug.
-addsrc Include the source code.
-comment=XXX Include the comment XXX.
Note that spaces can be used in arguments "like this".
Encryption of script and source code:
Script and source are protected by a symmetric crypto (-skey). The
symmetric key is then protected by a public key crypto (-pkey).
-pkey=N Use Kvaser public key number N.
Default:
0 - (no encryption) if no skey is entered
2 - (1024 bit RSA) if a skey is entered
-skey=K Use the symmetric key K.
If no K is entered and a pkey is, a random key K (hex)
will be generated for you
Key K formats:
-skey=0x11223344FFACAC - Hexadecimal format.
-skey=mysecretkey - ASCII format.
Contains multiple-precision arithmetic code originally written by
David Ireland, copyright (c) 2001-8 by D.I. Management Services Pty Limited
, and is used with permission.



I would recommend that you always compile with the -comment argument (holding your programs version number beside the descriptive text), and optionally -addsrc which attaches the source t program to the resulting .txe file. A .txe file may be opened using Kvaser TRX1, our lightweight IDE for developing t programs for Kvaser devices, and if the compilation was done using the -addsrc argument, the source code will be shown under the Source tab in the Kvaser TRX built-in .txe Inspector. If you would like to hide your script, you can use encryption to do so.

scc.exe myCanGenerator.t -addsrc -comment="My CAN generator program v1.0"

2.2 Create a configuration

Now that we have the compiled t program, `myCanGenerator.txe’, let us create a configuration that includes the compiled t program and also sets up two triggers. The configuration is written in XML format as specified in the document Specification of Kvaser Memorator Device configuration XML format. We create a configuration that sets the bitrate to 1 Mbit/s on channel 0 and 1, adds a trigger statement for CAN message id 3 on CAN channel 0 or 1, and a second trigger statement for CAN message id 6 on CAN channel 1. We specify that the first statement should start logging directly, and the second statement should stop logging after a delay of 2.5 seconds (by using a posttrigger).

With this setup, connecting CAN 1 to CAN 2 and having in mind that our script sends one CAN message per second, the expected result is to catch CAN message 3 through 8. The logging starts at CAN message id 3, and stops at CAN message id 6, but since we have a posttrigger of 2.5 seconds, CAN message id 7 and 8 are also expected to end up in the log file.

One tag of special note in the XML configuration is the tag BINARY_VERSION, which is used by the conversion library when creating the binary configuration for downloading to the device. Our device (running firmware version 3.0) should use Binary Configuration Format v6.0.

For more information about the XML format, please read the Specification of Kvaser Memorator Device configuration XML format.

<?xml version="1.0" ?>
<!DOCTYPE KVASER>
<KVASER>
  <VERSION>2.0</VERSION>
  <BINARY_VERSION>6.0</BINARY_VERSION>
  <SETTINGS>
    <MODE fifo_mode="NO" log_all="NO"/>
    <CANPOWER timeout="5000"/>
  </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>
    <TRIGGERS>
      <TRIGGER_MSG_ID can_ext="NO" can_fd="NO" channel="0" msgid="3" msgid_min="3"
       name="trigger_0" protocol="NONE" timeout="0"/>
      <TRIGGER_MSG_ID can_ext="NO" can_fd="NO" channel="1" msgid="6" msgid_min="6"
       name="trigger_1" protocol="NONE" timeout="0"/>
      <TRIGGER_MSG_ID can_ext="NO" can_fd="NO" channel="1" msgid="3" msgid_min="3"
       name="trigger_2" protocol="NONE" timeout="0"/>
    </TRIGGERS>
    <STATEMENTS>
      <STATEMENT posttrigger="0" pretrigger="0">
        <EXPRESSION>trigger_0 OR trigger_2</EXPRESSION>
        <ACTIONS>
          <ACTION_START_LOG/>
        </ACTIONS>
      </STATEMENT>
      <STATEMENT posttrigger="2500" pretrigger="0">
        <EXPRESSION>trigger_1</EXPRESSION>
        <ACTIONS>
          <ACTION_STOP_LOG/>
        </ACTIONS>
      </STATEMENT>
    </STATEMENTS>
  </TRIGGERBLOCK>
  <SCRIPTS>
    <SCRIPT default_channel="0" primary="YES">
      <FILENAME>myCanGenerator.txe</FILENAME>
      <PATH></PATH>
    </SCRIPT>
  </SCRIPTS>
</KVASER>

Listing 6: Sample XML configuration.

2.3 Validating the configuration

Even though the conversion from XML configuration to binary configuration will fail if any errors are found, I’d still recommended to do an explicit validation of the XML configuration using kvaMemoLibXml. This validation will give you warnings when you e.g. create a configuration that does not contain any expression to start logging. After the call to validation, we can read out the number of errors and warnings detected, together with a code and text summary of each.

import canlib.kvaMemoLibXml as kvaMemoLibXml

xl = kvaMemoLibXml.kvaMemoLibXml()
print("kvaMemoLibXml version: v" + xl.getVersion())

# Read in the XML configuration file
with open("config.xml", 'r') as myfile:
    config_xml = myfile.read()

# Validate the XML configuration
xl.kvaXmlValidate(config_xml)

# Get number of validation messages
(countErr, countWarn) = xl.xmlGetValidationStatusCount()
print("Errors: %d, Warnings: %d" % (countErr, countWarn))

# If we have any validation errors, print those
if countErr != 0:
    code = -1
    while code != 0:
        (code, text) = xl.xmlGetValidationError()
        print("%d: %s" % (code, text))

# If we have any validation warnings, print those
if countWarn != 0:
    code = -1
    while code != 0:
        (code, text) = xl.xmlGetValidationWarning()
        print("%d: %s" % (code, text))

# Exit if we had any validation errors or warnings
if countErr != 0 or countWarn != 0:
    raise Exception('Please fix validation Errors/Warnings.')

Listing 7: Validating the XML configuration.

This post added some complexity to the configuration, in the next part we will go back into kvmlib and see how calling kvmlib works in depth.

Footnotes

1 The Kvaser TRX tool is a light weight IDE for developing t programs for Kvaser
devices.



This article has been updated. To view the original, click on the box below.

Original Article

Original article published April 5, 2016

This blog uses now deprecated functions, see blog “Improved API in Python canlib v1.5” for more information.

1 Getting started with kvmlib

2 Adding script and triggers to a configuration

3 Digging deeper into kvmlib

4 Configure an SD card using kvmlib

In the first part we showed how to use kvmlib to configure a device using a simple configuration that logged everything. Let us now increase the complexity of our configuration by adding a t program and some triggers.


2.1 Prepare a t program to be included in the configuration

Let’s take a t program that generates some CAN traffic and add that to the configuration for testing purposes. This script is actually part of a test where we verify that start and stop of logging can be controlled using triggers. The script itself sends a CAN message every second, starting at 0.5 s from the time the script was started. The CAN message id is incremented by one for each sent CAN message. In total, 11 CAN messages are sent on the CAN bus.

// This script is intended to be used to check a configuration set as:
//
// [ ] Log everything
// [ ] FIFO mode
// Power settings: 5 Time to live after CAN power disconnected, in seconds
//
// CAN 1 should be connected to CAN 2
//
// Triggers:
// CAN1 Trigger1 Timer [4s] : Start logging, post-trigger=0
// CAN1 Trigger2 Timer [7s] : Stop logging, post-trigger=3000
//
// Script sends msg id:
// Id 1 at 0.5s
// Id 2 at 1.5s
// Id 3 at 2.5s
// Id 4 at 3.5s
// Id 5 at 4.5s
// ... and so on until
// Id 11 at 10.5s

variables {
  // Base channel. CAN messages will be sent on channel ch and received on
  // channel ch + 1
  const int ch = 0;

  // The singleshot timer is used to get a delay before the first CAN message
  // is sent.
  Timer singleshot;

  // The periodic timer is then used between each CAN message
  Timer periodic;

  // The message id of the sent CAN messages, will be incremented
  int msgId = 1;

  // The CAN message to be sent
  CanMessage msg;
}

on Timer singleshot {
  // Start the periodic timer to send 10 more CAN messages
  timerStart(periodic, 10);

  // After using the current CAN message id, increment before next use
  msg.id = msgId++;
  msg.flags = 0;
  msg.dlc = 8;
  msg.data = "\x12\x21\x13\x31\x22\x34\x41\x15";

  // Send CAN message
  canWrite(ch, msg);
  printf("Single shot MsgId: %d\n", msg.id);
}

on Timer periodic {
  // After using the current CAN message id, increment before next use
  msg.id = msgId++;

  // Send CAN message
  canWrite(ch, msg);
  printf("Periodic MsgId: %d\n", msg.id);
  if (!timerIsPending(periodic)) {
    printf("Timer done!");
  }
}

on start {
  printf("Starting testlogger companion script\n");

  // Setup the two CAN channels and go bus on.
  // This will override the settings in the binary configuration,
  // most notably the channels will no longer be in silent mode.
  canBusOff(ch);
  canBusOff(ch + 1);
  canSetBitrate(ch, canBITRATE_1M);
  canSetBitrate(ch + 1, canBITRATE_1M);
  canSetBusOutputControl(ch, canDRIVER_NORMAL);
  canSetBusOutputControl(ch + 1, canDRIVER_NORMAL);
  canBusOn(ch);
  canBusOn(ch + 1);

  singleshot.timeout = 500; // Wait half a second
  periodic.timeout = 1000; // One second period

  // Start the singleshot timer to send the first CAN message
  timerStart(singleshot);
  printf("Start periodic transmission\n");
}

on stop {
  printf("Stopping script\n");
  canBusOff(ch);
  canBusOff(ch + 1);
}

Listing 7: A sample t program that generates CAN messages.

The t program needs to be compiled before being added to the configuration. The t compiler is called scc.exe 5 and is normally placed at C:\Program Files (x86)\Kvaser\Canlib\Bin\scc.exe when the Kvaser CANlib SDK is installed. The usage help will be printed if invoked without any arguments.

scc.exe
No input file found.

Script compiler for Kvaser t-script.
This is version 3.8.505 (BETA) built on Tue Jun 05 10:19:55 2018.

Usage:

scc [-v -verbose] [-c] [-g] [-dx] [-devlines]
    [-addsrc] [-comment=XXX]
]
    [-pkey=N] [-skey=K]
@] [-dbase=] 

 where
  -c              Compile only. No output is produced.
  -dbase=XXX.dbc  Use the database XXX.dbc to define CAN messages
                  in the script.
  -devlines       Supress line numbers in generated code. Generates faster and
                  smaller code, but line numbers cannot be reported in exceptions.
  -g              Generate symbol tables for debug.
  -addsrc         Include the source code.
  -comment=XXX    Include the comment XXX.
Search for <> include files in .

Note that spaces can be used in arguments "like this".

  Encryption of script and source code:

  Script and source are protected by a symmetric crypto (-skey). The
  symmetric key is then protected by a public key crypto (-pkey).

  -pkey=N         Use Kvaser public key number N.
                  Default:
                  0 - (no encryption) if no skey is entered
                  2 - (1024 bit RSA) if a skey is entered
  -skey=K         Use the symmetric key K.
                  If no K is entered and a pkey is, a random key K (hex)
                  will be generated for you
                  Key K formats:
                  -skey=0x11223344FFACAC  - Hexadecimal format.
                  -skey=mysecretkey       - ASCII format.

  Contains multiple-precision arithmetic code originally written by
  David Ireland, copyright (c) 2001-8 by D.I. Management Services Pty Limited
, and is used with permission.

I would recommend that you always compile with the -comment argument (holding your programs version number beside the descriptive text), and optionally -addsrcwhich attaches the source t program to the resulting .txe file. A .txe file may be opened using Kvaser TRX 1 and if the compilation was done using the -addsrcargument, the source code will be shown under the Source tab in the Kvaser TRX built-in .txe Inspector. If you would like to hide your script, you can use encryption to do so.

scc.exe myCanGenerator.t -addsrc -comment="My CAN generator program v1.0"
Source size: 2516 bytes
Compilation succeeded

2.2 Create a configuration

Now that we have the compiled t program, myCanGenerator.txe, let us create a configuration that includes this compiled t program and also sets up two triggers. The configuration is written in XML format as specified in the document Specification of Kvaser Memorator Device configuration XML format.7 We create a configuration that sets the bitrate to 1 Mbit/s on channel 0 and 1, adds a trigger statement for CAN message id 3 on CAN channel 0 or 1, and a second trigger statement for CAN message id 6 on CAN channel 1. We specify that the first statement should start logging directly, and the second statement should stop logging after a delay of 2.5 seconds (by using a posttrigger).

With this setup, connecting CAN 1 to CAN 2 and having in mind that our script sends one CAN message per second, the expected result is to catch CAN message 3 through 8. The logging starts at CAN message id 3, and stops at CAN message id 6, but since we have a posttrigger of 2.5 seconds, CAN message id 7 and 8 are also expected to end up in the log file.

One tag of special note in the XML configuration is the tag BINARY_VERSION, which is used by the conversion library when creating the binary configuration for downloading to the device. Our device (running firmware version 3.11) should use Binary Configuration Format v6.0.

For more information about the XML format, please read the Specification of Kvaser Memorator Device configuration XML format.

<?xml version="1.0" ?>
<!-- config.xml -->
<!DOCTYPE KVASER>
<KVASER>
  <VERSION>2.0</VERSION>
  <BINARY_VERSION>6.0</BINARY_VERSION>
  <SETTINGS>
    <MODE fifo_mode="NO" log_all="NO"/>
    <CANPOWER timeout="0"/>
  </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>
    <TRIGGERS>
      <TRIGGER_MSG_ID can_ext="NO" can_fd="NO" channel="0" msgid="3" msgid_min="3"
       name="trigger_0" protocol="NONE" timeout="0"/>
      <TRIGGER_MSG_ID can_ext="NO" can_fd="NO" channel="1" msgid="6" msgid_min="6"
       name="trigger_1" protocol="NONE" timeout="0"/>
      <TRIGGER_MSG_ID can_ext="NO" can_fd="NO" channel="1" msgid="3" msgid_min="3"
       name="trigger_2" protocol="NONE" timeout="0"/>
    </TRIGGERS>
    <STATEMENTS>
      <STATEMENT posttrigger="0" pretrigger="0">
        <EXPRESSION>trigger_0 OR trigger_2</EXPRESSION>
        <ACTIONS>
          <ACTION_START_LOG/>
        </ACTIONS>
      </STATEMENT>
      <STATEMENT posttrigger="2500" pretrigger="0">
        <EXPRESSION>trigger_1</EXPRESSION>
        <ACTIONS>
          <ACTION_STOP_LOG/>
        </ACTIONS>
      </STATEMENT>
    </STATEMENTS>
  </TRIGGERBLOCK>
  <SCRIPTS>
    <SCRIPT default_channel="0" primary="YES">
      <FILENAME>myCanGenerator.txe</FILENAME>
      <PATH></PATH>
    </SCRIPT>
  </SCRIPTS>
</KVASER>

Listing 9: Sample XML configuration.

2.3 Validating the configuration

Even though the conversion from XML configuration to binary configuration will fail if any errors are found, I’d still recommended to do an explicit validation of the XML configuration using kvaMemoLibXml. This validation will give you warnings when you e.g. create a configuration that does not contain any expression to start logging. After the call to validation, we can read out the number of errors and warnings detected, together with a code and text summary of each.

# 04_validate_xml.py
from canlib import kvamemolibxml as xl

print('kvaMemoLibXml version: v{}'.format(xl.dllversion()))

# Read in the XML configuration file
with open("config.xml", 'r') as myfile:
    config_xml = myfile.read()

# Validate the XML configuration
xl.kvaXmlValidate(config_xml)

# Get number of validation messages
(countErr, countWarn) = xl.xmlGetValidationStatusCount()
print('Errors: {}, Warnings: {}'.format(countErr, countWarn))

# If we have any validation errors, print those
if countErr != 0:
    code = -1
    while code != 0:
        (code, text) = xl.xmlGetValidationError()
        print('{}: {}'.format(code, text))

# If we have any validation warnings, print those
if countWarn != 0:
    code = -1
    while code != 0:
        (code, text) = xl.xmlGetValidationWarning()
        print('{}: {}'.format(code, text))

# Exit if we had any validation errors or warnings
if countErr != 0 or countWarn != 0:
    # raise Exception('Please fix validation Errors/Warnings.')
    print('Please fix validation Errors/Warnings.')

Listing 10: Validating the XML configuration.

This post added some complexity to the configuration, in the next part we will go back into kvmlib and see how calling kvmlib works at the C API level.

Footnotes

1 The Kvaser TRX tool is a light weight IDE for developing t programs for Kvaser
devices.

Getting started with kvmlib Part One

Digging deeper into kvmlib Part Three

Author Image

Magnus Carlsson

Magnus Carlsson is a Software Developer for Kvaser AB and has developed firmware and software for Kvaser products since 2007. He has also written a number of articles for Kvaser’s Developer Blog dealing with the popular Python language.