protobuf在windows 的Qt和ubuntu的Qt上的使用(一)

一、 protobuf是什么

protobuf,Protocol Buffer (简称Protobuf) 是Google开源的性能优异、跨语言、跨平台的序列化库。

序列化(serialization、marshalling)的过程是指将数据结构或者对象的状态转换成可以存储(比如文件、内存)或者传输的格式(比如网络)。反向操作就是反序列化(deserialization、unmarshalling)的过程。

protobuf是一个很好的序列化工具, 因为其二进制序列化,确保了数据传输的保密性和占用带宽小的特性,并且数据结构前后兼容。用来保存数据和传输数据都是非常不错的选择。

目前Protobuf的稳定版本是3.9.2,于2019年9月23日发布。由于很多公司很早的就采用了Protobuf,所以很多项目还在使用proto2协议,目前是proto2和proto3同时在使用的状态。

Protobuf支持很多语言,比如C++、C#、Dart、Go、Java、Python、Rust等,同时也是跨平台的,所以得到了广泛的应用。

二、protobuf在windows10生成库

环境:系统:Win10 x64
编译器:Qt5.12.0 forWindows MinGW 64位版 和 cmake
protobuf版本:protobuf-cpp-3.8.0.zip

  1. protobuf开源地址:https://github.com/protocolbuffers/protobuf
  2. 找到想要的版本,下载protobuf源码:https://github.com/protocolbuffers/protobuf/tags
  3. 下载cmake安装:https://cmake.org/download/ 。我装的是 cmake-3.17.0-win64-x64.msi 。 安装成功后把cmake的安装路径添加到系统的环境变量中;
  4. 安装Qt,记得安装时选择MinGW 64位的编译器。安装成功后把Qt的安装路径添加到系统的环境变量中,如下:
    CMake和Qt编译器的环境变量设置.
    这时Cmake通过环境变量来找到Qt使用的编译器来打包protobuf库,在Qt中编译protobuf的编译器 和 我们自己编辑的代码使用同一个编译器来编译代码,可以避免因为编译器类型不同(VS和Qt使用不同的编译器)、同类型的编译器版本不同(32位版本和64位版本)等方面的问题。
  5. 使用cmake编译protobuf的.a库、根据proto文件编译生成对应的类文件的编译工具protoc.exe。
    打开cmake,点击Browse Source选取protobuf源码的路径,然后新建一个build文件夹,用来存放生成的库,并且通过Browse Build来添加到Where to build the binaries一项中。
    在这里插入图片描述
    接着点击Configure,在下拉选择框中选取MinGW Makefiles。
    在这里插入图片描述
    继续点击Configure后,可能会出现下面情况。通过查看错误信息发现是因为缺少某个模块而无法生成测试tests,这里可以将BUILD_TESTS一项取消来跳过生成tests。
    在这里插入图片描述
    重新点击Configure,会看到下方出现Configuring done,再点击Generate,会出现Generating done。而在build文件夹中会出现下面文件。这样就生成了makefile文件;
    在这里插入图片描述
    在这里插入图片描述
    在cmd中进入build文件夹,使用mingw32来进行编译。编译成功后会在build目录下生成libprotobuf.a、libprotobuf-lite.a、libprotoc.a、protoc.exe这四个文件。
    在这里插入图片描述
    至此已经成功编译完成protobuf的.a库,和根据proto文件编译生成对应的类文件的所需要的编译工具protoc.exe 。

三、定义proto文件

本次应用porobuf的目的是,android设备使用protobuf序列化项目算法所需要的原始数据,然后在Qt上使用C++语言反序列化android设备采集到的原始数据,得到的数据用于算法调优。我们定义的proto文件如下:

syntax = "proto3";
package cn.proto.java.model; //输出的包名
option java_outer_classname = "MsgObs";  //输出的类名
message ObsData {
  int32 n = 1;
  int32 nmax = 2;
  message GpsTime {
    int64 time = 1;
    double sec = 2 ;
  }
  message Data {
    GpsTime gtime = 1;
    int32 sat = 2;
    int32 rcv = 3;
  }
  repeated Data data = 3;
  repeated bytes corsObs = 4;
}

至于proto语法教程,参考其他资料。
由于我们需要序列化的数据,是多个message一起序列化,所以使用了一个message的长度(用4个字节表示)+ message序列化数据的格式保存到文件中。

四、使用protoc.exe工具生成proto文件对应的操作类

由于在使用protobuf时要先构建一个proto文件,然后生成对应的头文件和源文件,才能够使用。
在cmd打开的doc命令窗口,把当前目录切换到第二大步所生成的protoc.exe所在的目录,然后键入如下命令:protoc --cpp_out=. struct_obs.proto回车,生成对应的struct_obs.pb.h和struct_obs.pb.cc类文件。在生成的类中,每个proto文件的字段都有相对应的类成员变量对应,还有对应set/get类成员函数来操作这些成员变量。后面就可以创建这个类对象,来操作(读写)类成员变量了。
在这里插入图片描述

三、在windows Qt上使用protobuf库进行反序列化

创建Qt项目,首先在Qt的项目pro文件加入protobuf库(注意-lpthread要放在后面),还要把protobuf源码下的src文件夹拷到Qt项目下:
在这里插入图片描述
在这里插入图片描述
这样protobuf使用环境就配置好了,就可以开始写代码反序列化了:

void SolutionWithProtobufFile(string filenane, int ctx_id, FILE *fp)  {
    GOOGLE_PROTOBUF_VERIFY_VERSION;
    ObsData data;
    fstream input(filenane, ios::in | ios::binary);  // "../cui/message-2020_03_20_17_39_46.bin"
    if(!input.is_open())  {
        cout << "open bin file fail." << endl;
        return ;
    }
    input.seekg(0,ios::end);
    streampos sp=input.tellg(); //sp为定位指针,因为它在文件结束处,所以也就是文件的大小
    //cout<<"file size:"<<endl<<sp<<endl;
    input.seekg(0, std::ios::beg); // go back to the beginning
    char *buffer = new char[sp]; // allocate memory for a buffer of appropriate dimension
    input.read(buffer, sp); // read the whole file into the buffer
    //int readedBytes = input.gcount(); //看刚才读了多少字节
    //cout << "readedBytes " << readedBytes << endl;
    int pos = 0;  //表示长度+message开始的位置
    obs_t_f gnssobs;  //设备的观测数据
    obsd_t_f *pdata = nullptr;
    SourceData srcdata;
    char l[4] = {0};
    const unsigned char *p = nullptr;
    while(pos < sp) {
        for(int n=pos; n<pos+4; n++) {   //获取message的长度
            l[n-pos] = buffer[n];
        }
        size_t msg_len = bytes2Int(l);   //将byte数组转换成一个整数,表示一个message的长度
        char *msg = new char[msg_len];
        for(int i=pos+4; i<pos+4+msg_len; i++)   //截取一个完整的message
            msg[i-(pos+4)] = buffer[i];
        data.ParseFromArray(msg, msg_len);   //数组序列化解析,即一个message的解析

        gnssobs.n = data.n();
        gnssobs.nmax = data.nmax();
        pdata = new obsd_t_f[gnssobs.n];
        gnssobs.data = pdata;
        memset(pdata, 0, sizeof(obsd_t_f));
        for(int n=0; n < data.data_size(); n++)
        {
            ObsData_Data obsdata= data.data(n);
            ObsData_GpsTime time = obsdata.gtime();
            (pdata+n)->time.time = time.time();
            (pdata+n)->time.sec = time.sec();
            (pdata+n)->sat = static_cast<unsigned char>(obsdata.sat());
            (pdata+n)->rcv = static_cast<unsigned char>(obsdata.rcv());
        }
        for(int i=0; i<data.corsobs_size(); i++) {
            string strcorsobs = data.corsobs(i);
            setBaseData(srcdata, strcorsobs);
        }
        for(int i=0; i<data.corsnav_size(); i++) {
            string strcorsnav = data.corsnav(i);
            setNavData(srcdata, strcorsnav);
        }
        //---------读完整一包解算数据
        abstractmain->GetContext(ctx_id)->set_sourcedata("sourcedata", srcdata);  //把一包原始电文保存进context
        abstractmain->GetContext(ctx_id)->set_gnssobs(gnssobs.n, gnssobs.data);   //将观测数据保存进context
        p = abstractmain->SolutionFlow(ctx_id);  //解算
        LOGI(1,"%s\r\n", p);
        if (fp!=nullptr && p!=nullptr)
            fprintf(fp,"%s\r\n",p);

        pos = pos + 4 + msg_len;  //当前位置指向下一个message
        delete []msg;
    }
    delete []buffer;
    google::protobuf::ShutdownProtobufLibrary();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值