冗余 (Redundancy)
EtherCAT 冗余通过双网卡 + 环形拓扑实现链路级容错: 主网口走 primary 帧, 副网口走 secondary 帧, 任一段断线时 SDK 自动从两侧 WKC 合并完成的从站状态, 整个网络仍正常运行.
启用冗余通过 SetNetwork 同时绑定主/副网卡完成。
启用方式
冗余在 初始化阶段 通过 SetNetwork(mi, if_primary, if_secondary) 指定双网口启用。运行时只需 EnableRedundancy(mi, TRUE) 打开开关 (Start 之前调用)。
相关页面
- 冗余诊断 / 故障点检测详见 主站诊断 - 冗余状态
- 冗余链路状态变化事件:
RedundancyModeChanged - 网口扫描自动识别冗余对:
GetNetworkInfo - 从站级冗余诊断(主链/副链断路位): 从站诊断 - 冗余诊断
RedundancyState
typedef enum {
EC_REDUNDANCY_NONE = 0, /* 未启用 */
EC_REDUNDANCY_PRIMARY = 1, /* 仅主网口在用 */
EC_REDUNDANCY_SECONDARY = 2, /* 仅副网口在用 */
EC_REDUNDANCY_BOTH = 3 /* 主副均在用 (健康) */
} ec_redundancy_state_t;
typedef enum {
EC_RING_INACTIVE = 0, /* 未启用冗余 / 单网卡 */
EC_RING_DUAL = 1, /* 双向健康, 主副网口都收发正常 */
EC_RING_DEGRADED = 2 /* 环上有断点, 主副双发已合并恢复 */
} ec_ring_mode_t;
RedundancyStatus
冗余运行时状态详细信息。
typedef struct {
ec_redundancy_state_t state;
BOOL primary_link_up;
BOOL secondary_link_up;
uint32_t primary_tx_frames;
uint32_t primary_rx_frames;
uint32_t secondary_tx_frames;
uint32_t secondary_rx_frames;
uint32_t failover_count; /* 故障切换次数 */
uint32_t last_failover_time; /* 最近一次切换时间 (ms 时间戳) */
} ec_redundancy_status_t;
冗余控制函数:
BOOL EnableRedundancy(uint16_t master_index, BOOL enable);
BOOL ForceRedundancyFailover(uint16_t master_index);
BOOL CheckRedundancyHealth(uint16_t master_index);
void* GetRedundancyStatus(uint16_t master_index); /* 返回 ec_redundancy_status_t* */
int GetRingMode(uint16_t master_index);
BOOL GetSecondaryLinkStatus(uint16_t master_index);
uint16_t GetPrimaryWKC(void);
uint16_t GetSecondaryWKC(void);
void SetRedProcessdata(int mode); /* 0=关闭手工镜像, 1=自动镜像 (默认), 2=用户自行控制副路 */
int GetRedProcessdata(void);
EnableRedundancy()必须在Start()之前调用,且构造时已通过SetNetwork同时指定主/副网卡(if_secondary不为空字符串)。ForceRedundancyFailover()强制切到副网口(调试 / 计划性维护)。CheckRedundancyHealth()任一路异常返回FALSE。GetRedundancyStatus()返回内部指针,无需释放,跨周期内容会变化,不要长期保存。GetPrimaryWKC()/GetSecondaryWKC()冗余模式下独立跟踪;单网卡模式GetSecondaryWKC返回 0。SetRedProcessdata(mode=2)后副路 PDO 由用户自行写,不写则发 0,可能让从站走入 fail-safe。
完整示例
启用冗余 + 健康监控
#define DYNAMIC_LOAD
#include "ethercat.h"
#include <stdio.h>
#include <windows.h>
static void on_mode_change(uint16_t mi, int old_mode, int new_mode) {
const char* names[] = {"Inactive", "Dual", "Degraded"};
printf("冗余模式变化: %s -> %s\n", names[old_mode], names[new_mode]);
}
int main(void) {
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t mi = dll.Initialize();
dll.SetNetwork(mi, "\\Device\\NPF_{primary-guid}",
"\\Device\\NPF_{secondary-guid}");
dll.EnableRedundancy(mi, TRUE);
dll.RegisterRedundancyChanged(on_mode_change);
dll.SetStateSequence(mi, EC_STATE_OPERATIONAL, 10000);
dll.Start(mi);
while (1) {
BOOL healthy = dll.CheckRedundancyHealth(mi);
int mode = dll.GetRingMode(mi);
uint16_t wp = dll.GetPrimaryWKC();
uint16_t ws = dll.GetSecondaryWKC();
ec_redundancy_status_t* st =
(ec_redundancy_status_t*) dll.GetRedundancyStatus(mi);
printf("健康=%d 环模式=%d WKC(P/S)=%u/%u failover=%u\n",
healthy, mode, wp, ws, st ? st->failover_count : 0u);
Sleep(1000);
}
dll.Stop(mi);
dll.Dispose(mi);
UNLOAD_DLL(&dll);
return 0;
}
故障转移演练 / 双路差异化测试:
/* 计划性维护: 强制切到副网口, 拔主网卡线缆做检修 */
dll.ForceRedundancyFailover(mi);
Sleep(500);
/* 双路差异化故障注入: 切到手工模式, 副路由调用方控制 */
dll.SetRedProcessdata(2);
/* ... 此时副路 PDO 通过 WriteSlaveOutput 单独写 ... */
dll.SetRedProcessdata(1); /* 恢复默认镜像 */