Skip to content

3.13 TopicPubSub Subscribe/Publish Class

Overview

TopicPubSub provides WebSocket-based real-time subscribe/publish capabilities. The recommended entry point is Arm::sub_pub :

  1. Call Arm::Connect() first.
  2. Then call arm.sub_pub.Connect() .
  3. Start receiving.
  4. Subscribe to status / register / IO topics.
cpp
Arm arm;
STATUS_CODE ret = arm.Connect("10.27.1.254");
if (ret != STATUS_CODE::OK) {
    return;
}
ret = arm.sub_pub.Connect();if (ret != STATUS_CODE::OK) {
    arm.Disconnect();
    return;
}
arm.sub_pub.StartReceiving(    [](const Json::Value& message) {
        // Parse message here; hand time-consuming work to an application thread.
    }
);
arm.topicPubSub.SubscribeStatus({RobotTopicType::JOINT_POSITION});

Scenario Examples

The snippets below cover connection and receiving, topic subscription, active send/receive, and connection cleanup. They assume the arm object has already completed Arm::Connect() .

Connect and Start Callback Receiving

cpp
STATUS_CODE connectRet = arm.sub_pub.Connect("", 10);
if (connectRet != STATUS_CODE::OK || !arm.sub_pub.IsConnected()) {
    return;
}
STATUS_CODE receiveRet = arm.sub_pub.StartReceiving(
    [](const Json::Value& message) {
        const Json::Value copy = message;
        (void)copy;
    }
);

Subscribe to Status, Register, and IO Topics

cpp
STATUS_CODE statusRet = arm.sub_pub.SubscribeStatus(
    {RobotTopicType::JOINT_POSITION},
    200
);
STATUS_CODE regRet = arm.sub_pub.SubscribeRegister(
    RegTopicType::R,
    {1, 2},
    100
);
STATUS_CODE ioRet = arm.sub_pub.SubscribeIo(
    {{IOTopicType::DI, 1}},
    50
);

Send Text and Read from the Queue

cpp
STATUS_CODE sendRet = arm.sub_pub.SendText("{\"cmd\":\"ping\"}");
auto [message, messageRet] = arm.sub_pub.Receive(1000);
(void)sendRet;
(void)message;
(void)messageRet;

Remove Callback and Disconnect

cpp
STATUS_CODE removeRet = arm.sub_pub.RemoveMessageHandler();
STATUS_CODE disconnectRet = arm.sub_pub.Disconnect();
(void)removeRet;
(void)disconnectRet;

Example code:

cpp17/sub_pub_basic/src/main.cpp
cpp
#include "multi_instance_isolation/run.h"
#include "subscribe_topics/run.h"
#include "send_receive_text/run.h"

int main(void)
{
    // [ZH] 默认只调用一个门面方法;如需体验其他接口,请把下一行替换成下面任意一行。
    // [EN] The main function calls only one facade by default. Replace the next line with any line below to try other APIs.
    return RunSubPubBasicSubscribeTopics();
    // return RunSubPubBasicSendReceiveText();
    // return RunSubPubBasicMultiInstanceIsolation();
}

Interface Overview

cpp
Connect(const std::string& teachPanelIp = "", int32_t timeoutSecs = 10) -> STATUS_CODE
IsConnected() const -> bool
StartReceiving(const MessageHandler& handler) -> STATUS_CODE
SubscribeStatus(const std::vector<ROBOT_TOPIC_TYPE>& topics, int32_t frequency = 200) -> STATUS_CODE
SubscribeRegister(const REG_TOPIC_TYPE& regType, const std::vector<int32_t>& regIds, int32_t frequency = 200) -> STATUS_CODE
SubscribeIo(const std::vector<std::pair<IO_TOPIC_TYPE, int32_t>>& ioList, int32_t frequency = 200) -> STATUS_CODE
SendText(const std::string& text) -> STATUS_CODE
RemoveMessageHandler() -> STATUS_CODE
Receive(int32_t timeoutMs = 5000) -> std::pair<Json::Value, STATUS_CODE>
Disconnect() -> STATUS_CODE
MethodInputOutputKey Behavior
ConnectOptional address, handshake timeout in secondsSTATUS_CODEWhen the address is empty, uses the TP/IP bound during Arm::Connect() first
IsConnectedNoneboolReflects only the sub_pub WebSocket connection state
StartReceivingOne JSON callbackSTATUS_CODEStarts background receiving; a later callback replaces the previous one
SubscribeStatusTopic list, frequencySTATUS_CODESends the addTopic command
SubscribeRegisterRegister type, ID list, frequencySTATUS_CODESends the addRegTopic command
SubscribeIoIO type + ID list, frequencySTATUS_CODESends the addIoTopic command
SendTextRaw textSTATUS_CODESends raw text
RemoveMessageHandlerNoneSTATUS_CODERemoves the active callback without affecting the SDK receive queue
ReceiveTimeout in millisecondsstd::pair<Json::Value, STATUS_CODE>Reads the next message from the SDK receive queue
DisconnectNoneSTATUS_CODECloses the current WebSocket connection

Detailed Semantics

Connect

Signature

cpp
STATUS_CODE Connect(const std::string& teachPanelIp = "", int32_t timeoutSecs = 10);
ItemDescription
teachPanelIpUses the explicitly passed address. When empty, uses the target address already bound by Arm::Connect() first
timeoutSecsWebSocket handshake timeout, default 10 seconds
ReturnOK / INVALID_IP_ADDRESS / other error codes from the connection phase

Constraints and Behavior

  • When using arm.sub_pub , usually call parameterless Connect() .
  • When instantiating TopicPubSub separately, an address must be passed explicitly.
  • Fixed proxy WebSocket port 5609 is used.
  • If the same TopicPubSub instance is already connected, calling again returns OK and reuses the current connection.
  • Multiple Arm / TopicPubSub instances can coexist concurrently in the same process. Disconnecting one instance does not affect the WebSocket network environment of other instances. Multi-instance isolation example:
cpp17/sub_pub_basic/src/multi_instance_isolation/run.cpp
cpp
#include <iostream>
#include <json/json.h>
#include <string>
#include <utility>

#include "arm_api.h"
#include "status_code.h"

#include "multi_instance_isolation/run.h"

namespace {

std::pair<Json::Value, STATUS_CODE> SendPingAndReceive(
    Arm& arm,
    const std::string& source
)
{
    Json::Value request;
    request["cmd"] = "ping";
    request["param"]["source"] = source;

    Json::StreamWriterBuilder builder;
    builder["indentation"] = "";
    STATUS_CODE sendRet = arm.topicPubSub.SendText(Json::writeString(builder, request));
    if (sendRet != STATUS_CODE::OK) {
        return std::make_pair(Json::Value(), sendRet);
    }

    return arm.topicPubSub.Receive(5000);
}

} // namespace

/**
 * 多实例 TopicPubSub 隔离门面。
 * @return 0 表示成功,否则返回 1。
 */
int RunSubPubBasicMultiInstanceIsolation(void)
{
    // [ZH] 请把下面地址替换成当前机器人控制器 IP 和示教器 IP。
    // [EN] Replace the following addresses with the current controller IP and teach panel IP.
    const std::string controllerIp = "10.27.1.2";
    const std::string teachPanelIp = "10.27.1.102";

    Arm armA;
    Arm armB;
    STATUS_CODE armConnectRetA = armA.Connect(controllerIp, teachPanelIp);
    STATUS_CODE armConnectRetB = armB.Connect(controllerIp, teachPanelIp);
    if (armConnectRetA != STATUS_CODE::OK || armConnectRetB != STATUS_CODE::OK) {
        std::cerr << "[cpp17_sub_pub] 多实例连接失败 / Multi-instance connect failed, A="
                  << static_cast<int>(armConnectRetA) << ", B="
                  << static_cast<int>(armConnectRetB) << "\n";
        armA.Disconnect();
        armB.Disconnect();
        return 1;
    }

    STATUS_CODE subPubConnectRetA = armA.topicPubSub.Connect(teachPanelIp, 10);
    STATUS_CODE subPubConnectRetB = armB.topicPubSub.Connect(teachPanelIp, 10);
    auto [ackA, ackRetA] = SendPingAndReceive(armA, "cpp17_multi_instance_A");
    STATUS_CODE disconnectRetA = armA.topicPubSub.Disconnect();
    auto [ackB, ackRetB] = SendPingAndReceive(
        armB,
        "cpp17_multi_instance_B_after_A_disconnect"
    );
    STATUS_CODE disconnectRetB = armB.topicPubSub.Disconnect();

    Json::StreamWriterBuilder builder;
    builder["indentation"] = "";
    std::cout << "[cpp17_sub_pub] ArmA Connect 状态码 / ArmA connect status code: "
              << static_cast<int>(subPubConnectRetA) << "\n";
    std::cout << "[cpp17_sub_pub] ArmB Connect 状态码 / ArmB connect status code: "
              << static_cast<int>(subPubConnectRetB) << "\n";
    std::cout << "[cpp17_sub_pub] ArmA Receive 状态码 / ArmA receive status code: "
              << static_cast<int>(ackRetA) << ", 消息 / Message: "
              << Json::writeString(builder, ackA) << "\n";
    std::cout << "[cpp17_sub_pub] ArmA Disconnect 状态码 / ArmA disconnect status code: "
              << static_cast<int>(disconnectRetA) << "\n";
    std::cout << "[cpp17_sub_pub] ArmB Receive 状态码 / ArmB receive status code: "
              << static_cast<int>(ackRetB) << ", 消息 / Message: "
              << Json::writeString(builder, ackB) << "\n";
    std::cout << "[cpp17_sub_pub] ArmB Disconnect 状态码 / ArmB disconnect status code: "
              << static_cast<int>(disconnectRetB) << "\n";

    armA.Disconnect();
    armB.Disconnect();
    std::cout << "[cpp17_sub_pub] 多实例隔离示例结束 / Multi-instance isolation example finished\n";

    const bool success =
        subPubConnectRetA == STATUS_CODE::OK &&
        subPubConnectRetB == STATUS_CODE::OK &&
        ackRetA == STATUS_CODE::OK &&
        disconnectRetA == STATUS_CODE::OK &&
        ackRetB == STATUS_CODE::OK &&
        disconnectRetB == STATUS_CODE::OK;
    return success ? 0 : 1;
}

IsConnected

Signature

cpp
bool IsConnected() const;

Constraints and Behavior

  • This checks the sub_pub WebSocket, not the Arm HTTP session.
  • After Arm::Connect() succeeds, arm.sub_pub.IsConnected() is still false ; it becomes true only after arm.sub_pub.Connect() succeeds.

StartReceiving

Signature

cpp
STATUS_CODE StartReceiving(const MessageHandler& handler);

Input

ParameterTypeDescription
handlerstd::function<void(const Json::Value&)>Message callback

Output

  • Success: returns OK
  • Not connected: returns NOT_CONNECTED

Constraints and Behavior

  • One TopicPubSub instance keeps only one active callback. A later call replaces the previous callback.
  • The callback receives an already parsed Json::Value .
  • The callback is triggered when a WebSocket message arrives.
  • The SDK receive queue limit is 100 messages. When the limit is exceeded, the oldest messages are discarded.
  • Keep callback logic lightweight. Move time-consuming work to an application thread.
  • Do not directly call Arm::Connect() , Arm::Disconnect() , SubPub::Connect() , SubPub::Disconnect() , StartReceiving() , or RemoveMessageHandler() from the callback. These reentrant operations may return OTHER_ERR or have no effect.

SubscribeStatus

Signature

cpp
STATUS_CODE SubscribeStatus(
    const std::vector<ROBOT_TOPIC_TYPE>& topics,
    int32_t frequency = 200
);
ItemDescription
topicsTopic list to subscribe to. See ROBOT_TOPIC_TYPE constants
frequencySubscription frequency in Hz, default 200
Subscription behaviorAdds robot status topics

SubscribeRegister

Signature

cpp
STATUS_CODE SubscribeRegister(
    const REG_TOPIC_TYPE& regType,
    const std::vector<int32_t>& regIds,
    int32_t frequency = 200
);
ItemDescription
regTypeRegister type. See REG_TOPIC_TYPE
regIdsRegister ID list
frequencySubscription frequency in Hz
Subscription behaviorAdds register topics

SubscribeIo

Signature

cpp
STATUS_CODE SubscribeIo(
    const std::vector<std::pair<IO_TOPIC_TYPE, int32_t>>& ioList,
    int32_t frequency = 200
);
ItemDescription
ioListEach item is (ioType, ioId)
frequencySubscription frequency in Hz
Subscription behaviorAdds IO topics

SendText

Signature

cpp
STATUS_CODE SendText(const std::string& text);

Sends raw text directly. If you only need status, register, or IO subscriptions, prefer the subscription APIs above.

RemoveMessageHandler

Signature

cpp
STATUS_CODE RemoveMessageHandler();

Constraints and Behavior

  • One TopicPubSub instance has only one active callback, so this removes the current callback.
  • After the callback is removed, Receive() can still read from the SDK receive queue.
  • Returns OK even if it is not currently connected.

Receive

Signature

cpp
std::pair<Json::Value, STATUS_CODE> Receive(int32_t timeoutMs = 5000);
ItemDescription
timeoutMsTimeout, default 5000ms
Success returnJson::Value + OK
Timeout returnEmpty Json::Value + SUB_PUB_RECEIVE_TIMEOUT
Disconnected returnEmpty Json::Value + NOT_CONNECTED

Disconnect

Signature

cpp
STATUS_CODE Disconnect();

Disconnects the current WebSocket connection. If it is not currently connected, returns OK .