【问题标题】:Streaming data with ProtoBuf to log file with a header使用 ProtoBuf 将数据流式传输到带有标题的日志文件
【发布时间】:2019-10-23 07:51:28
【问题描述】:

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

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

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

限制是我只能使用一个 .proto 文件进行序列化和反序列化,并且我想避免使用 .proto 中的“重复”字段然后使用 nanopb 的 C 实现而出现的 Pb_callback 函数。 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 条消息,我无法使用一个 proto 文件一次性反序列化它们。我需要知道前两条消息的长度,对它们进行反序列化,然后从那里开始记录日志。

【问题讨论】:

    标签: c logging protocol-buffers proto nanopb


    【解决方案1】:

    我想避免使用 .proto 中的“重复”字段而出现的 Pb_callback 函数

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

    如果我将其拆分为 2 条消息,我无法使用一个 proto 文件一次性反序列化它们。

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

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

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

    我需要知道前两条消息的长度,对它们进行反序列化,然后从那里开始记录日志。

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

    quite common way 是添加一个长度前缀,正如 nanopb 的 pb_encode_delimited()pb_decode_delimited() 所做的那样。 C++ protobuf 库也支持这种格式。但是,这样做的一个缺点是许多命令行工具(如 protoc)不支持分隔格式,例如Python protobuf 库可以对它们进行解码somewhat complex

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

    message LogMessage {
       optional LogHeader header = 1;
       repeated Sensors sensors = 2 [(nanopb).max_count = 1];
    }
    

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

    【讨论】:

    • 如果我理解正确, .max_count 不是指您期望的最大重复次数(我不知道)吗?我不明白您为什么将其设置为1。我知道这是为了避免回调,但是为什么要设置为1?感谢@jpa 的解释。
    • @A.San 如果您只想一次编码一个传感器子消息,1 就足够了。但是,是的,您将无法使用 nanopb 以这种方式解码组合消息,您需要回调。
    • 好的,在这种情况下,我只需要用 nanopb 编码并在其他地方解码。所以这应该完成编码的工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    • 2016-03-01
    • 2020-02-13
    • 2020-01-23
    • 1970-01-01
    • 2020-07-24
    • 2013-05-17
    相关资源
    最近更新 更多