Десериализация потокового сообщения буфера протокола с полями заголовка и повторения - PullRequest
2 голосов
/ 21 января 2020

Я работаю над десериализацией файла журнала, который был сериализован в C с использованием буферов протокола (и NanoPB).

Файл журнала имеет короткий заголовок, состоящий из: объекта, версии и идентификатора. После заголовка поток данных должен быть непрерывным и должен регистрировать поля от датчиков, но не значения заголовка (это должно происходить только один раз и в начале). Тот же файл .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
...

Я отправил это вопрос , чтобы выяснить, как изначально структурировать файл .proto, когда я выполнял сериализацию в C. И, наконец, я использовал похожий подход, но не включил: [(nanopb) .max_count = 1];

Наконец, я выбрал следующее .proto в Python (может быть больше датчиков, чем 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.

Output of the deserializer

Это мой текущий код, используемый для десериализации сообщение в Python:

import PSessionLogs_pb2

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

Кроме того, я использовал proto c, чтобы сгенерировать .py эквивалент файла .proto для десериализации в автономном режиме.

1 Ответ

2 голосов
/ 22 января 2020

Похоже, что вы сериализовали заголовок, а затем сразу же сериализовали некоторые другие данные, что означает: вместо сериализации SessionLogs, у которой есть некоторые записи SessionLogsDetail, вы сериализовали SessionLogs, а затем вы Сериализовал (отдельно) SessionLogsDetail - это звучит примерно так? если так: да, это не будет работать правильно; есть способов сделать то, что вам нужно, но это не так просто, как просто сериализовать один за другим, потому что root объект protobuf никогда не завершается ; так что на самом деле происходит перезапись root объекта более поздними полями по номерам.

Существует два способа решения этой проблемы в зависимости от объема данных. Если размер (включая все строки сведений) невелик, вы можете просто изменить код так, чтобы он был истинным родителем / дочерним отношением, т. Е. Чтобы все строки были внутри родительского элемента. При записи данных это не означает , что означает, что вам нужно иметь все строки перед началом записи - существуют способы добавления дочерних строк, чтобы вы отправляли данные по мере их доступности; однако при десериализации он захочет загрузить все за один go, поэтому этот подход полезен только в том случае, если вы согласны с этим, т. е. у вас нет непристойных открытых рядов строк .

Если у вас большое количество строк, вам, по сути, нужно добавить собственное кадрирование. Это часто делается путем добавления префикса длины между каждой полезной нагрузкой, так что вы можете по существу читать по одному сообщению за раз. Некоторые библиотеки включают вспомогательные методы для этого; например, в java API это parseDelimitedFrom и writeDelimitedTo. Тем не менее, я понимаю, что 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}

(где префикс длины - это что-то простое, например, необработанный вариант, или просто 4-байтовое целое в некотором согласованном порядке байтов)

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