使用 Python3 中的结构记录解析串行数据

Parsing serial data with a Structure Record in Python3

我正在尝试解析通过串行端口从监视器接收到的数据。我已经成功地在 Python 中编写了一个脚本,以字节字符串的形式从监视器中检索数据,并根据 ISO3309 标准进行了必要的数据转换,最后执行了校验和以确保数据完整性。下面是提取的数据。

[198, 2, 40, 4, 0, 0, 131, 35, 158, 94, 0, 0, 0, 0, 0, 0, 0, 0, 1, 22, 1, 1, 44, 2, 4, 189, 189, 255, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 131, 35, 158, 94, 51, 58, 0, 0, 0, 18, 2, 128, 10, 129, 1, 128, 1, 128, 1, 128, 1, 0, 0, 0, 1, 0, 2, 128, 2, 128, 2, 128, 1, 128, 1, 0, 0, 0, 2, 0, 2, 128, 2, 128, 2, 128, 1, 128, 0, 0, 0, 0, 11, 0, 1, 128, 1, 128, 1, 128, 1, 128, 0, 0, 0, 0, 3, 0, 1, 128, 1, 128, 1, 128, 1, 128, 3, 0, 0, 0, 3, 1, 1, 128, 1, 128, 1, 128, 1, 128, 3, 0, 0, 0, 11, 0, 4, 128, 3, 0, 0, 0, 12, 0, 4, 128, 0, 0, 0, 0, 13, 0, 1, 128, 0, 0, 0, 0, 14, 0, 1, 128, 3, 0, 0, 0, 0, 0, 1, 128, 2, 128, 2, 128, 1, 128, 7, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 203, 29, 3, 0, 0, 0, 0, 0, 41, 8, 41, 8, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 0, 0, 0, 0, 11, 0, 1, 128, 1, 128, 1, 128, 1, 128, 32, 0, 0, 0, 0, 0, 1, 128, 1, 128, 255, 141, 2, 128, 1, 128, 1, 128, 0, 0, 0, 0, 173, 1, 1, 128, 0, 0, 0, 0, 13, 0, 1, 128, 1, 128, 1, 128, 1, 128, 0, 0, 0, 0, 14, 0, 1, 128, 1, 128, 1, 128, 1, 128, 0, 0, 0, 0, 65, 0, 131, 35, 158, 94, 0, 0, 0, 0, 18, 2, 1, 128, 1, 128, 1, 128, 1, 128, 3, 0, 0, 0, 0, 0, 1, 128, 1, 128, 4, 128, 1, 128, 1, 128, 1, 128, 73, 13, 0, 0, 176, 192, 0, 0, 226, 1, 0, 0, 198, 2, 0, 0, 240, 127, 170, 1, 0, 0, 0, 0, 9, 49, 28, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 3, 0, 0, 0, 0, 0, 0, 0, 0, 195, 165, 0, 0, 195, 165, 1, 128, 72, 8, 160, 15, 32, 78, 208, 7, 208, 7, 112, 23, 80, 5, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 52, 8, 0, 0, 0, 0, 208, 7, 112, 23, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141]

我有结构record.Please见附件 记录可能是用 C 编写的,这无济于事。但是,我的问题是 Header。如果,Struct: 只是一个具有多种数据类型的自定义变量。 Header 的结构是否直接对应于我的数据列表中的第一个整数?例如:r_len = 198、r_nbr=2、r_time=0?我是否继续按顺序向下移动列表以获得子记录?如果是这样,时间定义为自 1.1.1970 以来的秒数。那么这怎么可能来自单个整数呢?如果您能提供任何提示,我将不胜感激。感谢您阅读我的长文post。 Structure Record for the data

嗨,罗伯特,感谢你完成了我刚刚愉快完成的任务!

规范明确表示它以字节为单位传递数据,由 C-like 结构描述。我看不出你是如何读取数据的,但我猜你在这里得到了一个字节列表,因为那里的每个值都小于 256。

因此,通过查看结构,我可以尝试将其转换为 human-readable 形式。这是通过 python:

中的 struct 模块完成的
b = bytes(l)
hdr_data = struct.unpack_from('<hBBHLBBHH', b)
print(hdr_data)

说明

此处我将l视为初始字节列表。根据您提供的记录结构,我构建了 header:

的格式字符串

hBBHLBBHH表示:short, byte, byte, word, dword, byte, byte, word, word.

开头的

<表示byte-ordering是little-endian

打印出来的结果是

(710, 40, 4, 0, 1587422083, 0, 0, 0, 0)

这里的第一个值是 r_len,等于 710。你列表的长度 len(l) 是 711,所以很可能我对列表格式和字节顺序的初步猜测是正确的,结果你不小心有一个多余的字节。

下一步

虽然我们已经基本完成了 one-liner,但我们还没有阅读 sr_desc,也没有设置合适的名称匹配。

让我们更改拆包线以同时消耗 struct sr_desc[8]

hdr_data = struct.unpack_from('<hBBHLBBHH' + 'hB'*8, b)

现在我们将所有内容都放在一个元组中。

访问字段的一种非常方便的方法是 namedtuple

from collections import namedtuple
D_O_hdr = namedtuple('D_O_hdr',
                     ('r_len', 'r_nbr', 'dri_level', 'plug_id', 
                      'r_time', 'n_subset', 'res', 'dest_plug_id',
                      'r_maintype',))

header = D_O_hdr(*hdr_data[:9])
print(header)

您现在可以看到您可以轻松访问每个字段,例如 print(header.r_len) 等等。

相同,但有点棘手,因为 sr_data:

struct_sr_desc = namedtuple('sr_desc', ('sr_offset', 'sr_type',))

sr_data = [struct_sr_desc(off, typ) 
           for off, typ in zip(hdr_data[9::2], hdr_data[10::2])]

# Just make sure we've got a list of 8 objects
assert len(sr_data) == 8

这里的问题是我们需要将普通列表转换为结构列表。 hdr_data[9::2]sr_desc 的每个第一个字段(即 sr_offset),hdr_data[10::2] 取每个第二个字段。 zip 使一对列表成为一对列表。

sr_data[3].type 包含 255,这意味着您在一个结构中只有三个子记录,但解包它们已经在您身上了!只是不要忘记 sr_data[i].offset 包含距 header 末尾的偏移量。这很明显,因为 sr_data[0].offset 总是 0 但无论如何。

完整的脚本在这里:https://repl.it/@FooBarrior/CreativePartialOpenlook

祝你好运!