Проблемы с повторным полем в Protobuf. Как лучше использовать повторяющееся поле для сериализации / десериализации? - PullRequest
0 голосов
/ 08 января 2020

Рассмотрим следующий файл sensor.proto, который использует повторяющееся поле для инициализации нескольких сообщений.

syntax = "proto3";

package HUBSensors;

message Device {
    string name = 1;
    int32 id = 2;

    message Sensor {
        string name = 1;
        double temperature = 2;
        int32 humidity = 3;

        enum SwitchLevel {
            CLOSED = 0;
            OPEN = 1;
        }

        SwitchLevel door = 4;
    }

    repeated Sensor sensors = 3;
}

Теперь я хочу сериализовать некоторые случайные данные в файл. В качестве примера у меня будет одно устройство с несколькими датчиками, отсюда и повторный файл в прото. Я использую следующий код.

inline void serialize_to_file( const std::string &fileName )
{
    HUBSensors::Device device;

    device.set_name("HUB");
    device.set_id(1234);

    device.add_sensors()->set_name("Laboratory");
    device.add_sensors()->set_temperature(23.3);
    device.add_sensors()->set_humidity(2);
    device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);

    device.add_sensors()->set_name("Chml Laboratory");
    device.add_sensors()->set_temperature(2.3);
    device.add_sensors()->set_humidity(5);
    device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);

    device.add_sensors()->set_name("GU Laboratory");
    device.add_sensors()->set_temperature(8.3);
    device.add_sensors()->set_humidity(2);
    device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);


    std::ofstream ofs(fileName, std::ios_base::out | std::ios_base::binary );
    device.SerializeToOstream( &ofs );
    google::protobuf::ShutdownProtobufLibrary();
}

Чтобы проанализировать данные и распечатать результат, я использую следующее:

inline void parse_from_file( const std::string &fileName )
{
    HUBSensors::Device device;
    std::ifstream myFile;
    myFile.exceptions( std::ifstream::badbit );

    try {
        myFile.open(fileName);

        while ( myFile.good() )
        {
            device.ParseFromIstream( &myFile );

            //std::cout << device.sensors_size() << std::endl;

            std::cout << "Device Name : " << device.name() << std::endl;
            std::cout << "^^^^^^" << std::endl;
            for ( size_t i = 0; i < device.sensors_size(); i+=4)
            {
                std::cout << "Sensors Name : " << device.sensors(i).name() << std::endl;
                std::cout << "Temperature : " << device.sensors(i+1).temperature() << std::endl;
                std::cout << "Humidity : " << device.sensors(i+2).humidity() << std::endl;
                std::cout << " Door Status :  " << device.sensors(i+3).door() << std::endl;
                std::cout << "^^^^^^" << std::endl;
            }
        }
    }
    catch ( const std::ifstream::failure &e )
    {
        std::cerr << "Error Occurred when accessing the file" << std::endl;
        std::cout << e.what() << std::endl;
    }
    myFile.close();
    google::protobuf::ShutdownProtobufLibrary();
}

Основной файл для воспроизведения результатов:

#include <iostream>
#include <fstream>
#include <stdexcept>

#include "sensor.pb.h"


int main() {

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    const std::string fileName = "./device.data";

    serialize_to_file( fileName );
    parse_from_file( fileName );

    return 0;
}

Не кажется интуитивно понятным перебирать общий размер датчика и получать правильный индекс для отображения соответствующего поля. Даже при проверке

std::cout << device.sensors_size() << std::endl;

будет выводиться размер 12, который он не считает правильным, или

std::cout << decice.sensors(2).name() << std::endl;

не будет выводить что-либо, поскольку он находится в неверном индексе. Какой лучший способ использовать libprotobuf для определения повторяющегося поля для сериализации / десериализации лучше.


РЕДАКТИРОВАТЬ: Как следует из ответа, сериализация в файл должна быть как


inline void serialize_to_file( const std::string &fileName )
{
    HUBSensors::Device device;

    device.set_name("HUB");
    device.set_id(1234);

    auto sensor1 = device.add_sensors();
    auto sensor2 = device.add_sensors();
    auto sensor3 = device.add_sensors();

    sensor1->set_name("Laboratory");
    sensor1->set_temperature(23.3);
    sensor1->set_humidity(2);
    sensor1->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);

    sensor2->set_name("GU Laboratory");
    sensor2->set_temperature(44.3);
    sensor2->set_humidity(4);
    sensor2->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);

    sensor3->set_name("Chml Laboratory");
    sensor3->set_temperature(13.345);
    sensor3->set_humidity(6);
    sensor3->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);

    std::ofstream ofs(fileName, std::ios_base::out | std::ios_base::binary );
    device.SerializeToOstream( &ofs );
    google::protobuf::ShutdownProtobufLibrary();
}

1 Ответ

3 голосов
/ 08 января 2020

Вместо

    device.add_sensors()->set_name("Laboratory");
    device.add_sensors()->set_temperature(23.3);
    device.add_sensors()->set_humidity(2);
    device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);

вы должны написать

    auto sensor = device.add_sensors();
    sensor->set_name("Laboratory");
    sensor->set_temperature(23.3);
    sensor->set_humidity(2);
    sensor->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);

Таким образом, у вас будет 3 датчика, что, как я полагаю, является вашим намерением, и все они будут иметь каждый элемент данных комплект.

...