欢迎大家来到带你玩转车载测试系列课程,我是分享人M哥,目前从事车载控制器的软件开发及测试工作。
学习过程中如有任何疑问,可底下评论!
如果觉得文章内容在工作学习中有帮助到你,麻烦点赞收藏评论+关注走一波!感谢各位的支持!
CANoe作为一款功能强大的总线开发及测试工具,不仅仅是因为其丰富的库函数,最重要的是其兼容了多种编程语言,可以实现灵活的开发,极大的解决了纯CAPL编程所带来的限制,如通过动态链接库(DLL)来实现复杂函数的编写,使其能够在CAPL中可以调用,下面将对DLL的实现进行分析。
1. 什么是capldll
capldll是指利用C/C++语言按照VECTOR制定的规则生成的可以在CAPL中进行调用的.dll文件,与一般的dll文件所不同的是,capldll文件必须按照Vector官方指定的模板进行生成,方可在CAPL中进行调用,否则将无法调用。
Vector官方指定的模板位于C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 17.0.201\Programming\CAPLdll\文件下,如下图。
文件中有VS2017和VS2013两个工程,大家可以根据自己VS的版本选择任意工程,接下里我将以VS2017这一工程举例说明。
2. VS2017工程介绍
在VS2017工程中打开capldll.sln文件,如下图所示:
capldll.cpp这个文件初看相当复杂,从整体上来说,这段代码主要包含了两部分:自定义函数实现和回调函数定义,今天的主要任务是实现自定义函数,这里的函数定义与普通函数定义差不多,只是利用了CAPL的格式。
其格式如下所示:
(1)编写一个赋值函数:
void CAPLEXPORT far CAPLPASCAL appPut(unsigned long x
{
data = x;
}
{"dllPut", (CAPL_FARCALL)appPut, "CAPL_DLL","This function will save data from CAPL to DLL memory",'V', 1, "D", "\000", {"x"}},
其中,各个参数具体含义如下:
CAPL中数据类型与C/C++中的数据类型对应关系如下:
(2)使用引用编写dll函数:
void CAPLEXPORT far CAPLPASCAL appSum(long i, long j, long* s)
{
*s = i + j;
}
{"sum", (CAPL_FARCALL) appSum, "CAPL_DLL", "Sum via
reference parameter", 'V', 3, {'L', 'L', 'L' - 128}, "", {"i", "j", "s"} },
3. 实例解读
生成capldll文件包含两个数求和的函数,并在CANoe中调用验证,代码如下。
/*----------------------------------------------------------------------------
|
| File Name: capldll.cpp
|
| Example of a capl DLL implementation module and using CAPLLbacks.
|-----------------------------------------------------------------------------
| A U T H O R I D E N T I T Y
|-----------------------------------------------------------------------------
| Author Initials
| ------ --------
| Thomas Riegraf Ri Vector Informatik GmbH
| Hans Quecke Qu Vector Informatik GmbH
| Stefan Albus As Vector Informatik GmbH
|-----------------------------------------------------------------------------
| R E V I S I O N H I S T O R Y
|-----------------------------------------------------------------------------
| Date Ver Author Description
| ---------- --- ------ --------------------------------------------------
| 2003-10-07 1.0 As Created
| 2007-03-26 1.1 Ej Export of the DLL function table as variable
| Use of CAPL_DLL_INFO3
| Support of long name CAPL function calls
| 2020-01-23 1.2 As Support for GCC and Clang compiler on Linux
| Support for MINGW-64 compiler on Windows
|-----------------------------------------------------------------------------
| C O P Y R I G H T
|-----------------------------------------------------------------------------
| Copyright (c) 1994 - 2003 by Vector Informatik GmbH. All rights reserved.
----------------------------------------------------------------------------*/
#define USECDLL_FEATURE
#define _BUILDNODELAYERDLL
#include "../Includes/cdll.h"
#include "../Includes/VIA.h"
#include "../Includes/VIA_CDLL.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <map>
#if defined(_WIN64) || defined(__linux__)
#define X64
#endif
class CaplInstanceData;
typedef std::map<uint32_t, CaplInstanceData*> VCaplMap;
typedef std::map<uint32_t, VIACapl*> VServiceMap;
// ============================================================================
// global variables
// ============================================================================
//static uint32_t data = 0;
//static char dlldata[100];
VCaplMap gCaplMap;
VServiceMap gServiceMap;
// ============================================================================
// CaplInstanceData
//
// Data local for a single CAPL Block.
//
// A CAPL-DLL can be used by more than one CAPL-Block, so every piece of
// information thats like a global variable in CAPL, must now be wrapped into
// an instance of an object.
// ============================================================================
class CaplInstanceData
{
public:
CaplInstanceData(VIACapl* capl);
void GetCallbackFunctions();
void ReleaseCallbackFunctions();
// Definition of the class function.
// This class function will call the CAPL callback functions
uint32_t ShowValue(uint32_t x);
uint32_t ShowDates(int16_t x, uint32_t y, int16_t z);
void DllInfo(const char* x);
void ArrayValues(uint32_t flags, uint32_t numberOfDatabytes, uint8_t databytes[], uint8_t controlcode);
void DllVersion(const char* y);
private:
// Pointer of the CAPL callback functions
VIACaplFunction* mShowValue;
VIACaplFunction* mShowDates;
VIACaplFunction* mDllInfo;
VIACaplFunction* mArrayValues;
VIACaplFunction* mDllVersion;
VIACapl* mCapl;
};
CaplInstanceData::CaplInstanceData(VIACapl* capl)
// This function will initialize the CAPL callback function
// with the NLL Pointer
: mCapl(capl),
mShowValue(nullptr),
mShowDates(nullptr),
mDllInfo(nullptr),
mArrayValues(nullptr),
mDllVersion(nullptr)
{}
static bool sCheckParams(VIACaplFunction* f, char rtype, const char* ptype)
{
char type;
int32_t pcount;
VIAResult rc;
// check return type
rc = f->ResultType(&type);
if (rc!=kVIA_OK || type!=rtype)
{
return false;
}
// check number of parameters
rc = f->ParamCount(&pcount);
if (rc!=kVIA_OK || strlen(ptype)!=pcount )
{
return false;
}
// check type of parameters
for (int32_t i=0; i<pcount; ++i)
{
rc = f->ParamType(&type, i);
if (rc!=kVIA_OK || type!=ptype[i])
{
return false;
}
}
return true;
}
static VIACaplFunction* sGetCaplFunc(VIACapl* capl, const char* fname, char rtype, const char* ptype)
{
VIACaplFunction* f;
// get capl function object
VIAResult rc = capl->GetCaplFunction(&f, fname);
if (rc!=kVIA_OK || f==nullptr)
{
return nullptr;
}
// check signature of function
if ( sCheckParams(f, rtype, ptype) )
{
return f;
}
else
{
capl->ReleaseCaplFunction(f);
return nullptr;
}
}
void CaplInstanceData::GetCallbackFunctions()
{
// Get a CAPL function handle. The handle stays valid until end of
// measurement or a call of ReleaseCaplFunction.
mShowValue = sGetCaplFunc(mCapl, "CALLBACK_ShowValue", 'D', "D");
mShowDates = sGetCaplFunc(mCapl, "CALLBACK_ShowDates", 'D', "IDI");
mDllInfo = sGetCaplFunc(mCapl, "CALLBACK_DllInfo", 'V', "C");
mArrayValues = sGetCaplFunc(mCapl, "CALLBACK_ArrayValues", 'V', "DBB");
mDllVersion = sGetCaplFunc(mCapl, "CALLBACK_DllVersion", 'V', "C");
}
void CaplInstanceData::ReleaseCallbackFunctions()
{
// Release all the requested Callback functions
mCapl->ReleaseCaplFunction(mShowValue);
mShowValue = nullptr;
mCapl->ReleaseCaplFunction(mShowDates);
mShowDates = nullptr;
mCapl->ReleaseCaplFunction(mDllInfo);
mDllInfo = nullptr;
mCapl->ReleaseCaplFunction(mArrayValues);
mArrayValues = nullptr;
mCapl->ReleaseCaplFunction(mDllVersion);
mDllVersion = nullptr;
}
void CaplInstanceData::DllVersion(const char* y)
{
// Prepare the parameters for the call stack of CAPL.
// Arrays uses a 8 byte (64 bit DLL: 12 byte) on the stack, 4 Bytes for the number of element,
// and 4 bytes (64 bit DLL: 8 byte) for the pointer to the array
int32_t sizeX = (int32_t)strlen(y)+1;
#if defined(X64)
uint8_t params[16]; // parameters for call stack, 16 Bytes total (8 bytes per parameter, reverse order of parameters)
memcpy(params+8, &sizeX, 4); // array size of first parameter, 4 Bytes
memcpy(params+0, &y, 8); // array pointer of first parameter, 8 Bytes
#else
uint8_t params[8]; // parameters for call stack, 8 Bytes total
memcpy(params+0, &sizeX, 4); // array size of first parameter, 4 Bytes
memcpy(params+4, &y, 4); // array pointer of first parameter, 4 Bytes
#endif
if(mDllVersion!=nullptr)
{
uint32_t result; // dummy variable
VIAResult rc = mDllVersion->Call(&result, params);
}
}
uint32_t CaplInstanceData::ShowValue(uint32_t x)
{
#if defined(X64)
uint8_t params[8]; // parameters for call stack, 8 Bytes total
memcpy(params + 0, &x, 8); // first parameter, 8 Bytes
#else
void* params = &x; // parameters for call stack
#endif
uint32_t result;
if(mShowValue!=nullptr)
{
VIAResult rc = mShowValue->Call(&result, params);
if (rc==kVIA_OK)
{
return result;
}
}
return -1;
}
uint32_t CaplInstanceData::ShowDates(int16_t x, uint32_t y, int16_t z)
{
// Prepare the parameters for the call stack of CAPL. The stack grows
// from top to down, so the first parameter in the parameter list is the last
// one in memory. CAPL uses also a 32 bit alignment for the parameters.
#if defined(X64)
uint8_t params[24]; // parameters for call stack, 24 Bytes total (8 bytes per parameter, reverse order of parameters)
memcpy(params+16, &z, 2); // third parameter, offset 16, 2 Bytes
memcpy(params+ 8, &y, 4); // second parameter, offset 8, 4 Bytes
memcpy(params+ 0, &x, 2); // first parameter, offset 0, 2 Bytes
#else
uint8_t params[12]; // parameters for call stack, 12 Bytes total
memcpy(params+0, &z, 2); // third parameter, offset 0, 2 Bytes
memcpy(params+4, &y, 4); // second parameter, offset 4, 4 Bytes
memcpy(params+8, &x, 2); // first parameter, offset 8, 2 Bytes
#endif
uint32_t result;
if(mShowDates!=nullptr)
{
VIAResult rc = mShowDates->Call(&result, params);
if (rc==kVIA_OK)
{
return rc; // call successful
}
}
return -1; // call failed
}
void CaplInstanceData::DllInfo(const char* x)
{
// Prepare the parameters for the call stack of CAPL.
// Arrays uses a 8 byte (64 bit DLL: 12 byte) on the stack, 4 Bytes for the number of element,
// and 4 bytes (64 bit DLL: 8 byte) for the pointer to the array
int32_t sizeX = (int32)strlen(x)+1;
#if defined(X64)
uint8_t params[16]; // parameters for call stack, 16 Bytes total (8 bytes per parameter, reverse order of parameters)
memcpy(params+8, &sizeX, 4); // array size of first parameter, 4 Bytes
memcpy(params+0, &x, 8); // array pointer of first parameter, 8 Bytes
#else
uint8_t params[8]; // parameters for call stack, 8 Bytes total
memcpy(params+0, &sizeX, 4); // array size of first parameter, 4 Bytes
memcpy(params+4, &x, 4); // array pointer of first parameter, 4 Bytes
#endif
if(mDllInfo!=nullptr)
{
uint32_t result; // dummy variable
VIAResult rc = mDllInfo->Call(&result, params);
}
}
void CaplInstanceData::ArrayValues(uint32_t flags, uint32_t numberOfDatabytes, uint8_t databytes[], uint8_t controlcode)
{
// Prepare the parameters for the call stack of CAPL. The stack grows
// from top to down, so the first parameter in the parameter list is the last
// one in memory. CAPL uses also a 32 bit alignment for the parameters.
// Arrays uses a 8 byte (64 bit DLL: 12 byte) on the stack, 4 Bytes for the number of element,
// and 4 bytes (64 bit DLL: 8 byte) for the pointer to the array
#if defined(X64)
uint8_t params[32]; // parameters for call stack, 32 Bytes total (8 bytes per parameter, reverse order of parameters)
memcpy(params+24, &controlcode, 1); // third parameter, offset 24, 1 Bytes
memcpy(params+16, &numberOfDatabytes, 4); // second parameter (array size), offset 16, 4 Bytes
memcpy(params+ 8, &databytes, 8); // second parameter (array pointer), offset 8, 8 Bytes
memcpy(params+ 0, &flags, 4); // first parameter, offset 0, 4 Bytes
#else
uint8_t params[16]; // parameters for call stack, 16 Bytes total
memcpy(params+ 0, &controlcode, 1); // third parameter, offset 0, 1 Bytes
memcpy(params+ 4, &numberOfDatabytes, 4); // second parameter (array size), offset 4, 4 Bytes
memcpy(params+ 8, &databytes, 4); // second parameter (array pointer), offset 8, 4 Bytes
memcpy(params+12, &flags, 4); // first parameter, offset 12, 4 Bytes
#endif
if(mArrayValues!=nullptr)
{
uint32_t result; // dummy variable
VIAResult rc = mArrayValues ->Call(&result, params);
}
}
CaplInstanceData* GetCaplInstanceData(uint32_t handle)
{
VCaplMap::iterator lSearchResult(gCaplMap.find(handle));
if ( gCaplMap.end()==lSearchResult )
{
return nullptr;
}
else
{
return lSearchResult->second;
}
}
// ============================================================================
// CaplInstanceData
//
// Data local for a single CAPL Block.
//
// A CAPL-DLL can be used by more than one CAPL-Block, so every piece of
// information thats like a global variable in CAPL, must now be wrapped into
// an instance of an object.
// ============================================================================
void CAPLEXPORT CAPLPASCAL add (long a, long b, long *sum)
{
*sum = a + b;
}
// ============================================================================
// VIARegisterCDLL
// ============================================================================
VIACLIENT(void) VIARegisterCDLL (VIACapl* service)
{
uint32_t handle;
VIAResult result;
if (service==nullptr)
{
return;
}
result = service->GetCaplHandle(&handle);
if(result!=kVIA_OK)
{
return;
}
// appInit (internal) resp. "DllInit" (CAPL code) has to follow
gServiceMap[handle] = service;
}
void ClearAll()
{
// destroy objects created by this DLL
// may result from forgotten DllEnd calls
VCaplMap::iterator lIter=gCaplMap.begin();
const int32_t cNumberOfEntries = (int32_t)gCaplMap.size();
int32_t i = 0;
while ( lIter!=gCaplMap.end() && i<cNumberOfEntries )
{
//appEnd( (*lIter).first );
lIter = gCaplMap.begin(); // first element should have vanished
i++; // assure that no more erase trials take place than the original size of the map
}
// just for clarity (would be done automatically)
gCaplMap.clear();
gServiceMap.clear();
}
// ============================================================================
// CAPL_DLL_INFO_LIST : list of exported functions
// The first field is predefined and mustn't be changed!
// The list has to end with a {0,0} entry!
// New struct supporting function names with up to 50 characters
// ============================================================================
CAPL_DLL_INFO4 table[] = {
{CDLL_VERSION_NAME, (CAPL_FARCALL)CDLL_VERSION, "", "", CAPL_DLL_CDECL, 0xabcd, CDLL_EXPORT },
{"dlladd", (CAPL_FARCALL)add, "CAPL_DLL","This function will calculate SUM",'V', 3, {'L','L','L'-128}, "", {"a","b","sum"}},
};
CAPLEXPORT CAPL_DLL_INFO4* caplDllTable4 = table;
Debug后生成capldll.dll文件导入CAPL如下图:
运行CANoe输出结果如下:
说明我们生成的capldll文件可以实现功能。
感谢对本期内容不遗余力的学习,下期内容即将奉上,欢迎下次光临!