Client Wrapper & Tools
The project provides a reusable client wrapper, a graphical control panel, and example scripts, covering everything from "a few lines of code to call an interface" to "manual debugging in a GUI".
Client wrapper
The wrapper does not depend on any GUI library and is the recommended entry point for secondary development. Modules:
| Module | Description |
|---|---|
api_catalog.py | Single source of truth for the interface catalog (ApiInterface / Endpoint / Direction) |
proto_codec.py | ProtoCodec: JSON ↔ Protobuf codec, default template |
openapi_client.py | OpenApiClient / ClientOptions: MQTT connection, request-response, subscription |
ClientOptions
from example.core import ClientOptions
opts = ClientOptions(
host="192.168.55.1",
port=1883,
use_tls=False, # set True for MQTTS
username="",
password="",
openapi_key="", # written into header.openapi_key automatically
device_id="",
client_id="", # MQTT client ID, auto-generated when empty
connect_timeout=10.0,
request_timeout=10.0,
)
Request-response
from example.core import OpenApiClient, ClientOptions
with OpenApiClient(ClientOptions(host="192.168.55.1", openapi_key="<provided by XGRIDS>")) as client:
rsp = client.request(
req_topic="lixel/application/request/device_info",
req_message="DeviceInfoRequest",
payload={}, # dict or JSON string
rsp_topic="lixel/application/response/device_info",
rsp_message="DeviceInfoResponse",
)
print(rsp) # decoded dict
Inside, request() subscribes to the response topic, publishes the request, blocks for the first response, and decodes the return; raises TimeoutError on timeout.
Subscribe to reports
import time
from example.core import ClientOptions, OpenApiClient
def on_status(topic, data): # data is already decoded into a dict
print(topic, data)
with OpenApiClient(ClientOptions(host="192.168.55.1", openapi_key="<provided by XGRIDS>")) as client:
client.subscribe(
"lixel/application/report/device_status",
"DeviceStatusReport",
on_status,
)
time.sleep(5) # keep the connection alive to receive reports
For JSON payloads (heartbeat), pass an empty string for message and the callback receives a string. An exception raised in the callback is logged (Python prints to the terminal by default) and does not interrupt other subscriptions.
API overview
| Method | Description |
|---|---|
connect() / disconnect() | Connect / disconnect (supports the with context) |
is_connected | Connection status |
publish(topic, message, payload) | Publish a message |
subscribe(topic, message, callback) | Subscribe and register a callback |
subscribe_raw(topic_filter, callback) | Subscribe by wildcard; the callback receives raw bytes |
unsubscribe(topic) | Unsubscribe |
request(req_topic, req_message, payload, rsp_topic, rsp_message, timeout=None) | Synchronous request-response call |
codec | Built-in ProtoCodec |
Graphical control panel
The control panel shows all interfaces in tabs grouped by function, one card per interface, where you can edit a JSON payload to send a request or subscribe to reports to view the output.
python python/example/main.py # English UI by default
python python/example/main.py --lang zh # start in Chinese (also switchable live in the menu)
UI structure:
- Menu bar: Settings → Language (switch between English/Chinese live, no restart); About (shows the program version, injected at build time,
V0.0.0when run from source). - Top connection bar: Host / Port / Protocol (MQTT·MQTTS) / Username / Password / OpenAPI Key / Connect. Default host
192.168.55.1; Username and Password are shown only when MQTTS is selected and require the vendor-provided credentials; hidden and cleared when switching back to MQTT. - Left "Publish" panel: Topic dropdown (prefilled with all publishable request topics), message type dropdown, Load template, interface description, JSON editor, Publish button.
- Right "Subscribe" panel: Topic dropdown, Subscribe / Subscribe all, wildcard subscription (default
lixel/#), current subscription list, received messages list (double-click to see the full decoded content).
Note: publishing only sends a request and does not block the UI; responses and reports are all viewed in the "Subscribe" panel on the right. Before publishing a request, subscribe to the corresponding response topic first (or use the wildcard
lixel/#).
Example scripts
python/example/usage/ provides a standalone, runnable script for each interface — the most direct reference for secondary development, all based on the client wrapper and GUI-free:
cd python/example/usage
python device_info.py --host 192.168.55.1 --port 1883 --key <provided by XGRIDS>
python modeling.py --host 192.168.55.1 --key <provided by XGRIDS> --project demo --watch 5 # device must be in standby; a full run takes a minute or more
python heartbeat.py --host 192.168.55.1 --seconds 15 # the heartbeat needs no key
Common arguments: --host / --port / --tls / --username / --password / --key / --timeout. The device responds only to requests with a valid OpenAPI Key, so pass --key for everything except subscriptions like the heartbeat; without --host it defaults to the wired address 192.168.55.1 (the 1.0.0 default was 192.168.12.1 — use 1.0.1 or newer, or pass --host explicitly).
modeling.py first confirms the device is in standby, starts the scan, waits until it is really scanning, watches for --watch seconds, then stops the scan and waits for standby; if the device is not in standby it exits with code 2. Exit codes: 0 success; 1 start or stop wait timeout; 2 device not in standby; 3 the AC_START response code is not success (e.g. a wrong key); 4 no response to AC_START or AC_STOP; 130 interrupted with Ctrl-C. Once AC_START is sent, AC_STOP is always sent afterwards, except when AC_START gets no response (exit code 4), in which case the device state is unknown and you must check it yourself. The wait times are adjustable with --start-timeout (default 90 s) and --stop-timeout (default 180 s). For the full list of scripts, see python/example/usage/README.md in the repository.
Note:
lio_pcl.pydemonstrates point cloud decompression and needspip install lz4 zstandard; without them the script prints a hint and skips point cloud decoding.
Localization
All user-visible text in the control panel supports multiple languages. The UI is English by default and can be switched live from the "Settings → Language" menu, or specified at startup with --lang <code>. To add a language, copy and translate the translation file, compile it, and register it in the source constant LANGUAGES (python/example/gui/control_panel.py); it then appears in the menu.