异常处理
C++ SDK 使用异常机制报告错误,SDK 操作失败时抛出 DarraException。
DarraException
class DarraException : public std::runtime_error {
public:
DarraException(int code, const std::string& msg);
DarraException(const std::string& msg);
/** 获取错误码 */
int code() const noexcept;
};
DarraException 继承自 std::runtime_error,包含错误码和错误消息。位于 darra::ethercat 命名空间。
构造方式:
DarraException(int code, const std::string& msg)— 带错误码DarraException(const std::string& msg)— 不带错误码(默认 -1)
成员方法:
what()(const char*) — 错误消息(继承自 std::runtime_error)code()(int) — 错误码
异常触发场景
SDK 在以下场景抛出 DarraException:
| 场景 | 说明 |
|---|---|
| JSON 初始化失败 | EtherCATMaster_FromJson() / EtherCATMaster_FromJsonFile() 配置无效 |
CoEAccessDeniedException
CoE SDO 访问权限相关错误使用独立的异常类型 CoEAccessDeniedException, 同样位于 darra::ethercat 命名空间, 继承自 std::runtime_error。
class CoEAccessDeniedException : public std::runtime_error {
public:
CoEAccessDeniedException(uint16_t index, uint8_t sub, const std::string& name,
bool isRead, uint16_t objAccess);
uint16_t Index() const;
uint8_t SubIndex() const;
bool IsReadOperation() const;
uint16_t ObjAccess() const;
static CoEAccessDeniedException CreateWithCustomMessage(
uint16_t index, uint8_t sub, const std::string& msg,
bool isRead, uint16_t objAccess);
};
成员方法:
| 方法 | 类型 | 说明 |
|---|---|---|
| Index() | uint16_t | SDO 对象索引 |
| SubIndex() | uint8_t | 子索引 |
| IsReadOperation() | bool | 触发操作是否为读 (false=写) |
| ObjAccess() | uint16_t | 对象访问位图 (ObjAccess) |
| what() | const char* | 包含中文权限描述的可读消息 |
触发场景: 对只读对象写入、对只写对象读取、在当前 EtherCAT 状态下不允许的访问 (如 OP 写 PreOP-only 对象) 等。
示例:
try {
coe.SDOWriteU16(0x6041, 0, 0x0006); // StatusWord 是只读
} catch (const ethercat::CoEAccessDeniedException& e) {
printf("访问拒绝: idx=0x%04X sub=%u 读=%d (%s)\n",
e.Index(), e.SubIndex(), e.IsReadOperation(), e.what());
}
与 SdoError 的区别
SDO Abort Code (SdoError) 是底层从站返回的中止码, 通过 coe.LastSdoError() 查询, 不抛异常; CoEAccessDeniedException 是 SDK 在调用前根据对象字典访问位图主动判定的访问权限异常。
错误处理模式
异常捕获
#include "ethercat.hpp"
using namespace darra;
try {
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}")
.SetENI("config.deni")
.Build();
master.SetState(EcState::OP);
master.Start();
} catch (const ethercat::DarraException& e) {
printf("SDK 错误: %s (code=%d)\n", e.what(), e.code());
} catch (const std::exception& e) {
printf("标准错误: %s\n", e.what());
}
std::optional 返回值
大部分 Slave 和协议类方法使用 std::optional 表示可能失败的操作,不会抛出异常:
auto& slave = master.GetSlave(1);
// CoE 类型化读取(可能失败)
auto& coe = slave.GetCoE();
auto sw = coe.SDOReadU16(0x6041, 0); // std::optional<uint16_t>
if (sw) {
printf("StatusWord: 0x%04X\n", *sw);
} else {
printf("SDO 读取失败\n");
}
空 vector 返回值
数据读取方法返回空 std::vector<uint8_t> 表示失败:
auto& coe = slave.GetCoE();
// SDO 读取
auto data = coe.SDORead(0x6041, 0);
if (data.empty()) {
printf("SDO 读取失败\n");
return;
}
// FoE 文件读取
auto& foe = slave.GetFoE();
auto file = foe.Download("config.xml");
if (file.empty()) {
printf("文件读取失败\n");
}
bool 返回值
写入操作返回 bool 表示成功/失败:
auto& coe = slave.GetCoE();
bool ok = coe.SDOWriteU16(0x6040, 0, 0x0006);
if (!ok) {
printf("SDO 写入失败\n");
}
Build() 返回值
EtherCATMaster::Build() 返回 bool 而非抛出异常:
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}");
if (!master.Build()) {
printf("主站初始化失败\n");
return -1;
}
AL 错误分类
ALErrorClassifier 对从站 AL Status Code 进行分类,帮助快速判断错误性质和处理策略。
auto category = ALErrorClassifier::Classify(slave.ErrorCode());
switch (category) {
case ALErrorCategory::None: /* 无错误 */ break;
case ALErrorCategory::Transient: /* 瞬态错误,可重试 */ break;
case ALErrorCategory::Configuration: /* 配置错误,检查 PDO/SM */ break;
case ALErrorCategory::Hardware: /* 硬件错误,检查设备 */ break;
case ALErrorCategory::Unknown: /* 未知,查阅 ETG.1000 */ break;
}
详见 主站诊断 - AL 错误分类。
错误处理最佳实践
#include "ethercat.hpp"
using namespace darra;
int main() {
try {
// Build 返回 bool
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}")
.SetENI("config.deni");
if (!master.Build()) {
printf("初始化失败\n");
return -1;
}
auto& slave = master.GetSlave(1);
// 协议操作使用 optional/bool 检查
auto& coe = slave.GetCoE();
if (auto sw = coe.SDOReadU16(0x6041, 0)) {
printf("StatusWord: 0x%04X\n", *sw);
}
if (!coe.SDOWriteU16(0x6040, 0, 0x0006)) {
printf("ControlWord 写入失败\n");
}
// Start/Stop 返回 bool
master.SetState(EcState::OP);
if (!master.Start()) {
printf("启动失败\n");
}
getchar();
} catch (const ethercat::DarraException& e) {
fprintf(stderr, "DarraEtherCAT 错误: %s (code=%d)\n",
e.what(), e.code());
return 1;
}
return 0;
}