断线重连与状态恢复
上位机长时间运行时,网线拔插、交换机重启、控制器重启都会让连接断开。重连回来不等于可以继续干活:控制器侧的伺服状态、运行模式、队列状态可能已经被复位。本页给出可直接套用的重连与恢复顺序。
先记住三件事
set_reconnect默认关闭,不显式打开,断线后不会自动重连,后续调用一路返回-2。- 重连回调是 C 函数指针
void(*)(),只在重连成功后触发;回调里不要做耗时操作,也不要直接下发运动命令。 - 重连后必须重新读状态:
get_servo_state/get_current_mode/queue_motion_get_status,确认再继续。
一、两种做法
| 做法 | 适用 | 说明 |
|---|---|---|
| SDK 自动重连 | 网络短暂抖动、交换机重启 | set_reconnect(fd, true) + set_reconnect_callback(fd, cb),SDK 内部重试 |
| 业务层重连 | 控制器重启、需要换 IP、要控制重试节奏 | 自己 disconnect_robot + connect_robot,可控但要多写循环 |
两者可以叠加:先用 SDK 自动重连兜住短抖动,连续失败超过设定次数再由业务层重建连接。
二、接口
cpp
// 打开/关闭断开后自动重连(默认关闭)
Result set_reconnect(SOCKETFD socketFd, bool reconnect);
// 设置重连成功后的回调(C 函数指针,无参数)
Result set_reconnect_callback(SOCKETFD socketFd, void (*function)());三、恢复顺序(重连成功后按这个走)
| 步骤 | 调用 | 判据 |
|---|---|---|
| 1 | get_servo_state(fd, state) | 读到状态值,确认连接真的可用 |
| 2 | clear_error(fd) | 有报警先清;返回非 0 也要继续读状态确认 |
| 3 | set_servo_state(fd, 1) | 伺服置「就绪」;状态为 2(报警)或 3(运行)时该调用会返回 -4 |
| 4 | set_servo_poweron(fd) | 返回 0 只代表命令下发,必须继续轮询状态 |
| 5 | 轮询 get_servo_state 直到 3 | 超时(建议 5 s)则放弃本次恢复,报警给上层 |
| 6 | get_current_mode(fd, mode) | 确认当前是示教/运行/远程中的哪一个,再决定能否下发运动 |
| 7 | queue_motion_get_status(fd, on) | 用到队列的再确认队列模式是否还开着(断线时通常已被关闭) |
四、完整示例
cpp
#include <chrono>
#include <iostream>
#include <thread>
#include "cpp/interface/nrc_api.h"
namespace {
const char* kIp = "192.168.1.13";
const char* kPort = "6001"; // 命令 / 状态通道
volatile bool g_reconnected = false;
// 回调里只置标志,不做任何 SDK 调用、不做耗时操作
void onReconnected()
{
g_reconnected = true;
}
bool waitServoRunning(SOCKETFD fd, int timeoutMs)
{
const int step = 100;
for (int waited = 0; waited <= timeoutMs; waited += step) {
int state = -1;
if (get_servo_state(fd, state) == SUCCESS && state == 3) {
return true;
}
std::this_thread::sleep_for(std::chrono::milliseconds(step));
}
return false;
}
// 重连后的状态恢复;返回 false 表示需要人工介入
bool recoverAfterReconnect(SOCKETFD fd)
{
int state = -1;
if (get_servo_state(fd, state) != SUCCESS) {
std::cout << "读状态失败,连接不可用" << std::endl;
return false;
}
if (state == 3) {
std::cout << "仍在运行状态,无需重新上电" << std::endl;
return true;
}
clear_error(fd); // 有报警先清
set_servo_state(fd, 1); // 就绪
set_servo_poweron(fd); // 上电(返回 0 不等于已上电)
if (!waitServoRunning(fd, 5000)) {
std::cout << "上电超时,放弃本次恢复" << std::endl;
return false;
}
int mode = -1;
get_current_mode(fd, mode);
std::cout << "恢复完成,当前模式 = " << mode << std::endl;
return true;
}
} // namespace
int main()
{
SOCKETFD fd = connect_robot(kIp, kPort);
if (fd <= 0) {
std::cout << "连接失败: " << fd << std::endl;
return 1;
}
set_reconnect(fd, true);
set_reconnect_callback(fd, onReconnected);
// 业务主循环:只在连接可用时下发指令
while (true) {
if (g_reconnected) {
g_reconnected = false;
std::cout << "检测到重连,开始恢复状态" << std::endl;
if (!recoverAfterReconnect(fd)) {
std::cout << "恢复失败,转人工处理" << std::endl;
break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
disconnect_robot(fd);
return 0;
}五、常见现象对照
| 现象 | 原因 | 处理 |
|---|---|---|
断线后所有调用都返回 -2 | 没开 set_reconnect,或句柄已失效 | 开启自动重连;业务层检测到连续 -2 就重建连接 |
| 重连成功但机器人不动 | 伺服状态被复位(不再是 3),或模式被切走 | 按上面第三节重新上电 + 读模式 |
重连后第一次运动报 -4 | 队列/追加模式已被关闭,或机器人停在急停态 | 重新开队列模式;确认急停已释放 |
| 重连回调里崩溃 | 在回调线程里调了阻塞接口或直接发运动 | 回调只置标志,所有 SDK 调用回到业务线程 |
| PC 能 ping 通但连不上 | 控制器侧服务端口未监听(不是网络问题) | 换端口/查服务状态,属于控制器侧问题,按现场支持流程处理 |
六、注意
set_reconnect_callback只传函数指针,不能带上下文;多连接场景需要自己用全局映射把socketFd和业务对象对上(回调本身不带 fd,[待核]是否会在回调中传入句柄以你的版本头文件为准)。- 重连后不要复用断线前的运动指令:位置可能已经变了,按当前位置重新计算。
- 断线期间控制器可能仍在执行最后一条运动指令,恢复前先确认机器人已停止。