使用 ProtoBuf 将数据流式传输到带有 header 的日志文件

Streaming data with ProtoBuf to log file with a header

我正在尝试将数据流式传输到微控制器上 SD 卡上的日志文件,该微控制器从一些传感器读取数据并将值存储在文件中。

为了序列化我将使用 NanoPB 的数据,C 的 protobuf 实现非常节省资源。

日志文件结构如下:需要写一个简短的header,由GUID和固件版本组成。在 header 之后,数据流应该是连续的,它应该记录来自传感器的字段而不是 header 值(这应该只发生一次并且在开始时)。

限制是我只能使用一个 .proto 文件进行序列化和反序列化,我想避免使用 .proto 中的 "repeated" 字段然后使用nanopb 的 C 实现。 https://jpa.kapsi.fi/nanopb/docs/concepts.html.

我尝试过的实现如下(字段只是示例):

syntax = "proto3";

import "timestamp.proto";
import "nanopb.proto";

message LogHeader {
    string firmware = 1 [(nanopb).max_size = 11];  
    string GUID = 2 [(nanopb).max_size = 11];       
}

message Sensors {
    int32 TimeStamp = 3;        
    // Sensory data
    int32 Sens1 = 4;
    int32 Sens2 = 5;
    int32 Sens3 = 6;
    int32 Sens4 = 7;
    int32 Sense5 = 8;

}

我们的想法是拥有一个经过处理后如下所示的日志文件:

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
...

但是如果所有字段都在同一条消息中,则每次重复都会记录 GUID 和固件。如果我将它分成 2 条消息,我将无法使用一个原始文件一次性反序列化它们。我需要知道前两条消息的长度,反序列化它们,然后从那里开始处理日志。

I want to avoid Pb_callback functions that emerge from using "repeated" fields in the .proto

请注意,您可以为重复字段指定 max_count,就像为字符串指定 max_size 一样,然后您将得到一个简单的数组而不是回调。

While if I split it in 2 messages I have not been able to deserialize them in one go with one proto file.

Protobuf 反序列化需要知道消息类型。处理此问题的最常见方法是使用带有子消息的单个 top-level 消息:

message LogMessage {
   optional LogHeader header = 1;
   optional Sensors sensors = 2;
}

然后您可以将 header 和传感器字段以及 has_headerhas_sensors 中的一个或两个设置为 true 或 false,以指示您是否要包括该子字段。但是无论内容如何,​​你总是序列化和反序列化为LogMessage,所以不同消息类型之间不会混淆。

I would need to know the length of the first two messages, deserialize them and then start from there on with the log.

是的,这也是 protobuf 初学者常见的问题。 Protobuf 消息本身不对其长度进行编码,因此如果您在一个文件中有多个消息,则需要以某种方式将它们分开。

一个quite common way is to add a length prefix, as done by nanopb's pb_encode_delimited() and pb_decode_delimited(). This format is also supported by the C++ protobuf library. However, a drawback of that is that many command line tools like protoc do not support the delimited format, and e.g. Python protobuf library makes decoding them somewhat complex.

另一种选择是使整个文件看起来像一条消息,但将其分成多个部分。 Protobuf 具有合并功能,也就是说,如果您只是一个接一个地附加消息,它们就会合并在一起。这可以通过在 LogMessage:

中包含重复字段来完成
message LogMessage {
   optional LogHeader header = 1;
   repeated Sensors sensors = 2 [(nanopb).max_count = 1];
}

现在,如果您对 LogMessage 的多个副本进行编码,每个副本都有一个 sensors 条目,它们将合并在一起。然后,如果您解码该文件,它将显示为具有多个 sensors 条目的单个 LogMessage