技术博客

12/04/2016 作者 Magnus Carlsson

深入了解kvmlib

这是使用Kvaser Memorator第二代设备通过kvmlib进行配置和读取记录数据4篇系列文章的第三篇:

在第一篇文章中,我们已经讲述了如何使用kvmlib来配置设备,并利用Python读取记录的报文。在本文中,我们将用低级函数重新查看我们在第一篇文章中所设的配置。这些低级函数与本地C语言API共享他们的名称,所以你使用C语言API代替Python的时候不会遇到任何问题。我们也有关于为Visual Studio设置CANlib的系列博文,可能会对你有用。全部程序列表可在GitHub上获得。

3.1 打开设备

使用CANlib打开设备需要的知识不仅仅是知道编码(EAN)。 当使用Pythons kvDevice模块连接到设备时,代码在内部查看连接到我们计算机的所有设备,并返回与我们的搜索条件匹配的第一个设备,在我们的例子中是编码。

# Connect to our Kvaser Memorator Pro 5xHS with EAN 00778-9
dev = kvDevice.kvDevice(ean="73-30130-00778-9")
print dev

列表11打印有关已连接的Kvaser设备信息

>>> Device: Kvaser Memorator Pro 5xHS (channel 0)
EAN : 73-30130-00778-9
S/N : 1023
FW : v3.0.546
Card : 0
Drv : kcany0a
Card channel : 0
Canlib channel: 0

在这里我们注意到我们的设备连接到CANlib通道0和Card通道号0。当从CANlib打开设备时使用CANlib通道号,并且在kvmlib中打开设备时以相应的方式使用Card通道号1。如果我们已打开第二台设备,那些数字在第二台设备上将不同。

当使用kvmlib打开我们的设备时,除了使用Card通道指定设备之外,我们还需要指定设备类型。设备类型用于决定设备使用的LIO数据格式的版本2。v3.0版本的固件使用设备类型“kvmDEVICE_MHYDRA_EXT”。

让我们来看看如何直接使用kvmlib设置设备时钟,这与你如何使用C语言API进行设置相一致。

ml = kvmlib.kvmlib()

# Since our firmware is v3.0 we should be using kvmDEVICE_MHYDRA_EXT
# as the device type.
deviceType = kvmlib.kvmDEVICE_MHYDRA_EXT

# We know that our device is connected to Card channel number 0
cardChannel = 0
 
# We have firmware version 3.0 in our device, this means that
# the FW is using Lio Data Format v5.0 and we should use
# kvmDEVICE_MHYDRA_EXT as the deviceType
ml.deviceOpen(memoNr=cardChannel, devicetype=deviceType)

# Having obtained a kvmHandle, we can now e.g. check the
# device serial number
print "Serial number:%d" % ml.deviceGetSerialNumber()
  
# Set the real time clock of the device
ml.deviceSetRTC(datetime.datetime.now())
  
# Read the device time
print "Current device time is %s" % ml.deviceGetRTC()
 
# Close device
ml.close()

列表12:使用kvmlib设置Kvaser设备的实时时钟

Serial number:1023
Current time is 2016-02-11 13:01:00.509000
Setting time...
Current device time is 2016-02-11 13:01:00

3.2 初始化SD卡

如前所述,打开设备时使用正确的设备类型非常重要。如果我们使用错误的LIO数据格式(参考使用设备类型)打开设备,我们将无法在读取期间看到任何日志报文。在初始化SD卡时使用正确的设备类型更为重要,因为固件将无法访问使用不正确的LIO数据格式初始化的SD卡。

想要初始化SD卡,需使用kvmlib打开kvmHandle,此处我们需要提供我们的设备类型,它在Kvaser Memorator第二代设备(运行在fw v3.0)上是kvmDEVICE_MHYDRA_EXT.3

调用deviceMountKmf()返回LIO数据格式,因此我们可以方便地检查我们提供的是正确的设备类型。在下面的代码中,我们可看到SD卡尚未初始化的情况。

大多数SD卡被分配用于在初始化期间记录日志报文,但我们可以通过传递两个不同的参数到初始化命令影响两种文件的大小。

第一个参数是指定“为用户文件保留的空间”,这意味着SD卡容量可以免费供用户使用。我们可以在设备中运行/t/程序生成文本文件,或者我们可能有想要复制到SD卡的其他用户文件。

第二个参数影响名为DATABASE.BIN的文件的大小,该文件由Kvaser Memorator配置工具使用,可能包含用户可选择保存到SD卡的配置和数据库文件。

ml = kvmlib.kvmlib()
  
# Since our firmware is v3.0 we should be using kvmDEVICE_MHYDRA_EXT
# as the device type.
deviceType = kvmlib.kvmDEVICE_MHYDRA_EXT
 
# We saw earlier that our device is connected to Card channel number 0
cardChannel = 0
 
# Open the device
ml.deviceOpen(memoNr=cardChannel, devicetype=deviceType)
  
try:
    # Mount the log area
    lioDataFormat = ml.deviceMountKmf()
    print "Lio Data Format v%s" % lioDataFormat
    # Verify that the LIO data format of the card corresponds to
    # the device type we used when opening the device
    if lioDataFormat != '5.0':
        print "Unexpected Lio Data Format:", lioDataFormat
        if lioDataFormat == '3.0':
            print("This log file can be read if you reopen the"
                  " device as kvmDEVICE_MHYDRA.")
        exit(1)
except kvmlib.kvmDiskNotFormated:
    print "SD card is not initialized..."
    exit(1)
  
# Format the SD Card and reserve 10 MB for configuration files
# (i.e. DATABASE.BIN) and 1000 MB for our own files.
print "Initializing SD card..."
ml.deviceFormatDisk(reserveSpace=10000, dbaseSpace=10)
  
# Report info about the disk area allocated for logging
(diskSize, usedDiskSize) = ml.kmfGetUsage()
print "Log size: %d MB\nUsed: %d MB" % (diskSize, usedDiskSize)
  
# Close the device
ml.close()

列表13:验证LIO数据格式并初始化Kvaser设备磁盘

Lio Data Format v5.0 Initializing SD card... Log size: 6112 MB Used: 0 MB

我们的设备配16GB的SD卡,当我们现在为DATABASE.BIN预留10MB空间以及10GB可用空间,我们剩下6112MB可用于记录数据。由于我们刚刚初始化了SD卡,已记录数据为0MB(已使用)。

设备SD卡现在已初始化,可以进行配置。

3.3 保存配置

要配置设备,我们将已验证的XML配置转换为二进制配置并将此二进制配置下载到设备。这就像我们在这个博文系列的第一篇文章中做的一样,但是在这里我们直接调用kvmlib,因此需要公开deviceTypecardChannel的用法。

ml = kvmlib.kvmlib()
xl = kvaMemoLibXml.kvaMemoLibXml()
  
# Since our firmware is v3.0 we should be using kvmDEVICE_MHYDRA_EXT
# as the device type.
deviceType = kvmlib.kvmDEVICE_MHYDRA_EXT
  
# We saw earlier that our device is connected to Card channel number 0
cardChannel = 0
  
# Read in the XML configuration file
with open("config.xml", 'r') as myfile:
    config_xml = myfile.read()
  
# Convert the XML configuration to a binary configuration
config_lif = xl.kvaXmlToBuffer(config_xml)
  
# Open the device and write the configuration
ml.deviceOpen(memoNr=cardChannel, devicetype=deviceType)
ml.kmfWriteConfig(config_lif)
  
# Close the device
ml.close()

列表14:将配置转换和下载到Kvaser设备

为了以后能够以明文检索配置,我们对已经使用和下载到设备上的文件创建一个zip文件(config.zip)4 。由于早前当我们编译t程序时我们使用了参数 -addsrc,我们只需要编译的.txe文件和我们的XML配置5 。如果我们使用任何数据库,我们也将它们添加到zip包中。

import zipfile
import canlib
  
# Create Zip archive
with zipfile.ZipFile("config.zip", mode='w',
                     compression=zipfile.ZIP_DEFLATED) as zipf:
    # Adding files to zip archive
    zipf.write("config.xml")
    zipf.write("myCanGenerator.txe")
  
cl = canlib.canlib()
  
# We know that our device was connected to CANlib channel number 0
canlibChannel = 0
  
# Open the device and write the zip archive
ch = cl.openChannel(channel=canlibChannel)
# Since the SD card is formated using FAT, we should use
# a 8.3 filename as the target filename
ch.fileCopyToDevice("config.zip", "config.zip")
 
ch.close()

列表15:使用zip包下载明文配置

3.4 读取结果并保存到文件

在设备已经在现场(即我们已经通过CAN连接器供电并让脚本运行)运行之后,我们可以再次将我们的设备连接到我们的计算机并读取记录的数据。6 为了保存数据以备将来使用,我们现在使用kme50格式将数据写入文件。以后可以使用Kvaser Memorator配置工具中包含的转换器将此格式转换为多种格式。

所有日志文件中的第一个条目包含有关已记录设备的信息。我们在这里利用这一优势,将产品编码和产品序列号的一部分放在生成的.kme50文件的名称中。

当从设备读取所有日志文件时,我们删除设备上的日志文件,以便为新的日志记录做好准备(重用相同的配置)。

import glob
import os
  
ml = kvmlib.kvmlib()
  
# Since our firmware is v3.0 we should be using kvmDEVICE_MHYDRA_EXT
# as the device type.
deviceType = kvmlib.kvmDEVICE_MHYDRA_EXT
  
# We saw earlier that our device is connected to Card channel number 0
cardChannel = 0
  
# Directory to put the resulting files in
resultDir = "result"

=# Make sure the result directory exists and is empty
if os.path.isdir(resultDir):
    files = glob.glob(os.path.join(resultDir, "*"))
    for f in files:
        os.remove(f)
else:
    os.mkdir(resultDir)
os.chdir(resultDir)
 
# Open the device
ml.deviceOpen(memoNr=cardChannel, devicetype=deviceType)

try:
    # Mount the log area
    lioDataFormat = ml.deviceMountKmf()
    print "Lio Data Format v%s" % lioDataFormat
    # Verify that the LIO data format of the card corresponds to
    # the device type we used when opening the device
    if lioDataFormat != '5.0':
        print "Unexpected Lio Data Format:", lioDataFormat
        if lioDataFormat == '3.0':
            print("This log file can be read if you reopen the"
                  " device as kvmDEVICE_MHYDRA.")
        exit(1)
except kvmlib.kvmDiskNotFormated:
    print "SD card is not initialized..."
    exit(1)

# Read number of recorded logfiles
fileCount = ml.logFileGetCount()
print "Found %d file%s on card:" % (fileCount,
                                    "s" if fileCount > 1 else "")

# Loop through all logfiles and write their contents to .kme50 files
for fileIndx in range(fileCount):
    eventCount = ml.logFileMount(fileIndx)
    print "\tFile %3d: %10d events" % (fileIndx, eventCount)
    logEvent = ml.logFileReadEventLogFormat()
    #
    # The first logEvent contains device information
    memoEvent = logEvent.createMemoEvent()
    sn = memoEvent.serialNumber
    ean_lo = memoEvent.eanLo
    ean_sn = "%05x-%x_%d" % ((ean_lo >> 4) & 0xfffff, ean_lo & 0xf, sn)
    # Add EAN and serial number info to filename
    logfileName = "log_%s_%d.kme50" % (ean_sn, fileIndx)
    ml.kmeCreateFile(logfileName, kvmlib.kvmFILE_KME50)
    while logEvent is not None:
        # Write event to stdout
        print logEvent
        ml.kmeWriteEvent(logEvent)
        # Read next event
        logEvent = ml.logFileReadEventLogFormat()
    ml.kmeCloseFile()
  
# Delete all logfiles
ml.logFileDeleteAll()
  
# Close device
ml.close()

列表16:读取记录的数据并保存到.kme50文件

阅读博文使用kvmlib和Python分析记录的数据 ,了解如何使用kvmlib从设备读取数据的另一个示例。

3.5 从设备读取配置

之前我们将配置的副本放在SD卡上的config.zip文件中,我们现在可以使用CANlib读取所有用户文件。然后所获取文件可以使用诸如7-zip压缩软件打开。7

import os

import canlib

cl = canlib.canlib()

# We already knew that our device was connected to CANlib channel number 0
canlibChannel = 0
# Open the device
ch = cl.openChannel(channel=canlibChannel)

# List files on device
numFiles = ch.fileGetCount()

if numFiles:
    for i in range(numFiles):
        name = ch.fileGetName(i)
        # Skip known system files
        if (os.path.splitext(name)[1].lower() == '.kmf'
                or name.lower() == 'param.lif'
                or name.lower() == 'database.bin'):
            print "Skipping %s" % name
        else:
            # Copy user files to PC
            print "Copying %s" % name
            ch.fileCopyFromDevice(name, name)

ch.close()

列表17:从Kvaser设备复制用户文件

Skipping PARAM.LIF
Skipping LOG00000.KMF
Skipping LOG00001.KMF
Skipping LOG00002.KMF
Skipping LOG00003.KMF
Skipping LOG00004.KMF
Skipping LOG00005.KMF
Skipping DATABASE.BIN
Copying CONFIG.ZIP

因此这篇关于使用低级kvmlib调用配置我们的Kvaser Memorator设备的文章到此结束。我们将在本系列文章的下一篇和最后一篇中,讲述针对如果我们只能访问插入设备的SD卡所要采取的步骤。

脚注

1 更多内容见博文是CANlib通道号还是卡号?

2 LIO数据格式是关于如何在SD卡上存储数据的规范。最新的LIO数据格式v5.0能够处理CAN FD可生成的更大的数据帧。

3 早于3.0版本的固件应使用设备类型kvmDEVICE.MHYDRA

4 这类似于当您勾选“在磁盘上保存配置和数据库”时Kvaser Memorator配置工具所执行的操作。 然后,该工具将使用的配置和数据库文件插入到文件DATABASE.BIN中。

5 在上一篇文章中,在配置中添加脚本和触发器

6 我们应该也可以在这里查看LIO数据格式版本,参考我们早先的有关于如何实现的初始化代码。

7 7-zip是一款用于处理存档的开源Windows实用程序,详细信息请参阅www.7-zip.org

Author Image

Magnus Carlsson

Margus Carlsson是Kvaser AB公司的软件开发人员,从2007年以来深度参与了Kvaser固件和软件的开发。他还为Kvaser的技术博客撰写了许多用流行的Python语言编写应用程序的文章。