跳到主要内容

冗余 (Redundancy)

EtherCAT 冗余通过双网卡 + 环形拓扑实现链路级容错: 主网口走 primary 帧, 副网口走 secondary 帧, 任一段断线时 SDK 自动从两侧 WKC 合并完成的从站状态, 整个网络仍正常运行.

启用冗余通过 SetNetwork 同时绑定主/副网卡完成。

启用方式

冗余在 初始化阶段 通过 SetNetwork(mi, if_primary, if_secondary) 指定双网口启用。运行时只需 EnableRedundancy(mi, TRUE) 打开开关 (Start 之前调用)。

相关页面

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); /* 恢复默认镜像 */