本文是共包括4部分的技术博客的第三部分。此博客介绍通过kvmlib,使用第二代Kvaser Memorator数据记录仪,配置和读取已记录的数据:
技术博客
进一步了解kvmlib
在本博客的第一部分中,我们说明了如何使用kvmlib配置设备,并读取用python配置的报文。在这一部分中,我们将再去看一下我们在第二部分中所做的配置,介绍怎样使用kvmlib做更多操作,包括保存和恢复你在Kvaser Memorator的SD卡上的配置。你可能对下面有一个博客系列感兴趣:Setting Up CANlib for Visual Studio(为视觉工作室设置CANlib)。
3.1 打开设备
使用kvmlib打开设备需要的不仅是EAN编号。当使用Python canlib设备模块连接到一个设备时,此代码会在内部查找连接到计算机上的所有设备,并给出第一个符合搜索条件的设备,在本例中的条件是EAN编号。
# 05_报告_设备.py
from canlib import Device, EAN
#通过最后6位数字,为我们的设备生成一个EAN目标
ean = EAN('00819-9')
print('Full EAN: {}\n'.format(ean))
# 寻找我们的 Kvaser Memorator Pro 2xHS ,系列号EAN 00819-9
dev = Device.find(ean=ean, serial=None)
# 打印有关这个设备的信息
print(dev.probe_info())
Full EAN: 73-30130-00819-9
CANlib Channel: 0
Card Number : 0
Device : Kvaser Memorator Pro 2xHS v2 (channel 0)
Driver Name : kcany0a
EAN : 73-30130-00819-9
Firmware : 3.11.0.557
Serial Number : 11573
列表 8: 打印一个已连接上的Kvaser设备的信息。
现在你可以注意到,我们的设备连接到CANlib通道0,这个CANlib通道号用来从CANlib1打开此设备。如果我们打开的是第二个设备,第二个设备上的那些编号将不同。
当使用kvmlib打开我们的设备,除了指定这个CANlib通道号,我们还可以指定一个设备类型。这个设备类型用来确定该设备在使用什么版本的LIO数据格式2/a>。固件版本v3.0使用 设备型号 kvmDEVICE_MHYDRA_EXT
, 这是v1.7版本python canlib模块的缺省设置。kvmlib.openDevice
的最后一个自变量是mount
,它表明Memorator的存储区是否已装上(缺省是False
)。
让我们来看一下怎样用kvmlib配置此设备。
# 06_设置_时钟.py
from canlib import kvmlib
import datetime
# 我们知道我们的设备已连接到CANlib 通道0
channel = 0
# 我们还知道我们的设备的类型是kvmDEVICE_MHYDRA_EXT
with kvmlib.openDevice(
channel_number=channel,
device_type=kvmlib.Device.MHYDRA_EXT) as device:
# 我们已经获得了此设备, 比如说,现在我们可以查看系列号
print("Serial number:", device.serial_number)
# 读取设备时间
print("Current device time is", device.rtc)
# 设置该设备的实时钟
device.rtc = datetime.datetime.now()
# 读取设备时间
print("New device time is", device.rtc)
列表 9: 用kvmlib为一个Kvaser设备设置实时钟。
Serial number: 11573
Current device time is 2018-08-10 07:47:46
New device time is 2018-08-10 07:52:48
运行上面的代码可获得下面的结果:
3.2 初始化SD卡
如前所述,在打开一个设备时,使用正确的设备类型是非常重要的。如果我们使用错误的LIO数据格式(如使用设备类型规定的那样)打开一个设备,在读取数据时,我们将无法看到任何记录的报文。在初始化SD卡时,使用正确的设备类型更为重要,因为如果使用不正确的LIO数据格式初始化SD卡,固件将无法访问该卡。
为了初始化此SD卡,我们需要设备类型。我们用kvmlib.openDevice
来打开设备,在一个Kvaser Memorator二代设备(运行固件版本v3.0)上的设备类型是 kvmDEVICE_MHYDRA_EXT
3。
然后我们调用 format_disk
初始化SD卡。在初始化当中,SD的大部分空间是用来存储记录报文的,但是我们可以通过给初始化命令添加两个不同的自变量,影响两个部分的存储空间大小。
第一个自变量是指定“为用户文件保留空间”,这意味着SD卡的这部分空间将供用户支配使用。我们可以在该设备中运行一个 t程序,生成文本文件,或者我们可能有其他用户文件要复制到SD卡上。
第二个自变量能影响一个叫做DATABASE.BIN 的文件大小, Kvaser Memorator Config Tool在使用它,另外它可能还包含设置和数据库文件,用户可以选择把这类文件存储在SD卡上。
在装上Memorator新格式化的记录区以后,我们检查是否获得了预期的LIO数据格式,从而验证我们提供了正确的设备类型。
# 07_验证_lio_初始化_sdcard.py
from canlib import kvmlib
from canlib import VersionNumber
# 之前我们看到该设备已连接到CANlib 通道 0
channel = 0
# 我们还知道此设备类型是kvmDEVICE_MHYDRA_EXT
device_type = kvmlib.kvmDEVICE_MHYDRA_EXT
# 打开设备
with kvmlib.openDevice(channel_number=channel,
device_type=device_type,
mount=False) as memorator:
# 格式化 SD 卡并给配置文件保留 10 MB
# (i.e. DATABASE.BIN) 和 10000 MB 给我们自己的文件。
print('Initializing SD card...')
memorator.format_disk(reserved_space=10000,
database_space=10)
# 装上存储区
memorator.mount()
# 读取LIO数据格式版本
lio_data_format = memorator.log.ldf_version
# 验证SD卡的LIO数据格式和此设备的类型相符 - 我们用这个类型来打开设备
if lio_data_format != VersionNumber(major=5, minor=0):
if lio_data_format == VersionNumber(major=3, minor=0):
raise ValueError('This log file can be read if you reopen the'
' device as kvmDEVICE_MHYDRA.')
raise ValueError('Unexpected Lio Data Format: ' +
str(lio_data_format))
# 报告此卡上分配给数据存储区的信息
print('Disk size: {disk:.0f} MB\n'
' Log size: {log:.0f} MB. Used: {used:.0f} MB'.
format(disk=memorator.disk_size,
log=memorator.disk_usage.total,
used=memorator.disk_usage.used))
列表10: 验证 LIO数据格式和初始化一个Kvaser设备的SD卡。
Initializing SD card...
Disk size: 15932 MB
Log size: 5895 MB. Used: 0 MB
我们的Kvaser Memorator设备有一个16 GB的SD卡,当我们现在为DATABASE.BIN保留10 MB,和分配10 GB到闲余空间时,我们只剩下5895 MB用于记录存储。因为我们刚刚初始化了SD卡,所以我们记录了0 MB(已用)数据。
此设备及其SD卡现在已初始化并为配置做好准备。
3.3 保存配置
为了将配置保存到设备的SD卡,我们将验证过的XML配置转换为二进制配置,并将此二进制配置下载到设备上(就像我们在这个博客系列的第一部分中所做的那样)。
# 08_保存_设置.py
from canlib import kvamemolibxml
from canlib import kvmlib
# 我们之前看到此设备连接到了CANlib 通道 0
channel = 0
# 读取 XML 设置文件
with open("config.xml", 'r') as myfile:
print('Reading xml')
config_xml = myfile.read()
# 把XML设置转换为二进制设置
config_lif = kvamemolibxml.kvaXmlToBuffer(config_xml)
# 打开memorator 设备并书写该设置
# 设备类型 kvmDEVICE_MHYDRA_EXT 是缺省设置
with kvmlib.openDevice(channel_number=channel) as memorator:
print('Writing config')
memorator.write_config(config_lif)
列表 11: 转换并下载配置到一个Kvaser 设备上。
# 09_复制_文件_到_设备.py
import zipfile
from canlib import canlib
# 生成压缩文档
with zipfile.ZipFile("config.zip", mode='w',
compression=zipfile.ZIP_DEFLATED) as zipf:
# 添加文件到压缩文档中
zipf.write("config.xml")
zipf.write("myCanGenerator.txe")
# 我们知道此设备已连接到CANlib通道0
channel = 0
# 打开设备并书写压缩文档
with canlib.openChannel(channel=channel) as ch:
# 因为SD卡是用FAT格式化的, 我们应该使用一个8.3格式的文件名作为目标文件名
ch.fileCopyToDevice("config.zip", "config.zip")
列表 12:用压缩文档下载明确文字配置。
3.4读取结果并保存到文件上
当此设备进入现场运行后(即我们通过CAN接头通电并运行脚本),我们可以再次将设备连接到计算机上并读取已记录的数据6 。
为了将来保存数据,我们现在使用kme50格式将数据写入文件。此格式可在之后使用转换器 – 包括在Kvaser Memorator Config Tool中 – 转换为多种格式。
所有记录文件中的第一个条目都包含有关存储设备的信息。我们在这里利用这个特点,将部分EAN和序列号加到生成的.kme50
文件的名称中。
当从设备中读取到所有记录文件后,我们将删除设备上的记录文件,以便为新的记录操作做好准备(重复使用相同的配置)。
# 10_读取_已记录的_数据.py
import glob
import os
from canlib import EAN, VersionNumber
from canlib import kvmlib
# 我们之前看到我们的设备连接到 CANlib 通道 0
channel = 0
# 将存放结果文件的目录
resultDir = "result"
# 确认结果目录存在而且是空闲的
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)
# 打开设备并装上存储区配置
# 设备类型 kvmDEVICE_MHYDRA_EXT 是缺省设置
# 注意 openDevice() 可能会看到KvmDiskNotFormated
with kvmlib.openDevice(channel_number=channel, mount=True) as memorator:
lio_data_format = memorator.log.ldf_version
# 确认SD卡的LIO 数据格式符合设备类型,我们用此类型来打开该设备
if lio_data_format != VersionNumber(major=5, minor=0):
if lio_data_format == VersionNumber(major=3, minor=0):
raise ValueError('This log file can be read if you reopen the'
' device as kvmDEVICE_MHYDRA.')
raise ValueError('Unexpected Lio Data Format: ' + str(lio_data_format))
# 读取记录下来的记录文件数量
num_log_files = len(memorator.log)
print("Found {num} file(s) on card: ".format(num=num_log_files))
# 选择所有记录文件并将它们的内容书写到.kme50 文件
for i, log_file in enumerate(memorator.log):
print("\n==== File {index}: {start} - {end}, approx {num} events".
format(index=i,
start=log_file.start_time,
end=log_file.end_time,
num=log_file.event_count_estimation()))
# 第一个记录事件包括设备信息
event_iterator = iter(log_file)
first_event = next(event_iterator)
ean = EAN.from_hilo([first_event.eanHi, first_event.eanLo])
serial = first_event.serialNumber
# 把EAN和系列号添加到文件名上
logfile_name = ('log_{ean}_{sn}_{index:03}.kme50'.
format(ean=str(ean), sn=serial, index=i))
print('Saving to:', logfile_name)
with kvmlib.createKme(logfile_name) as kme:
print(first_event)
kme.write_event(first_event)
for event in event_iterator:
# 把事件书写到stdout上
print(event)
kme.write_event(event)
# 删除所有记录文件
# memorator.log.删除所有文件()
列表 13:读取已记录的数据并保存到.kme50文件上。
运行上面的程序,在此记录里出现的每个通道上,我们能看到6个报文。
Found 1 file on card:
==== File 0: 2018-08-09 15:19:45 - 2018-08-09 15:19:51, approx 20 events
Saving to: log_73-30130-00819-9_11573_000.kme50
*t: - EAN:73-30130-00819-9 s/n:11573 FW:v3.11.557 LIO:v5.0
t: 2.824746112 DateTime: 2018-08-09 15:19:45
t: 2.824746112 Log Trigger Event (type: 0x2, trigno: 0x01, pre-trigger: 0, post-trigger: 0)
t: 2.824746112 ch:1 f: 2 id: 3 dlc: 8 d:12 21 13 31 22 34 41 15
t: 2.824746137 Log Trigger Event (type: 0x2, trigno: 0x01, pre-trigger: 0, post-trigger: 0)
t: 2.824746137 ch:0 f: 42 id: 3 dlc: 8 d:12 21 13 31 22 34 41 15
t: 3.824770237 ch:1 f: 2 id: 4 dlc: 8 d:12 21 13 31 22 34 41 15
t: 3.824770262 ch:0 f: 42 id: 4 dlc: 8 d:12 21 13 31 22 34 41 15
t: 4.824788362 ch:1 f: 2 id: 5 dlc: 8 d:12 21 13 31 22 34 41 15
t: 4.824788387 ch:0 f: 42 id: 5 dlc: 8 d:12 21 13 31 22 34 41 15
t: 5.824810487 Log Trigger Event (type: 0x2, trigno: 0x01, pre-trigger: 0, post-trigger: 2500)
t: 5.824810487 ch:1 f: 2 id: 6 dlc: 8 d:12 21 13 31 22 34 41 15
t: 5.824810512 ch:0 f: 42 id: 6 dlc: 8 d:12 21 13 31 22 34 41 15
t: 6.824826612 ch:1 f: 2 id: 7 dlc: 8 d:12 21 13 31 22 34 41 15
t: 6.824826637 ch:0 f: 42 id: 7 dlc: 8 d:12 21 13 31 22 34 41 15
t: 7.824851737 ch:1 f: 2 id: 8 dlc: 8 d:12 21 13 31 22 34 41 15
t: 7.824851762 ch:0 f: 42 id: 8 dlc: 8 d:12 21 13 31 22 34 41 15
参阅博客 Examining logged data with kvmlib and Python 上的另一个范例 – 有关怎样使用kvmlib读取一个设备的数据。
3.5 从设备上读取配置。
之前,我们将配置的副本放在SD卡的config.zip文件中,现在可以使用CANlib读取所有用户文件,然后可以使用7-zip7打开结果文件。
# 11_读取_配置_从_设备.py
import os
from canlib import canlib
# 我们的设备是连接到 CANlib 通道 0
channel = 0
# 打开设备
with canlib.openChannel(channel=channel) as ch:
# 列出设备上的文件
numFiles = ch.fileGetCount()
if numFiles:
for i in range(numFiles):
name = ch.fileGetName(i)
# 跳过已知文件
if (os.path.splitext(name)[1].lower() == '.kmf'
or name.lower() == 'param.lif'
or name.lower() == 'database.bin'):
print('Skipping', name)
else:
# 复制用户文件到电脑
print('Copying: ', name)
ch.fileCopyFromDevice(name, name)
列表 14: 从 Kvaser设备上复制用户文件。
至此本文讨论了用kvmlib设置时间和配置我们的Kvaser Memorator设备。我们将在下一部分,也是本系列博客的最后一部分,介绍当我们只能处理SD卡时,怎样进行操作,之后将它插入Kvaser Memorator 设备中。
注释
1 Read more in the post Was that the CANlib channel number or Card Number?
2 The LIO data format is the specification on how to store data on the SD card. The latest LIO data format, v5.0, is capable of handling the larger data frames that can be generated by CAN FD.
3 Firmware earlier than 3.0 should use device type kvmDEVICE.MHYDRA.
4 当你点击“Save Configuration and Databases on the Disk(保存配置和数据库到卡上)”时,这和 Kvaser Memorator Config Tool 的功能相似。此工具然后将把使用过的配置和数据库文件放入 DATABASE.BIN文件。
5 在本博客的第二部分, Adding script and triggers to a configuration.(为配置添加脚本和触发点
)。
6 我们很可能还应该在这里检查LIO数据格式版本, 参照我们之前的初始化代码来了解怎样操作。
7 7-zip是一个开放资源Windows档案处理使用功能, 请见 www.7-zip.org 了解更多信息。