首页 > 解决方案 > nanopb中的编码和解码问题

问题描述

最近开始探索 nanopb - 如果我的问题听起来很愚蠢,请道歉。当我修改 nanopb 的简单示例时,我在分配和检索字符串和整数时遇到了一些问题。让我在我的问题之前给出我的步骤 -

  1. 我定义了 simple.proto 文件
message Device{
optional string devid =1; 
optional string mac = 2; 
optional string cpu=3 [default = "x86"] ; 
optional bool isSecured=4;
optional int32 uptime = 5 [default = 1234];
}

  1. 还定义了 simple.option
Device.devid max_size:64
Device.cpu max_size:64

然后我像往常一样编译:protoc -osimple.pb simple.proto

  1. 这是我的代码:

3a) 使用与如何对 pb_callback_t 类型的字符串进行编码中相同的字符串编码解码实用程序函数

//string encode decode to pb
bool encode_string(pb_ostream_t* stream, const pb_field_t* field, void* const* arg)
{
    const char* str = (const char*)(*arg);

    if (!pb_encode_tag_for_field(stream, field))
        return false;

    return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}

bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
    uint8_t buffer[1024] = {0};

    /* We could read block-by-block to avoid the large buffer... */
    if (stream->bytes_left > sizeof(buffer) - 1)
        return false;

    if (!pb_read(stream, buffer, stream->bytes_left))
        return false;

    /* Print the string, in format comparable with protoc --decode.
     * Format comes from the arg defined in main().     */
    printf((char*)*arg, buffer);
    return true;
}

3b)这是我基于 simple.c 示例的主要代码片段 -

    /* This is the buffer where we will store our message. */
    uint8_t buffer[128];
    size_t message_length;
    bool status;

    /* Encode our message */
    {
        /* Allocate space on the stack to store the message data, check out the contents of simple.pb.h
         * good to always initialize your structures so that no garbage data from RAM in there.   */

        //Device message = Device_init_zero;   //init zero   for empty
        Device message = Device_init_default;  //init default for default

        /* Create a stream that will write to our buffer. */
        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));

        /* Fill in the data */
        message.devid.arg = "device1";
        message.devid.funcs.encode = &encode_string;


        //strcpy(message.devid,"device1");  // no easier way like this ?

        message.isSecured = true;   // should be 1 if printed with %d
        message.uptime=9876;        // should change, why it is not working ?

        /* Now we are ready to encode the message! */
        // encode stream to buffer , also get the buffer length
        status = pb_encode(&stream, Device_fields, &message);
        message_length = stream.bytes_written;

        /* Then just check for any errors.. */
        if (!status)
        {
            printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
            return 1;
        }
    }

    /* Now we could transmit the message over network, store it in a file etc.   */
    /* just decode it immediately. */
    {
         /* Allocate space for the decoded message. */
        Device message = Device_init_zero;

        /* Create a stream that reads from the buffer. */
        pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);

        message.devid.funcs.decode = &print_string;
        message.devid.arg = "before decode Device - devid: %s \n"; //works here

        message.cpu.funcs.decode = &print_string;
        message.cpu.arg = "before decode Device -cpu: %s \n";   //where in op?

        printf("before decode isSecured %d\n",message.isSecured);  // doesn't work
        
        printf("before decode uptime %d\n",message.uptime); //doesn't work

        /* Now ready to decode the message. */
        // decode  stream buffer into message
        status = pb_decode(&stream, Device_fields, &message);

        /* Check for errors... */
        if (!status)
        {
            printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
            return 1;
        }

        /* Print the data contained in the message. */
        
        message.devid.funcs.decode = &print_string;
        message.devid.arg = "after decode Devic - devid: %s \n";  // doesn't work here

        printf(" after decode isSecured %d\n",message.isSecured);  // prints default 0        

    printf(" after decode uptime %d\n",(int)message.uptime);   //prints default assigned in proto
    }
  1. 构建并运行后的输出:
$ ./simple
before decode isSecured 0
before decode uptime 0
before decode Device - devid: device1
 after decode isSecured 0
 after decode uptime 1234

我的查询(还在代码中添加了我的内联注释):

  1. 在原始 simple.c message.lucky_number=13 分配有效,但这里 message.uptime 分配不起作用,它采用默认值。同样,将布尔值分配给 message.isSecured 也不起作用。请告诉我错在哪里。
  2. 我在 pb_encode 之前使用了 Device_init_default,因为有些具有默认值,而在 pb_decode 调用之前使用了 Device_init_zero,因为它将在解码后填充。我的方法正确吗?
  3. 除了 encode_string 和 decode_string util 之外,还有什么更简单的方法可以使用 strcpy 分配字符串值并通过 printf("%s",strvar) 以 C 方式打印它?
  4. 该字符串仅在 pb_decode 调用之前打印,但正常运行时间默认值在 pb_decode 调用之后打印。布尔值分配也不起作用。为什么 ?我的错误是什么?
  5. 我在https://github.com/nanopb/nanopb/blob/master/tests/callbacks/encode_callbacks.c中看到了编码字符串和 int 函数 如何编码和解码 float 和 boolean ?

感谢期待

标签: protocol-buffersnanopb

解决方案


  1. 在原始 simple.c message.lucky_number=13 分配有效,但这里 message.uptime 分配不起作用,它采用默认值。同样,将布尔值分配给 message.isSecured 也不起作用。请告诉我错在哪里。

如果您查看生成的.pb.h文件,您会发现每个可选字段都有一个 boolean has_field。您还必须将其设置为 true,以表示该字段存在。

  1. 我在 pb_encode 之前使用了 Device_init_default,因为有些具有默认值,而在 pb_decode 调用之前使用了 Device_init_zero,因为它将在解码后填充。我的方法正确吗?

没关系。

  1. 除了 encode_string 和 decode_string util 之外,还有什么更简单的方法可以使用 strcpy 分配字符串值并通过 printf("%s",strvar) 以 C 方式打印它?

因为您已经max_size为字符串字段设置了 a,所以它们应该作为char数组而不是回调生成。您可以尝试-v像这样传递开关:../generator/nanopb_generator.py -v simple.pb以查看更详细的消息,这些消息可以指出该选项不适用的原因。可能是文件名不正确或消息名不正确。

该字符串仅在 pb_decode 调用之前打印,但正常运行时间默认值在 pb_decode 调用之后打印。布尔值分配也不起作用。为什么 ?我的错误是什么?

我在https://github.com/nanopb/nanopb/blob/master/tests/callbacks/encode_callbacks.c中看到了编码字符串和 int 函数如何编码和解码 float 和 boolean ?

好吧,通常您不必求助于回调。但是,如果您决定需要它们,您可以使用 对布尔值进行编码,pb_encode_varint()并使用pb_encode_fixed32(). 对于研究回调,protobuf 编码文档这个测试用例可能会有所帮助。


您可能会发现该network_server示例对学习很有用。

此外,当您每个帖子只有一个问题时,Stack Overflow 格式效果最好。这样,问题和答案就保持集中且易于理解。


推荐阅读