2

我正在反序列化使用协议缓冲区(和 NanoPB)在 C 中序列化的日志文件。

日志文件有一个简短的标头,由以下部分组成:实体、版本和标识符。在标头之后,数据流应该是连续的,它应该记录来自传感器的字段,而不是标头值(这应该只在开始时出现一次)。相同的 .proto 文件用于序列化文件。对于标头和流数据,我没有单独的 .proto 文件。

在我实施之后,我认为它应该如下所示:

firmware "1.0.0"
GUID "1231214211321" (example)
Timestamp 123123
Sens1 2343
Sens2 13123
Sens3 13443
Sens4 1231
Sens5 190
Timestamp 123124
Sens1 2345
Sens2 2312
...

当我在 C 中实现序列化时,我最初发布了这个问题来弄清楚如何构造 .proto 文件。最后我使用了类似的方法,但没有包括:[(nanopb).max_count = 1];

最后,我在 Python 中选择了以下 .proto (传感器可以多于 5 个):

syntax = "proto3";

import "timestamp.proto";

 message SessionLogs {
int32 Entity = 1;      
string Version = 2;  
string GUID = 3;     
repeated SessionLogsDetail LogDetail = 4;
}
message SessionLogsDetail 
{    
int32 DataTimestamp = 1;        // internal counter to identify the order of session logs
// Sensor data, there can be X amount of sensors.
int32 sens1 = 2;
int32 sens2= 3;
int32 sens3= 4;
int32 sens4= 5;
}

此时,我可以在使用设备登录时序列化一条消息,根据文件大小,日志似乎可以工作,但我无法在 Python 脱机上对其进行反序列化,以检查我的实现是否正确。而且我不能在 C 中做到这一点,因为它是一个嵌入式应用程序,我想用 Python 离线进行后处理。

另外,我已经检查了这个在线 protobuf 反序列化器,我可以在其中传递序列化文件并在不需要 .proto 文件的情况下对其进行反序列化。在其中,我可以看到标题值(字段 3 为空,因此看不到)和记录的信息。所以这让我认为序列化是正确的,但我在 Python 上错误地反序列化它。

解串器的输出

这是我当前用于在 Python 中反序列化消息的代码:

import PSessionLogs_pb2

with open('$PROTOBUF_LOG_FILENAME$', 'rb') as f:
read_metric =  PSessionLogs_pb2.PSessionLogs()
read_metric.ParseFromString(f.read())

除此之外,我还使用 protoc 生成了 .proto 文件的 .py 等价文件以脱机反序列化。

4

1 回答 1

2

看起来您已经序列化了一个标头,然后立即序列化了一些其他数据,这意味着:不是序列化具有一些 SessionLogsDetail 记录的 SessionLogs,而是序列化一个 SessionLogs,然后您已经序列化(单独)一个 SessionLogsDetail -那个听起来是对的吗?如果是这样:是的,这将无法正常工作;有很多方法可以做你所追求的,但它并不像一个接一个地序列化那么简单,因为根 protobuf 对象永远不会终止;所以实际发生的是它用后面的字段按数字覆盖根对象。

有两种方法可以解决这个问题,具体取决于数据量。如果大小(包括所有详细信息行)很小,您只需更改代码,使其成为真正的父/子关系,即所有行都在父级内。写入数据时,这并不意味着您需要在开始写入之前拥有所有行 - 有一些方法可以添加子行,以便您在数据可用时发送数据;但是,在反序列化时,它会希望一次性加载所有内容,因此这种方法只有在您对此感到满意时才有用,即您没有淫秽的开放式行数。

如果您有大量行,则基本上需要添加自己的框架。这通常是通过在每个有效负载之间添加一个长度前缀来完成的,这样您基本上可以一次读取一条消息。一些库为此提供了辅助方法;例如,在 java API 中,这是parseDelimitedFromwriteDelimitedTo. 但是,我的理解是 python API目前不支持这个实用程序,所以你需要自己做框架:(


总而言之,您目前拥有:

{header - SessionLogs}
{row 0 - SessionLogsDetail}
{row 1 - SessionLogsDetail}

选项1是:

{header - SessionLogs
  {row 0 - SessionLogsDetail}
  {row 1 - SessionLogsDetail}
}

选项 2 是:

{length prefix of header}
{header - SessionLogs}
{length prefix of row0}
{row 0 - SessionLogsDetail}
{length prefix of row1}
{row 1 - SessionLogsDetail}

(其中长度前缀很简单,例如原始 varint,或者在某些约定的字节序中只是一个 4 字节整数)

于 2020-01-21T21:59:33.690 回答