SN-IND-1-040 Diagnostics With CAPL Since 9.0 SP3
SN-IND-1-040 Diagnostics With CAPL Since 9.0 SP3
2021-02-04
Table of Contents
In the table below you will find the icon conventions used throughout the Support Note.
Symbol Utilization
This icon indicates notes and tips that facilitate your work.
2 Overview
This Support Note will show you how to access an ECU using the diagnostic features of CANoe and (as far as
possible) of CANalyzer and how to use the diagnostic functions provided by the CAPL programming language.
This Support Note will only cover diagnostics on CAN, but aside from the bus specific aspects, diagnostics on
FlexRay, LIN, K-Line and DoIP is quite similar.
This Support Note is intended for CANoe versions starting from 9.0 SP3 and higher.
There is a separate Support Note for older CANoe versions. To receive it, please contact the Vector
Support (contact information in chapter 18).
All screenshots in this note are taken from CANoe 14.
SN-IND-1-040_Diagnostics_with_CAPL_since_9.0_SP3 1
3 Configuring the diagnostic components of CANoe/CANalyzer
If you want to create diagnostic tests or if you want to access the diagnostic data of an ECU with
CANoe/CANalyzer, you have to add a diagnostic description first.
There are different types of descriptions available:
> Choose the targeted network name and click on the button “Add Diagnostic Description”.
Select one of the above-mentioned diagnostic description types in the appearing drop-down
menu and select the file (except for basic diagnostics where the Basic Diagnostic Editor will
open after closing this dialog box).
After the selection of the diagnostic description, the diagnostic configuration for the chosen
network is available:
The configuration`s branch is named after the ECU qualifier (here Door).
The most important settings are:
> Interface
The interface is a set of communication parameters to access the ECU.
In case the diagnostic description does not contain a valid or suitable set of these
parameters, you can use some default interfaces (prefixed with {generated}) for 11-bit and
29-bit addressing, for normal fixed addressing (for J1939), extended addressing, etc. See
screenshot below:
> Variant
The variant determines which services are available for diagnostics.
If the diagnostic description contains more than one variant, please choose the desired one
here.
The settings on this page depend on the bus type. For CAN it looks like this:
If you have selected one of the default interfaces (prefixed with {generated}), the override manually
setting is automatically checked as you are required to make some settings (like CAN ID).
> Addressing
Here you can set the CAN IDs used for functional or physical requests and responses
when “Override manually” is enabled.
It`s also possible to activate 29-bit identifier via the check box
The settings on this page do also depend on the bus type. For CAN it looks like this:
If you have selected one of the default interfaces (prefixed with {generated}) the override manually
setting is automatically checked.
Two templates for creating such a DLL with Visual Studio (including the project file) can be found in the
following directory:
If you want to use a service that is not defined in the added diagnostic description (referred to as
master description) you can use additional descriptions to extent the Master Description. This is
especially useful if the master description cannot or shall not be changed.
This property page is bus independent and therefore always looks like this:
It`s also possible to add more than one additional description (as depicted in the screenshot above).
You can drag the lines with the descriptions up or down to set the search order within the descriptions.
The top description is taken first and the description on the bottom is taken last when a service is
searched for.
Please find more information about qualifiers in the following chapters 4 and 5.
The qualifier is in contrast to the name language independent (always indicated in English). In CDD files this
qualifier is there often called a shortcut qualifier (especially for services). In PDX files the qualifier is equivalent
to the ODX short name. The ODX long name cannot be used here.
For programming with CAPL, these qualifiers and objects can easily be accessed in the CAPL browser using
the Symbols explorer which can be switched to Diagnostics
Service qualifier
_RQ = Request
Parameter qualifier
Select Symbols-Tab
Please use the symbols in the toolbar to change the display of the diagnostic elements in the tree view.
Depending on the selection either the Diagnostic Classes or Diagnostic Services will be displayed.
> ECU
> Services
> Parameters
Each of these objects has its own qualifiers which have to be used in the various diagnostics functions or in
the definition of diagnostics CAPL objects. There is an ECU qualifier that is used to address a specific ECU.
For each ECU, there are several diagnostic classes available, but these classes are only used for obtaining a
better structure, in CAPL they are generally not required.
Each class consists of related services (e.g. class Fault Memory contains Fault Memory Services), each
service has its own service qualifier. Please note that the read and write services for a specific data identifier
have different qualifiers, the basic name of the service appended with a _Read or _Write supplement. Both
parts together constitute the qualifier.
Again for a better structure, the qualifiers for a service are divided into 3 categories, consisting of the
Mnemonic for the underlying service (e. g. RDBI for ReadDataByIdentifier) and an appendix for Positive
Response (_PR), Negative Response (_NR) and Request (_RQ).
Underneath these structuring elements, you will find the parameter qualifiers for the respective service. For
accessing these parameters, these qualifiers are required.
To use these qualifiers, just drag and drop them from the Symbol Explorer into the CAPL browser’s editing
window.
As you can access multiple ECUs with CANoe\CANalyzer, you need to address the ECU that is targeted by
the diagnostic CAPL function calls.
This is done by using the ECU qualifier in on diag.. event handlers and DiagRequest/DiagResponse
objects.
The ECU qualifier is specified in the diagnostics configuration (see chapter 3).
on diagResponse Door.Serial_Number_Write
{
// Triggers when response from Door ECU is received
}
diagSendRequest(myRequest);
// Send Request
If you want to send a request which is not specified in the diagnostics description, you can create and send the
complete request (including SID and subfunction) on raw level.
After the declaration of the diagRequest-object it’s necessary to adjust the size of the service using the
the DiagResize function. Please find more information about working on raw level in chapter 15.
diagResize(req, elCount(request));
diagSetPrimitiveData(req, request, elCount(request));
diagSendRequest(req);
> numeric (physical values: direct values or with applying a conversion rule or formula)
> raw (raw values: as transmitted on the bus byte by byte)
> symbolic (for numeric values which are represented by text tables)
Depending on the type and size of a parameter, you have to use a specific SetParameter function:
> for data types with a size of more than 4 bytes (e.g. VIN) or if you want to set the raw value: use
DiagSetParameterRaw
> for data types, up to 4 bytes use DiagSetParameter. The distinction if a parameter is treated as a
symbolic or numeric one is done by observing the parameter type. A char[] parameter type is the indicator
for a symbolic value, a double type is used for numerical parameters.
It is also possible to use DiagSetParameterRaw for raw access.
> for avoiding the use of a non-available numerical value or misspelled symbolic parameter values, you
should check the return code of the DiagSetParameter functions to see if the parameter has been set
successfully before sending a request (which in the fault case could contain the default value or in worst
case garbage).
if(ret>=0)
{
ret = diagSendRequest(req);
if(ret>=0)
write("Request has been (partially) sent");
else
write("Could not sent request");
}
else
{
write("Could not set parameter");
}
You can set all the parameters of a request subsequently before finally using the
DiagSendRequest function to send the request.
If the service contains a parameter with an iterative data type (like a list of DTCs), in order to set
the parameter within the iteration it is necessary to use diagSetComplexParameter or
diagSetComplexParameterRaw.
If the service contains a parameter with a variable length, it might be necessary to adjust the size of
the service using the DiagResize function.
For CANoe test modules there exists a different approach. Please refer to chapter 12.
This event handler is only called when the request has been sent from CAPL.
Requests sent by Diagnostic Console will not trigger this event handler.
You can read the parameters of the received response using the DiagGetParameter and
DiagGetParameterRaw functions. Like their DiagSet counterparts from chapter 7, the use depends on the
size and type of the parameters.
You can check whether the received response is a positive or a negative response by using
DiagIsPositiveResponse or DiagIsNegativeResponse.
> numeric (physical values: direct values or with applying a conversion rule or formula)
> raw (raw values: as transmitted on the bus byte by byte)
> symbolic (for numeric values which are represented by text tables)
Depending on the type and size of a parameter, you have to use a specific GetParameter function:
> for data types with a size of more than 4 bytes (e.g. VIN) or if you want to read the raw value: use
DiagGetParameterRaw
> for data types up to 4 bytes: use DiagGetParameter. The distinction if a parameter is treated as a
symbolic or numeric one is done by observing the parameter type: a char [] parameter type is the
indicator for a symbolic value, a double type is used for numerical parameters.
It is also possible to use DiagGetParameterRaw for raw access.
There are different types of responses possible:
on diagResponse Door.Serial_Number_Read
{
long ret;
byte serialNumber[13];
if(ret>=0)
write("Serial number is (hex): %02X %02X %02X etc.",
serialNumber[0], serialNumber[1], serialNumber[2]);
else
write("Could not retrieve parameter");
}
else
{
write("Negative response code: 0x%02X",
diagGetResponseCode(this));
}
}
If the response contains a parameter with an iterative data type (like a list of DTCs), in order to
read the parameter`s value within the iteration it is necessary to use diagGetComplexParameter
or diagGetComplexParameterRaw.
Please see chapter 9 for more information.
To read all DTCs, you have to get the number of parameter (DTC) iterations by using the function
diagGetIterationCount (available since CANoe 9.0 SP3) first.
on key '7'
{
diagRequest Door.FaultMemory_ReadAllIdentified req;
diagSetParameter(req, "DtcStatusMask", 0x09); // Set the status mask
diagSendRequest(req);
}
on diagResponse Door.FaultMemory_ReadAllIdentified
{
long length;
byte StatusByte;
byte bit;
char text[200];
int i;
dword DTC;
if(0 != diagIsPositiveResponse(this))
{
length = diagGetIterationCount(this, "ListOfDTC"); // Get then number of iterations
if(length >= 0 )
{
write(" ");
write("Read All identified DTCs:");
write("-------------------------");
// Get the symbolic and numeric value of one status availability mask bit
bit = diagGetParameter(this, "DtcAvailabilityMask.TestFailed");
diagGetParameter(this, "DtcAvailabilityMask.TestFailed", text, elCount(text));
for(i=0;i<length;i++)
{
// Get the symbolic and numeric value of the DTC
DTC = diagGetComplexParameter(this,"ListOfDTC", i, "DTC");
diagGetComplexParameter(this, "ListOfDTC", i, "DTC", text, elCount(text));
// Get the DTC status byte as numerical value
StatusByte = diagGetComplexParameter(this,"ListOfDTC", i, "StatusOfDtc");
// Get the symbolic and numeric value of one status byte bit
bit =
diagGetComplexParameter(this,"ListOfDTC", i,"StatusOfDtc.TestFailed");
diagGetComplexParameter
(this, "ListOfDTC", i, "StatusOfDtc.TestFailed", text, elCount(text));
}
else
{
write("Error retrieving iteration length: %d", length);
}
Extended data records and snapshot data records can be different for each DTC, and there can be one or
more different record types for both data structures. The assignment of the record numbers or types is done
using multiplexor components which determine the data structure actually used.
Generally, the extended data record number or the snapshot number is the multiplexor which determines the
contents of the associated extended data record or the snapshot record. For reading such data, you have to
observe the record number and then read the corresponding parameters for each record type. If you want to
write such data structures (for ECU simulation), you have to set the multiplexor parameter first (which will
adapt the data structure immediately) and then the parameters of the records. Extended data records or
snapshot records can also be implemented as lists (iterations) of such records, similar to DTC lists. In this
case the complex parameter CAPL functions have to be used (please see chapter 9).
The example below for these data structures is not universal, there are different ways to model and implement
such data structures in diagnostic description files. Especially for extended data records, it is not typical as it
does only provide access to one specific extended data record, not to all at once.
Note: The parameter qualifiers in the following examples depend on the diagnostic description which is used,
therefore you most likely need to adapt them to your requirements.
diagSendRequest(req);
}
if(diagIsPositiveResponse(this))
{
DTC = diagGetParameter(this, "DTC"); // Get the DTC
statusByte = diagGetParameter(this, "Status_Of_Dtc"); // Get the status byte
diagSendRequest(req);
}
if(diagIsPositiveResponse(this))
{
DTC=DiagGetParameter(this,"DTC"); // get DTC
statusByte=DiagGetParameter(this, "StatusOfDTC"); // get status byte
write("DTC %06X",DTC);
write("StatusByte: %02X",statusByte);
iCount = diagGetIterationCount(this,
"ListOfDTCSnapshotRecord"); // Get the number of iterations
write("NumberOfIDs: %d",NoIDs);
write("DID 0x%04X - Wheel Speed FR: %.1f",
(word)DiagGetComplexParameter(this, "ListOfDTCSnapshotRecord",
index,"Wheel_Speed"), // display the DID-number in the write window
wheelSpeedFR);
Which function to use depends on the length of the key computation. If the computation is guaranteed to take
significantly less than 1 ms, DiagGenerateKeyFromSeed may be used. Otherwise
DiagStartGenerateKeyFromSeed in combination with the callback _Diag_GenerateKeyResult should be
used.
For CANoe test modules there are different functions (e.g. TestWaitForGenerateKeyFromSeed or
TestWaitForUnlockEcu) available.
For a general introduction of diagnostics in test modules please look into chapter 12.
Templates for the seed & key DLL (Visual Studio, C++) can be found in the directory:
The templates are only supplied with CANoe. The path slightly differs for older CANoe versions.
Please add the compiled seed & key DLL to the CANoe/CANalyzer Diagnostic Configuration as explained in
chapter 3
on diagResponse Door.SeedLevel1_Request
{
byte seedArray[4];
long ret;
char buffer[100];
if(diagIsPositiveResponse(this))
{
ret=DiagGetParameterRaw(this,"SecuritySeed",seedArray, elcount(seedArray));
if(ret>=0)
{
write("Seed is (hex) %X %X %X %X",
seedArray[0],seedArray[1],seedArray[2],seedArray[3]);
// calculate the key using the received seed: security level 1, variant
"Common"
ret=DiagStartGenerateKeyFromSeed("Door",seedArray,
elcount(seedArray),1,"Common","");
ret=DiagSetParameterRaw(reqKey,"SecurityKey",
computedKey,elcount(computedKey)); // put the key into the send key service
if(ret>=0)
DiagSendRequest(reqKey); // send the key
else
write("Could not set key-parameter");
}
else // errors occured while calculating the key
{
write("Error code %ld during key calculation",result);
if(result==-84)
write("invalid security level");
if(result==-86)
write("buffer too small");
}
}
switch(iSecurityLevel)
{
case 1: // algorithm for security level 1: swap the 2 seed bytes
ioKeyArray[0]=iSeedArray[1];
ioKeyArray[1]=iSeedArray[0];
oSize=2;
break;
For diagnostic tests with its request-response pairing, these test sequences are very suitable elements. After
sending a request, you should make sure the request has been actually sent using the function
TestWaitForDiagRequestSent. Afterwards you wait for the response using the TestWaitForDiagResponse
function, the received parameters of the response can be obtained with the diagGetRespParameter function.
The TFS offers the test report features that provide HTML reports of the tests. Diagnostic objects can be
written into this test report using the TestReportWriteDiagObject function. There is also a
TestReportWriteDiagResponse function.
It is important to evaluate the response of the TestWaitForDiagResponse function, as it could hint to either
the timeout specified in this function or the diagnostic P2 or P2 extended timeout. See KnowledgeBase article:
Handling of TestWaitFor DiagResponse Function.
testcase TestCase1_ReadSpeedFR()
{
DiagRequest Door.Wheel_Speed_Read req;
long ret,ret2,ret3;
char buffer[100];
float wheelSpeedFR;
13 Simulating an ECU
Since CANoe 10.0 the simulation of an ECU has been simplified. It is no longer necessary to add the CAPL
Callback Interface (CCI) as well as the OSEK_TP.DLL to the simulation node.
However, if your configuration needs to run on older CANoe versions or you need to execute tests including
fault injection on TP level or you need to deal with more complex ECU simulations, using the CCI and the
OSEK_TP.DLL is still necessary.
Please find more information about the CCI in the Application Note “AN-IND-1-
012_CAPL_Callback_Interface”, which is provided in the Doc-folder of your CANoe installation
> Open CANoe and add a CAPL node in the Simulation Setup.
> Add the diagnostic description for the ECU to be simulated under Configuration |
Diagnostics/ISO-TP configuration.
> Activate the check box Simulation by and select the previously created CAPL node.
Please find below a chart which shows the steps outlined above in a more compact way.
Please note: For LIN and FlexRay ECUs, adding a database (*.ldf, *.fibex) is mandatory.
on diagRequest *
{
diagResponse this resp;
diagSendPositiveResponse(resp);
}
on diagRequest Door.Serial_Number_Read
{
diagResponse Door.Serial_Number_Read resp;
byte serialNumber[13] = {
0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF
};
long ret;
ret = diagSetParameterRaw(resp,
"Serial_Number", serialNumber, elCount(serialNumber));
if(ret >= 0)
diagSendResponse(resp);
}
Please open the diagnostic configuration and select under Usage of the diagnostics description the entry
Functional Group Requests.
When waiting for the response, keep in mind that there will probably be more than one response,
so you have to keep waiting for a response after the reception of a response as long as a P2
timeout has not occurred (because after reception of a response, the P2 timer is reset).
diagResize(req, elCount(rawData));
DiagSetPrimitiveData(req,rawData,elcount(rawData));
DiagSendRequest(req);
// is equivalent to:
diagResize(req2, 2);
DiagSetPrimitiveByte(req2,0,0x10);
DiagSetPrimitiveByte(req2,1,0x99);
DiagSendRequest(req2);
on diagResponse Door.*
{
byte rawData[4095];
if((DiagGetPrimitiveByte(this,0)==0x50)
&&(DiagGetPrimitiveByte(this,1)==0x99))
{
write("This is the response to not-defined service 0x10 0x99");
DiagGetPrimitiveData(this,rawData,elcount(rawData));
write("%d Bytes received",DiagGetPrimitiveSize(this));
}
}
However, for complex services the Diag…Primitive functions might be better suitable than Basic
Diagnostics.
16 Object-oriented programming
In CAPL the datatypes diagRequest and diagResponse can also be used like classes
in object-oriented programming languages such as C++.
This means that all diagnostics functions (methods) belonging to the diagRequest- or diagResponse-class
can be called like methods on objects.
Please find the CAPL sample from chapter 7 adapted to object-oriented programming below.
if(ret>=0)
{
ret = req.SendRequest();
if(ret>=0)
write("Request has been (partially) sent");
else
write("Could not sent request");
}
else
{
write("Could not set parameter");
}
CHM Help:
• AN-IND-1-001_CANoe_CANalyzer_as_Diagnostic_Tools
• AN-IND-1-004_Diagnostics_via_Gateway_in_CANoe
• AN-IND-1-012_CAPL_Callback_Interface
The Application Notes are provided in the Doc-folder of your CANoe\CANalyzer installation.