冗余 (Redundancy)
EtherCAT 冗余通过双网卡 + 环形拓扑实现链路级容错: 主网口走 primary 帧, 副网口走 secondary 帧, 任一段断线时 SDK 自动从两侧 WKC 合并完成的从站状态, 整个网络仍正常运行.
通过 master.GetDiagnostics() 访问冗余状态; 启用冗余通过初始化时指定双网口完成。
冗余在 初始化阶段 通过 SetNetwork(primary, redundant) 指定双网口启用, 不需要单独的开关 API。运行时无需手动控制。冗余管理函数位于 darra::ethercat::master_redundancy 命名空间, 提供 EnableRedundancy / DisableRedundancy / GetRedundancyStatus / IsRedundancyActive / ForceFailover / CheckRedundancyHealth / GetBreakPoints。
- 冗余诊断 /
RingMode/BreakPoint详见 主站诊断 - 冗余状态 - 冗余链路状态变化事件:
RedundancyModeChanged - 网口扫描自动识别冗余对:
darra::ethercat::statics::get_network_info()
RedundancyState
enum class RedundancyState : int {
None = 0, // 无冗余
Primary = 1, // 仅使用主网络
Secondary = 2, // 仅使用备用网络
Both = 3 // 使用双网络 (活动冗余)
};
RedundancyStatus
冗余运行时状态详细信息。
struct RedundancyStatus {
RedundancyState state; // 冗余总体状态
bool primaryLinkUp; // 主链路是否在线
bool secondaryLinkUp; // 副链路是否在线
uint32_t primaryTxFrames; // 主网口发送帧数
uint32_t primaryRxFrames; // 主网口接收帧数
uint32_t secondaryTxFrames; // 副网口发送帧数
uint32_t secondaryRxFrames; // 副网口接收帧数
uint32_t failoverCount; // 故障切换次数
uint32_t lastFailoverTime; // 上次切换时间 (Unix 时间戳)
};
struct RedundancyBreakPoint {
uint16_t slaveIndex; // 断点所在从站索引
uint8_t port; // 断点端口号 (0-3)
};
冗余 WKC 读取 (合并 WKC)
环形冗余下, 主帧 (primary) 从 P0 入网走 ring + branch, 副帧 (secondary) 从 P1 入网仅走 ring。某段断开时一侧帧只能看到断点之前的从站, 另一侧从反方向补齐 —— SDK 把两侧 WKC 合并, 得到本周期真正完成 PDO 交换的从站数。
铁律: 合并 WKC == 期望 WKC, 即网络完好; 此时主口单口 WKC 可以 < 期望, 不是故障。 切勿用 PrimaryWKC() 单口值去比 ExpectedWKC() 判掉站 —— 断点被冗余补偿时这必然误报。
WKC 访问器都在高层包装 MasterDiagnosticsInfo 上(由 master.GetDiagnostics() 返回的 Diagnostics& 构造,见 主站诊断 - 获取诊断对象)。合并 (当前) WKC 与期望 WKC 经 GetSnapshot() 一致快照取出:
| 类别 | 访问器 | 读写 | 说明 |
|---|---|---|---|
| 合并 WKC (权威) | GetSnapshot().WkcActual | 只读 | 主+副合并后本周期真正完成交换的从站数 |
| 期望 WKC | GetSnapshot().WkcExpected / ExpectedWKC() | 只读 | 拓扑固定真值, 永不下调迁就劣化总线 |
| 主口单口 | PrimaryWKC() | 只读 | 主口实测 WKC; 断点被补偿时可 < 期望, 属正常 |
| 副口单口 | SecondaryWKC() | 只读 | 副口实测 WKC; 单网卡模式恒为 0 |
| 冗余激活 | RedundancyActive() | 只读 | 双端口均有流量 (即存在被补偿的断点) |
判健康的正确写法 — 以合并 WKC 为准:
auto& base = master.GetDiagnostics(); // Diagnostics&
MasterDiagnosticsInfo diag(base, master.MasterNumber()); // 高层包装
auto snap = diag.GetSnapshot();
// ✅ 正确: 合并 WKC == 期望 WKC, 网络完好 (即使有断点被冗余补偿)
bool linkHealthy = (snap.WkcActual == snap.WkcExpected);
if (linkHealthy && diag.RedundancyActive())
printf("有断点, 但冗余已补齐, 网络仍完整 (合并 WKC=%u/%u)\n",
snap.WkcActual, snap.WkcExpected);
else if (!linkHealthy)
printf("真有从站掉了: 合并 WKC=%u < 期望=%u\n",
snap.WkcActual, snap.WkcExpected);
// 诊断断点位置时再看主/副单口 (不要拿单口去判整体健康)
printf("主口单口 WKC=%u, 副口单口 WKC=%u\n",
diag.PrimaryWKC(), diag.SecondaryWKC());
完整示例
启用冗余 + 健康监控
#include "ethercat.hpp"
#include <chrono>
#include <thread>
using namespace darra::ethercat;
int main() {
// 推荐通过 statics::get_network_info() 自动识别冗余对
auto adapters = statics::get_network_info();
NetworkInfo primary, redundant;
int found = 0;
for (auto& a : adapters) {
if (a.RedundantSlaveNum > 0) {
(found++ == 0 ? primary : redundant) = a;
if (found >= 2) break;
}
}
if (found < 2) return -1;
EtherCATMaster master(dll);
master.SetNetwork(primary, redundant)
.SetENI("C:/config.deni");
// 启用冗余 (Build 前调用)
master_redundancy::EnableRedundancy(master);
master.Build();
// 订阅冗余模式变化 (用 OnRedundancyModeChanged 注册)
master.Events().OnRedundancyModeChanged([](uint16_t mi, int oldM, int newM) {
printf("冗余模式: %d -> %d\n", oldM, newM);
});
master.SetState(EcState::OP);
master.Start();
// 周期性健康检查
auto& base = master.GetDiagnostics(); // Diagnostics&
MasterDiagnosticsInfo diag(base, master.MasterNumber()); // 高层包装, RingMode() 返回 darra::RingMode 枚举
while (true) {
auto status = master_redundancy::GetRedundancyStatus(master);
if (status) {
printf("冗余: ring_mode=%d active=%d primary=%d secondary=%d failover=%u\n",
static_cast<int>(diag.RingMode()),
master_redundancy::IsRedundancyActive(master),
status->primaryLinkUp, status->secondaryLinkUp,
status->failoverCount);
}
auto bps = master_redundancy::GetBreakPoints(master);
for (auto& bp : bps) {
printf("故障点: 从站[%u] 端口=%u\n", bp.slaveIndex, bp.port);
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 0;
}
- ETG.1500 §5.7 Cable Redundancy
- 其他语言 SDK 对齐: C#
EnableRedundancy/ JavaenableRedundancy/ Pythonenable_redundancy/ Rustenable_redundancy