客户端与示例
C++ 客户端库位于命名空间 lixel::openapi,公共头文件在 cpp/include/lixel/openapi/。本篇介绍它提供的能力与用法。
头文件一览
| 头文件 | 提供什么 |
|---|---|
client.h | Client:Connect、Request、Publish、Subscribe、SubscribeMessage、RemoveSubscription |
scan.h | RunScan:开始扫描、等设备真正进入扫描、停止、等回到待机 |
pointcloud.h | DecodePointCloud:把 lio_pcl 负载解码为带缩放的点 |
codec.h | 按名字取消息,protobuf 与 JSON 互转 |
catalog.h | 18 个接口、34 个主题的表 |
codes.h、result.h | 设备状态码,Result 与 Errc |
客户端 Client
连接
lixel::openapi::ClientOptions options;
options.transport.host = "192.168.55.1";
options.openapi_key = "<由 XGRIDS 提供>";
lixel::openapi::Client client(options);
lixel::openapi::Result r = client.Connect(); // 阻塞到连接完成
Connect() 失败返回 Errc::ConnectFailed 或 Errc::ConnectTimeout,之后客户端仍可再次 Connect()。
请求-响应
openapi::aiot_msgs::DeviceInfoRequest req;
openapi::aiot_msgs::DeviceInfoResponse rsp;
lixel::openapi::Result r = client.Request(
"lixel/application/request/device_info", req,
"lixel/application/response/device_info", &rsp);
Request 的返回码:
| 返回码 | 含义 |
|---|---|
Ok | 收到响应,且 Error.code 是成功码 |
DeviceError | 收到响应,但设备返回错误码(见 Result::device_code、desc),response 已填充 |
RequestTimeout | 超时内没有收到同 seq 的响应。设备可能仍执行了请求,判断前先向设备确认 |
NotConnected | 未连接,或等待期间连接断开 |
CalledFromCallback | 在订阅回调里调用了 Request |
订阅上报
client.SubscribeMessage(
"lixel/application/report/device_status", "DeviceStatusReport",
[](const std::string& topic, const google::protobuf::Message& msg) {
// msg 已解析为对应 protobuf 消息
});
注:订阅回调运行在 MQTT 层的线程上,一次处理一条消息,要尽量简短。回调里不能调用
Connect、Disconnect;调用Request会返回Errc::CalledFromCallback而不是阻塞。传给回调的字符串仅在调用期间有效,需要留用的要自行拷贝。
点云解码
实时点云(report/lio_pcl,MqttCompressMsg)用 DecodePointCloud 解码,一步完成解压与反序列化:
#include "lixel/openapi/pointcloud.h"
lixel::openapi::PointCloud cloud;
lixel::openapi::Result r = lixel::openapi::DecodePointCloud(payload, &cloud);
// 成功后 cloud.points 里每个点的 x/y/z 已按 header.scaler 还原(设备发送 scaler 为 1000)。
压缩格式是 LZ4 block(不是 frame)。Zstd 压缩的点云与负的 scaler 返回 Errc::Unsupported。点的 rgbi 按设备原样传出(proto 只注明 R+G+B+Intensity,字节序未进一步约定)。
扫描流程
扫描的开始和停止在设备端是异步的:AC_START 的响应只表示请求被接受,设备随后经待机 → 开始扫描 → 扫描中。RunScan 封装了完整流程——订阅设备状态、等待待机、启动、等真正进入扫描、观察一段时间、停止、等回到待机:
#include "lixel/openapi/scan.h"
lixel::openapi::ScanOptions options;
options.project_name = "demo";
options.watch = std::chrono::seconds(8);
lixel::openapi::ScanReport report = lixel::openapi::RunScan(client, options);
return report.exit_code(); // 0 成功,其余见 ScanOutcome
RunScan 会阻塞调用线程,要求客户端已连接,且不能在订阅回调里调用。结果 ScanOutcome:Completed(0)、Timeout(1)、NotStandby(2)、StartRejected(3)、NoResponse(4)、Error(5)、Interrupted(130)。
18 个命令行示例
cpp/examples/ 下每个 python/example/usage/ 脚本都有一个同名、同选项的 C++ 程序,随库一起构建(默认开),产物在 cpp/build/examples/:
./cpp/build/examples/heartbeat --seconds 5 # 不需要 Key
./cpp/build/examples/device_info --key <由 XGRIDS 提供>
./cpp/build/examples/modeling --key <由 XGRIDS 提供> --project demo --watch 8
./cpp/build/examples/lio_pcl --key <由 XGRIDS 提供> --seconds 20 # 设备扫描期间
通用选项:--host(默认 192.168.55.1)、--port、--tls、--username、--password、--key、--timeout;TLS 另有 --ca-file 和 --insecure。退出码:0 成功,1 连不上或没有响应,2 选项错误(modeling:设备不在待机),3 设备返回错误码,130 被中断。
18 个示例:control_point、data_unpack、device_config、device_control、device_info、device_status、get_ntrip_profile、get_rtk_advance、heartbeat、lio_pcl、lio_pose、measure_point、modeling、modeling_status、project_list、set_ntrip_profile、set_rtk_advance、system_error。
注:其中
device_control、device_config、set_ntrip_profile、set_rtk_advance、control_point、measure_point会改动设备,device_control可能关机或格式化磁盘,使用前请确认。
与 Python 客户端的区别
C++ 客户端有意在几处与 Python 不同:
| 方面 | C++ | Python |
|---|---|---|
| 响应匹配 | 按 header.seq 匹配请求(设备原样带回) | 取响应主题上的第一条消息 |
| JSON 转换 | 严格:字段名或枚举名写错会报错 | 静默丢弃 |
| MQTTS 证书 | 默认校验服务端证书;设备自签名证书需传 --ca-file 或用 --insecure 关闭 | 从不校验 |
回调里调用 Request | 返回 Errc::CalledFromCallback | 阻塞 |
其他使用限制:
- 同一进程多个
Client共用一个 MQTT 工作线程,建立第二个连接约需 2 秒。每个示例只用一个Client,不受影响。 - 正在等待的
Request无法取消,最迟在request_timeout到时结束。