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

标签 c logging protocol-buffers proto nanopb

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

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

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

限制是我只能使用一个 .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 条消息,我将无法使用一个原始文件一次性反序列化它们。我需要知道前两条消息的长度,反序列化它们,然后从那里开始处理日志。

最佳答案

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

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

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

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

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

然后您可以将 header 和传感器字段中的一个或两个设置为 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 消息本身不对其长度进行编码,因此如果您在单个文件中有多个消息,则需要以某种方式将它们分开。

A 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

关于c - 使用 ProtoBuf 将数据流式传输到带有 header 的日志文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58518051/

相关文章:

c - C 中的 Arraylist 在重新分配时失败

c - 'sizeof' 对不完整类型问题的无效应用

c - C 中的字符数组移位

c++ - 您如何管理日志记录性能?

c++ - Protobuf导致ParseFromIstream上的段错误

c++ - 使用 bool 导出压缩结构

Django:仅记录我项目的应用程序

java - 在 log4j 2 中使用 throwable 和参数

protocol-buffers - Protocol Buffer : Version Change

go - 如何使 Go gRPC 在服务器端与标准 IO 一起工作?