冗余 (Redundancy)
EtherCAT 冗余通过双网卡 + 环形拓扑实现链路级容错: 主网口走 primary 帧, 副网口走 secondary 帧, 任一段断线时 SDK 自动从两侧 WKC 合并完成的从站状态, 整个网络仍正常运行.
通过 master 直接调用启用 / 强制故障转移, 通过 master.diagnostics_info() 查看诊断.
- 冗余诊断 /
RingMode/BreakPoint详见 主站诊断 - 冗余状态 - 冗余链路状态变化事件:
on_redundancy_mode_changed
启用与状态
| 方法 | 返回类型 | 说明 |
|---|---|---|
| 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)
环形冗余下, 主帧 (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 | 只读 | 主+副合并后本周期真正完成交换的从站数 |
| 期望 WKC | diagnostics_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