4.13 C99 SubPub 订阅发布接口
概述
C99 SubPub 提供 WebSocket 订阅发布能力,包含连接、断开、发送原始文本、启动后台接收、同步接收、订阅状态 / 寄存器 / IO。
对应头文件:
include/c_arm_sub_pub.h
推荐调用顺序
| 步骤 | 接口 | 说明 |
|---|---|---|
| 1 | Arm_Connect | 建立机器人会话。 |
| 2 | Arm_SubPub_Connect | 建立 SubPub WebSocket 连接;固定使用代理端口 5609 。 |
| 3 | Arm_SubPub_StartReceiving | 启动后台接收并注册消息回调。 |
| 4 | Arm_SubPub_SubscribeStatus / Arm_SubPub_SubscribeRegister / Arm_SubPub_SubscribeIo | 发起状态、寄存器或 IO 订阅。 |
| 5 | Arm_SubPub_Receive | 按需从 SDK 接收队列同步取消息。 |
| 6 | Arm_SubPub_Disconnect | 断开 SubPub WebSocket 连接。 |
接口签名
Arm_SubPub_Connect
c
int Arm_SubPub_Connect(ArmHandle* h, const char* teachPanelIp, int timeoutSecs);| 项 | 说明 |
|---|---|
| 描述 | 建立 SubPub WebSocket 连接。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功teachPanelIp : const char* ,示教器地址;传 NULL 或空字符串时使用当前 Arm 会话绑定地址timeoutSecs : int ,连接超时时间,单位秒 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
| 备注 | teachPanelIp 为空时使用当前 Arm 会话绑定的地址;同一实例已连接时再次调用返回成功,不重建连接。 |
Arm_SubPub_Disconnect
c
int Arm_SubPub_Disconnect(ArmHandle* h);| 项 | 说明 |
|---|---|
| 描述 | 断开 SubPub WebSocket 连接。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
Arm_SubPub_IsConnected
c
int Arm_SubPub_IsConnected(ArmHandle* h);| 项 | 说明 |
|---|---|
| 描述 | 查询 SubPub 是否连接。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功 |
| 返回值 | 已连接返回非 0 ,未连接返回 0 |
| 备注 | 只表示 SubPub WebSocket 状态,不表示 Arm HTTP 会话状态。 |
Arm_SubPub_SendText
c
int Arm_SubPub_SendText(ArmHandle* h, const char* text);| 项 | 说明 |
|---|---|
| 描述 | 发送原始文本消息。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功text : const char* ,待发送的原始文本 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
Arm_SubPub_StartReceiving
c
int Arm_SubPub_StartReceiving(ArmHandle* h, ArmSubPubMessageCallback callback);| 项 | 说明 |
|---|---|
| 描述 | 启动后台消息接收。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功callback : ArmSubPubMessageCallback ,消息回调函数,收到 WebSocket 文本时调用 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
| 备注 | 同一 SubPub 实例只保留一个活动回调,后一次调用会覆盖前一次。 |
Arm_SubPub_RemoveMessageHandler
c
int Arm_SubPub_RemoveMessageHandler(ArmHandle* h);| 项 | 说明 |
|---|---|
| 描述 | 移除消息回调。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
| 备注 | 移除当前活动回调,不清空 SDK 接收队列;未连接时调用也按成功处理。 |
Arm_SubPub_Receive
c
int Arm_SubPub_Receive(ArmHandle* h, int timeoutMs, char* outBuf, size_t bufSize, size_t* outLen);| 项 | 说明 |
|---|---|
| 描述 | 同步接收一条消息。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功timeoutMs : int ,接收超时时间,单位毫秒outBuf : char* ,输出字符串缓冲区,由调用方分配bufSize : size_t ,输出缓冲区大小,包含结尾 \0 的空间outLen : size_t* ,输出消息所需总字节数,包含结尾 \0 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
| 备注 | 超时返回接收超时状态码;未连接返回未连接状态码;缓冲区不足时 outLen 写入所需字节数。 |
Arm_SubPub_SubscribeStatus
c
int Arm_SubPub_SubscribeStatus(ArmHandle* h, const int* topics, size_t count, int frequency);| 项 | 说明 |
|---|---|
| 描述 | 订阅机器人状态主题。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功topics : const int* ,状态主题数组count : size_t ,数组元素数量frequency : int ,订阅频率参数 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
Arm_SubPub_SubscribeRegister
c
int Arm_SubPub_SubscribeRegister(ArmHandle* h, int regType, const int* regIds, size_t count, int frequency);| 项 | 说明 |
|---|---|
| 描述 | 订阅寄存器主题。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功regType : int ,寄存器主题类型regIds : const int* ,寄存器编号数组count : size_t ,数组元素数量frequency : int ,订阅频率参数 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
Arm_SubPub_SubscribeIo
c
int Arm_SubPub_SubscribeIo(ArmHandle* h, const int* ioTypes, const int* ioIds, size_t count, int frequency);| 项 | 说明 |
|---|---|
| 描述 | 订阅 IO 主题。 |
| 请求参数 | h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功ioTypes : const int* ,IO 主题类型数组ioIds : const int* ,IO 编号数组count : size_t ,数组元素数量frequency : int ,订阅频率参数 |
| 返回值 | STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理 |
回调和 topic
c
typedef void (*ArmSubPubMessageCallback)(const char* jsonStr, size_t len);| 类型 | 说明 |
|---|---|
ArmRobotTopicType | 机器人状态主题,例如 ARM_ROBOT_TOPIC_JOINT_POSITION 、 ARM_ROBOT_TOPIC_SERVO_STATUS |
ArmRegTopicType | 寄存器主题,包含 ARM_REG_TOPIC_R 、 ARM_REG_TOPIC_MR 、 ARM_REG_TOPIC_SR 、 ARM_REG_TOPIC_PR |
ArmIoTopicType | IO 主题,包含 ARM_IO_TOPIC_DI 、 ARM_IO_TOPIC_DO 、 ARM_IO_TOPIC_AI 、 ARM_IO_TOPIC_AO 等 |
| 项 | 规则 |
|---|---|
jsonStr | UTF-8 JSON 文本 |
len | 文本长度,不含结尾 \0 |
| 回调线程 | WebSocket 消息到达线程 |
Receive | outLen 回写所需总字节数,包含结尾 \0 |
| 缓冲区不足 | Arm_SubPub_Receive 返回 BUFFER_TOO_SMALL |
| 接收队列 | SDK 接收队列上限为 100 条,超过上限时丢弃最旧消息 |
| 回调覆盖 | 同一实例后注册的回调会覆盖前一次回调 |
| 回调重入 | 回调中避免直接调用连接、断开、启动接收或移除回调类接口 |
回调里只做轻量处理,耗时逻辑转交业务线程。
连接与订阅规则
| 项 | 说明 |
|---|---|
| WebSocket 端口 | SubPub 固定使用代理端口 5609 。 |
| 空地址连接 | teachPanelIp 为 NULL 或空字符串时,使用 Arm_Connect() 绑定的地址。 |
| 多实例 | 多个 ArmHandle* 可分别建立 SubPub 连接;断开其中一个时,其他实例保持各自连接状态。 |
| 订阅频率 | frequency 单位为 Hz;状态、寄存器、IO 订阅都使用该频率字段。 |
| 原始文本 | Arm_SubPub_SendText 用于调试或自定义协议;常规状态、寄存器、IO 数据优先使用订阅接口。 |
最小调用示例
c
#include <stdio.h> // 引入 printf,用于打印 SubPub 连接状态
#include "c_arm_api.h" // 引入 C99 SDK 总头文件
int main(void) // 示例程序入口
{ // 进入示例主函数
ArmHandle* h = Arm_Create(); // 创建 C99 会话句柄
if (h == NULL) { // 判断句柄是否创建失败
return 1; // 创建失败时退出
} // 结束句柄判断
if (Arm_Connect(h, "10.27.1.2", "10.27.1.102") != 0) { // 连接控制器
Arm_Destroy(h); // 连接失败时释放句柄
return 1; // 返回错误
} // 结束连接判断
int ret = Arm_SubPub_Connect(h, "10.27.1.102", 10); // 建立 SubPub 连接
printf("sub_pub_connected=%d\n", Arm_SubPub_IsConnected(h)); // 打印 SubPub 连接状态
Arm_SubPub_Disconnect(h); // 断开 SubPub 连接
Arm_Disconnect(h); // 断开机器人连接
Arm_Destroy(h); // 销毁句柄
return ret == 0 ? 0 : 1; // 根据连接结果返回
} // 结束示例主函数场景化示例
c
void OnMessage(const char* jsonStr, size_t len) // 定义消息回调
{ // 进入消息回调
(void)jsonStr; // 示例中保留 JSON 文本
(void)len; // 示例中保留文本长度
} // 结束消息回调
int topics[1] = {ARM_ROBOT_TOPIC_JOINT_POSITION}; // 准备机器人状态主题列表
int regIds[2] = {1, 2}; // 准备寄存器编号列表
int ioTypes[1] = {ARM_IO_TOPIC_DI}; // 准备 IO 类型列表
int ioIds[1] = {1}; // 准备 IO 编号列表
char msg[2048] = {0}; // 准备同步接收缓冲区
size_t msgLen = 0U; // 准备接收消息长度
int connectRet = Arm_SubPub_Connect(h, NULL, 10); // 建立 SubPub 连接
int receiveRet = Arm_SubPub_StartReceiving(h, OnMessage); // 启动后台接收
int statusRet = Arm_SubPub_SubscribeStatus(h, topics, 1, 200); // 订阅关节位置主题
int regRet = Arm_SubPub_SubscribeRegister(h, ARM_REG_TOPIC_R, regIds, 2, 100); // 订阅 R1 / R2
int ioRet = Arm_SubPub_SubscribeIo(h, ioTypes, ioIds, 1, 50); // 订阅 DI1
int textRet = Arm_SubPub_SendText(h, "{\"cmd\":\"ping\"}"); // 发送原始文本
int syncRet = Arm_SubPub_Receive(h, 1000, msg, sizeof(msg), &msgLen); // 同步接收一条消息
int removeRet = Arm_SubPub_RemoveMessageHandler(h); // 移除消息回调
int disconnectRet = Arm_SubPub_Disconnect(h); // 断开 SubPub 连接
(void)connectRet; // 示例中保留状态码
(void)receiveRet; // 示例中保留状态码
(void)statusRet; // 示例中保留状态码
(void)regRet; // 示例中保留状态码
(void)ioRet; // 示例中保留状态码
(void)textRet; // 示例中保留状态码
(void)syncRet; // 示例中保留状态码
(void)removeRet; // 示例中保留状态码
(void)disconnectRet; // 示例中保留状态码示例代码
cpp
#include <stdio.h>
extern "C" {
#include "c_arm_api.h"
}
int main(void)
{
// [ZH] 本示例直接在源码中写死连接地址,不解析命令行参数。
// [EN] This example hard-codes the connection addresses in the source code and does not parse command-line arguments.
// [ZH] 创建并连接 SDK 句柄。
// [EN] Create the SDK handle and connect to the robot.
ArmHandle* handle = Arm_Create();
if (handle == NULL) {
printf("[c99_sub_pub] 创建句柄失败 / Failed to create the handle\n");
return 1;
}
int ret = Arm_Connect(handle, "10.27.1.2", "10.27.1.102");
if (ret != 0) {
printf("[c99_sub_pub] 连接失败 / Connect failed, 状态码 / Status code: %d\n", ret);
Arm_Destroy(handle);
return 1;
}
printf("[c99_sub_pub] 机器人连接成功 / Robot connected successfully\n");
// [ZH] 建立订阅连接并打印连接状态。
// [EN] Establish the subscription connection and print the connection state.
ret = Arm_SubPub_Connect(handle, "10.27.1.102", 10);
printf("[c99_sub_pub] Connect 状态码 / Connect status code: %d\n", ret);
printf("[c99_sub_pub] 当前连接状态 / Current connection state: %d\n", Arm_SubPub_IsConnected(handle));
// [ZH] 顺序执行回调注册、主题订阅、发送和接收接口。
// [EN] Execute the callback registration, subscriptions, send, and receive APIs in sequence.
ret = Arm_SubPub_StartReceiving(handle, NULL);
printf("[c99_sub_pub] StartReceiving 状态码 / StartReceiving status code: %d\n", ret);
int statusTopics[1] = {ARM_ROBOT_TOPIC_JOINT_POSITION};
ret = Arm_SubPub_SubscribeStatus(handle, statusTopics, 1U, 200);
printf("[c99_sub_pub] SubscribeStatus 状态码 / SubscribeStatus status code: %d\n", ret);
int regIds[2] = {1, 2};
ret = Arm_SubPub_SubscribeRegister(handle, ARM_REG_TOPIC_R, regIds, 2U, 200);
printf("[c99_sub_pub] SubscribeRegister 状态码 / SubscribeRegister status code: %d\n", ret);
int ioTypes[1] = {ARM_IO_TOPIC_DI};
int ioIds[1] = {1};
ret = Arm_SubPub_SubscribeIo(handle, ioTypes, ioIds, 1U, 200);
printf("[c99_sub_pub] SubscribeIo 状态码 / SubscribeIo status code: %d\n", ret);
ret = Arm_SubPub_SendText(handle, "{\"cmd\":\"ping\",\"param\":{\"source\":\"c99_example\"}}");
printf("[c99_sub_pub] SendText 状态码 / SendText status code: %d\n", ret);
char message[2048] = {0};
size_t messageLen = 0U;
ret = Arm_SubPub_Receive(handle, 5000, message, sizeof(message), &messageLen);
printf("[c99_sub_pub] Receive 状态码 / Receive status code: %d, 长度 / Length: %zu, 消息 / Message: %s\n", ret, messageLen, message);
ret = Arm_SubPub_RemoveMessageHandler(handle);
printf("[c99_sub_pub] RemoveMessageHandler 状态码 / RemoveMessageHandler status code: %d\n", ret);
ret = Arm_SubPub_Disconnect(handle);
printf("[c99_sub_pub] Disconnect 状态码 / Disconnect status code: %d\n", ret);
// [ZH] 断开机器人连接并销毁句柄。
// [EN] Disconnect the robot and destroy the handle.
Arm_Disconnect(handle);
Arm_Destroy(handle);
printf("[c99_sub_pub] 示例结束 / Example finished\n");
return 0;
}