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 , 연결 timeout 시간, 단위는 초 |
| 반환값 | 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 , 수신 timeout 시간, 단위는 밀리초outBuf : char* , 호출자가 할당하는 출력 문자열 버퍼bufSize : size_t , 끝의 \0 공간을 포함한 출력 버퍼 크기outLen : size_t* , 출력 메시지에 필요한 총 바이트 수. 끝의 \0 을 포함합니다. |
| 반환값 | STATUS_CODE 정수값. 0 은 성공을 의미하며, 그 외 값은 상태 코드로 처리합니다. |
| 비고 | timeout 시 수신 timeout 상태 코드를 반환합니다. 미연결 상태에서는 미연결 상태 코드를 반환합니다. 버퍼가 부족하면 outLen 에 필요한 바이트 수를 씁니다. |
Arm_SubPub_SubscribeStatus
c
int Arm_SubPub_SubscribeStatus(ArmHandle* h, const int* topics, size_t count, int frequency);| 항목 | 설명 |
|---|---|
| 설명 | 로봇 상태 topic을 구독합니다. |
| 요청 파라미터 | h : ArmHandle* , C99 세션 핸들. 일반적으로 Arm_Create() 에서 얻으며, 업무 인터페이스 호출 전 연결이 성공해야 합니다.topics : const int* , 상태 topic 배열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);| 항목 | 설명 |
|---|---|
| 설명 | 레지스터 topic을 구독합니다. |
| 요청 파라미터 | h : ArmHandle* , C99 세션 핸들. 일반적으로 Arm_Create() 에서 얻으며, 업무 인터페이스 호출 전 연결이 성공해야 합니다.regType : int , 레지스터 topic 타입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 topic을 구독합니다. |
| 요청 파라미터 | h : ArmHandle* , C99 세션 핸들. 일반적으로 Arm_Create() 에서 얻으며, 업무 인터페이스 호출 전 연결이 성공해야 합니다.ioTypes : const int* , IO topic 타입 배열ioIds : const int* , IO 번호 배열count : size_t , 배열 요소 수frequency : int , 구독 주파수 파라미터 |
| 반환값 | STATUS_CODE 정수값. 0 은 성공을 의미하며, 그 외 값은 상태 코드로 처리합니다. |
콜백 및 topic
c
typedef void (*ArmSubPubMessageCallback)(const char* jsonStr, size_t len);| 타입 | 설명 |
|---|---|
ArmRobotTopicType | 로봇 상태 topic. 예: ARM_ROBOT_TOPIC_JOINT_POSITION , ARM_ROBOT_TOPIC_SERVO_STATUS |
ArmRegTopicType | 레지스터 topic. ARM_REG_TOPIC_R , ARM_REG_TOPIC_MR , ARM_REG_TOPIC_SR , ARM_REG_TOPIC_PR 등을 포함합니다. |
ArmIoTopicType | IO topic. 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> // SubPub 연결 상태를 출력하기 위한 printf
#include "c_arm_api.h" // C99 SDK 최상위 헤더
int main(void) // 예제 프로그램 진입점
{ // 예제 main 함수 시작
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; // 연결 결과에 따라 반환
} // 예제 main 함수 종료시나리오 예제
c
void OnMessage(const char* jsonStr, size_t len) // 메시지 콜백 정의
{ // 메시지 콜백 시작
(void)jsonStr; // 예제에서 JSON 텍스트 보관
(void)len; // 예제에서 텍스트 길이 보관
} // 메시지 콜백 종료
int topics[1] = {ARM_ROBOT_TOPIC_JOINT_POSITION}; // 로봇 상태 topic 목록 준비
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); // 관절 위치 topic 구독
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;
}