Client & Examples
The C++ client library lives in the lixel::openapi namespace, with public headers in cpp/include/lixel/openapi/. This page covers what it offers and how to use it.
Headers at a glance
| Header | What it gives you |
|---|---|
client.h | Client: Connect, Request, Publish, Subscribe, SubscribeMessage, RemoveSubscription |
scan.h | RunScan: start a scan, wait until the device is really scanning, stop, wait for standby |
pointcloud.h | DecodePointCloud: decode the lio_pcl payload into scaled points |
codec.h | message by name, protobuf to and from JSON |
catalog.h | the table of 18 interfaces and their 34 topics |
codes.h, result.h | device status codes, Result and Errc |
The Client
Connect
lixel::openapi::ClientOptions options;
options.transport.host = "192.168.55.1";
options.openapi_key = "<provided by XGRIDS>";
lixel::openapi::Client client(options);
lixel::openapi::Result r = client.Connect(); // blocks until connected
A failed Connect() returns Errc::ConnectFailed or Errc::ConnectTimeout, and the client can be used for another Connect() afterwards.
Request-response
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);
Return codes of Request:
| Code | Meaning |
|---|---|
Ok | A response arrived and its Error.code is a success code |
DeviceError | A response arrived but with an error code (see Result::device_code, desc); response is filled |
RequestTimeout | No response with the same seq within the timeout. The device may still have carried out the request; confirm with it before assuming otherwise |
NotConnected | Not connected, or the connection dropped while waiting |
CalledFromCallback | Request was called from a subscription callback |
Subscribe to reports
client.SubscribeMessage(
"lixel/application/report/device_status", "DeviceStatusReport",
[](const std::string& topic, const google::protobuf::Message& msg) {
// msg is already parsed into the matching protobuf message
});
Note: subscription callbacks run on the MQTT layer's thread, one message at a time; keep them short. A callback must not call
ConnectorDisconnect; callingRequestreturnsErrc::CalledFromCallbackinstead of blocking. The strings passed to a callback are valid only for the duration of the call; copy what you need.
Point cloud decoding
The real-time point cloud (report/lio_pcl, MqttCompressMsg) is decoded with DecodePointCloud, which decompresses and deserializes in one step:
#include "lixel/openapi/pointcloud.h"
lixel::openapi::PointCloud cloud;
lixel::openapi::Result r = lixel::openapi::DecodePointCloud(payload, &cloud);
// On success, each point's x/y/z in cloud.points is reconstructed by header.scaler (the device sends scaler as 1000).
The compression is LZ4 block (not frame). A Zstd-compressed point cloud and a negative scaler return Errc::Unsupported. A point's rgbi is passed through unchanged (the proto only notes R+G+B+Intensity, with no further byte-order convention).
Scan flow
Starting and stopping a scan are asynchronous on the device: the response to AC_START only means the request was accepted, and the device then goes standby → starting → scanning. RunScan wraps the whole flow — subscribe to the device status, wait for standby, start, wait until really scanning, watch for a while, stop, and wait for standby:
#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 success, others see ScanOutcome
RunScan blocks the calling thread, requires a connected client, and must not be called from a subscription callback. The ScanOutcome result: Completed (0), Timeout (1), NotStandby (2), StartRejected (3), NoResponse (4), Error (5), Interrupted (130).
The 18 command line examples
cpp/examples/ has one C++ program for each script in python/example/usage/, with the same name and options, built with the library (on by default) into cpp/build/examples/:
./cpp/build/examples/heartbeat --seconds 5 # no key needed
./cpp/build/examples/device_info --key <provided by XGRIDS>
./cpp/build/examples/modeling --key <provided by XGRIDS> --project demo --watch 8
./cpp/build/examples/lio_pcl --key <provided by XGRIDS> --seconds 20 # while the device scans
Common options: --host (default 192.168.55.1), --port, --tls, --username, --password, --key, --timeout; for TLS also --ca-file and --insecure. Exit codes: 0 success, 1 could not connect or no answer, 2 bad option (modeling: the device was not in standby), 3 the device answered with an error code, 130 interrupted.
The 18 examples: 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.
Note: among them,
device_control,device_config,set_ntrip_profile,set_rtk_advance,control_point, andmeasure_pointchange the device, anddevice_controlcan power it off or format the disk — confirm before use.
Differences from the Python client
The C++ client intentionally differs in a few places:
| Aspect | C++ | Python |
|---|---|---|
| Response matching | By header.seq, which the device copies back | The first message on the response topic |
| JSON conversion | Strict: a misspelled field or enum name is an error | Dropped silently |
| MQTTS certificate | Verifies the server certificate by default; the self-signed device certificate needs --ca-file, or --insecure to turn verification off | Never verifies |
Calling Request in a callback | Returns Errc::CalledFromCallback | Blocks |
Other limits:
- Several
Clientobjects in one process share one MQTT worker thread; opening a second connection takes about 2 seconds. Each example uses oneClientand is not affected. - A
Requestthat is waiting cannot be cancelled; it ends withinrequest_timeout.
Next steps
- For the build, dependencies, and integration, see Quick Start.
- For the protocol itself, see the shared chapters Interface Catalog, Real-time Data, and Error Codes.