跳到主要内容

冗余 (Redundancy)

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

通过 master 直接调用启用 / 强制故障转移, 通过 master.diagnostics_info() 查看诊断.

相关页面

启用与状态

方法返回类型说明
enable_redundancy(bool)Result<()>启用 / 禁用冗余 (需在 start() 之前调用)
force_redundancy_failover()Result<()>强制冗余故障切换 (调试 / 维护用)
check_redundancy_health()bool检查冗余健康状态
diagnostics_info().ring_mode()RingMode环拓扑冗余运行模式 (枚举, 推荐)
ring_mode()i32环拓扑冗余运行模式原始值 (0=Inactive, 1=Dual, 2=Degraded)
secondary_link_status()bool副网口链路是否正常

enable_redundancy()

pub fn enable_redundancy(&self, enable: bool) -> Result<()>

启用 / 禁用冗余. 必须在 start() 之前调用, 且构造主站时已经通过 set_network(primary, redundant) 指定副网卡.

force_redundancy_failover()

pub fn force_redundancy_failover(&self) -> Result<()>

强制把当前发送路径切到副网口, 调试或预防性维护时使用.

check_redundancy_health()

pub fn check_redundancy_health(&self) -> bool

检查冗余链路当前是否健康.

ring_mode() / RingMode 枚举

// 推荐: 经诊断对象返回强类型枚举
pub fn ring_mode(&self) -> RingMode // MasterDiagnosticsInfo::ring_mode

// 原始值: 主站 / RedundancyManager 上直接返回 i32
pub fn ring_mode(&self) -> i32 // EtherCATMaster::ring_mode / RedundancyManager::ring_mode

环拓扑冗余运行模式。master.diagnostics_info().ring_mode() 返回强类型 RingMode 枚举(推荐);master.ring_mode()RedundancyManager::ring_mode() 返回底层原始 i32,与枚举判别值一一对应。

#[repr(i32)]
pub enum RingMode {
Inactive = 0, // 未激活: 冗余监控未初始化
Dual = 1, // 双向冗余: P1 打开, secondary 激活后两端发送 LRW
Degraded = 2, // 降级模式: secondary 链路不可用, 仅 primary 工作
}

冗余过程数据模式

EtherCATMaster::set_redundancy_mode() / EtherCATMaster::redundancy_mode()

pub fn set_redundancy_mode(mode: i32)
pub fn redundancy_mode() -> i32

静态方法, 设置 / 读取全局冗余过程数据处理模式 (0 = 禁用, 1 = 启用).

冗余 WKC 读取 (合并 WKC)

合并 WKC 是权威值, 别拿主口单口 WKC 判故障

环形冗余下, 主帧 (primary) 从 P0 入网走 ring + branch, 副帧 (secondary) 从 P1 入网仅走 ring。某段断开时一侧帧只能看到断点之前的从站, 另一侧从反方向补齐 —— SDK 把两侧 WKC 合并, 得到本周期真正完成 PDO 交换的从站数。

铁律: 合并 WKC == 期望 WKC, 即网络完好; 此时主口单口 WKC 可以 < 期望, 不是故障。 切勿用单口 WKC 去比期望判掉站 —— 断点被冗余补偿时这必然误报。

判整体健康用 master.diagnostics_info() (类型 MasterDiagnosticsInfo) 上的合并 WKC; 诊断断点位置再用 RedundancyManager 的主/副单口访问器。

类别方法返回类型读写说明
合并 WKC (权威)diagnostics_info().wkc()u16只读主+副合并后本周期真正完成交换的从站数
期望 WKCdiagnostics_info().expected_wkc()u16只读拓扑固定真值, 永不下调迁就劣化总线
缺额diagnostics_info().wc_deficit()u16只读期望 - 合并实测, >0 才表示真有从站掉了
冗余激活diagnostics_info().redundancy_active()bool只读双端口均有流量 (即存在被补偿的断点)
主口单口RedundancyManager::primary_wkc()u16只读主口实测; 断点被补偿时可 < 期望, 属正常
副口单口RedundancyManager::secondary_wkc()u16只读副口实测; 单网卡模式恒为 0
主口单口 (RT)RedundancyManager::primary_wkc_rt()u16只读RT 路径直读 (内核已滤波, 延迟更低)
副口单口 (RT)RedundancyManager::secondary_wkc_rt()u16只读RT 路径直读 (内核已滤波, 延迟更低)
主口期望RedundancyManager::topo_primary_intact_wkc()u16只读主发帧期望 WKC = ring + branch (主口拓扑完好真值)
副口期望RedundancyManager::topo_secondary_intact_wkc()u16只读副发帧期望 WKC = ring (副口拓扑完好真值)

判健康的正确写法 — 以合并 WKC 为准:

use darra_ethercat::master::redundancy::RedundancyManager;

let diag = master.diagnostics_info();

// ✅ 正确: 合并 WKC == 期望 WKC, 网络完好 (即使有断点被冗余补偿)
let link_healthy = diag.wkc() == diag.expected_wkc();

if link_healthy && diag.redundancy_active() {
println!("有断点, 但冗余已补齐, 网络仍完整 (合并 WKC={})", diag.wkc());
} else if !link_healthy {
println!("真有从站掉了: 合并 WKC={} < 期望={} (缺额={})",
diag.wkc(), diag.expected_wkc(), diag.wc_deficit());
}

// 诊断断点位置时再看主/副单口 (不要拿单口去判整体健康)
let red = RedundancyManager::new(master.index());
println!("主口单口 WKC={} (期望主路={}), 副口单口 WKC={} (期望副路={})",
red.primary_wkc(), red.topo_primary_intact_wkc(),
red.secondary_wkc(), red.topo_secondary_intact_wkc());

完整示例

启用冗余 + 健康监控

use darra_ethercat::EtherCATMaster;

let mut master = EtherCATMaster::new()?;
master.set_network(r"\Device\NPF_{primary-guid}", r"\Device\NPF_{secondary-guid}")?;

// 启用冗余 (start 前调用)
master.enable_redundancy(true)?;

master.set_state(EcState::Operational)?;
master.start();

// 订阅冗余模式变化 (回调参数: master_index, old_mode, new_mode)
master.events().on_redundancy_mode_changed(|_master, old_mode, new_mode| {
println!("冗余模式: {} -> {}", old_mode, new_mode);
});

// 周期性健康检查
loop {
let healthy = master.check_redundancy_health();
let mode = master.ring_mode();
let sec_ok = master.secondary_link_status();
println!("冗余: healthy={} ring_mode={} secondary_link={}", healthy, mode, sec_ok);
std::thread::sleep(std::time::Duration::from_secs(1));
}

故障转移演练

// 模拟主网口故障, 强制切到副网口
master.force_redundancy_failover()?;
std::thread::sleep(std::time::Duration::from_millis(500));

// 检查副链路是否承载主路
assert!(master.secondary_link_status());
assert_eq!(master.state(), EcState::Operational);
参考
  • ETG.1500 §5.7 Cable Redundancy