Потоковая передача данных с помощью ProtoBuf в файл журнала с заголовком - PullRequest
1 голос
/ 23 октября 2019

Я пытаюсь передать данные в файл журнала на SD-карте на микроконтроллере, который считывает данные с некоторых датчиков и сохраняет значение в файле.

Для сериализации данных я буду использовать NanoPB,Реализация protobuf для C, которая является достаточно ресурсоэффективной.

Файл журнала имеет следующую структуру: ему нужно написать короткий заголовок, состоящий из GUID, и версию прошивки. После заголовка поток данных должен быть непрерывным, и он должен регистрировать поля от датчиков, но не значения заголовка (это должно происходить только один раз и в начале).

Ограничение состоит в том, что я могу толькоиспользуйте один файл .proto для сериализации и десериализации, и я хочу избежать использования функций Pb_callback, возникающих из-за использования «повторяющихся» полей в .proto, а затем из C-реализации nanopb. 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 сообщения, я не смогу десериализовать их за один раз с одним файлом прото. Мне нужно знать длину первых двух сообщений, десериализовать их, а затем начать с журнала.

1 Ответ

1 голос
/ 23 октября 2019

Я хочу избежать функций Pb_callback, возникающих при использовании «повторяющихся» полей в .proto

Обратите внимание, что вы можете указать max_count для повторяющихся полей, как вы указалиmax_size для строк, и вместо обратного вызова вы получите простой массив.

Хотя, если я разделю его на 2 сообщения, я не смогу десериализовать их за один раз с однимфайл прото.

Для десериализации Protobuf необходимо знать тип сообщения. Наиболее распространенный способ справиться с этим - создать одно сообщение верхнего уровня с вложенными сообщениями:

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

Затем вы можете установить одно или оба поля заголовка и датчика, а также has_header и has_sensorsв true или false, чтобы указать, хотите ли вы включить это подполе. Но независимо от содержимого, вы всегда сериализуете и десериализуете как LogMessage, так что нет никакой путаницы между различными типами сообщений.

Мне нужно знать длину первых двух сообщений, десериализоватьих, а затем начинайте с лога.

Да, это обычная проблема для новичков и с protobuf. Сами сообщения Protobuf не кодируют их длину, поэтому, если у вас есть несколько сообщений в одном файле, вам нужно как-то их разделить.

A весьма распространенный способ - добавить префикс длины, так каксделано наноппами pb_encode_delimited() и pb_decode_delimited(). Этот формат также поддерживается библиотекой C ++ protobuf. Однако недостатком этого является то, что многие инструменты командной строки, такие как protoc, не поддерживают формат с разделителями, и, например, библиотека Python protobuf делает их декодирование несколько сложным .

Другой вариант заключается всделать так, чтобы весь файл выглядел так, как будто это было одно сообщение, но пишите его несколькими частями. Протобуфы имеют функцию слияния, то есть, если вы просто добавляете сообщения друг за другом, они объединяются вместе. Это можно сделать, включив повторяющиеся поля в LogMessage:

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

Теперь, если вы закодируете несколько копий LogMessage, каждая с одной записью sensors, они объединятся. Затем, если вы декодируете файл, он будет выглядеть как один LogMessage с несколькими sensors записями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...