从站事件
通过 slave.events() 访问。事件参数已过滤到当前从站,签名简洁。
事件自动路由
主站级 DLL 回调会自动路由到对应从站的 slave.events()。订阅从站事件无需 master_index/slave_index 参数,系统已自动过滤到当前从站。
协议专属事件
FSoE 安全事件通过 FsoeManager 订阅,请参考 FSoE。
功能概览
| 类别 | 事件 / 方法 | 说明 |
|---|---|---|
| 从站状态事件 | slave.events().on_state_changed() | 从站 EtherCAT 状态变化 |
| slave.events().on_emergency() | CoE Emergency 紧急消息 | |
| slave.events().on_offline() | 从站离线(热插拔断开) | |
| slave.events().on_online() | 从站上线(热插拔恢复) | |
| slave.events().on_dc_sync_lost() | DC 同步丢失 | |
| 输入数据变化 | slave.events().on_input_changed() | 输入 PDO 数据变化(原生检测) |
从站状态事件
on_state_changed
slave.events().on_state_changed(|old_state, new_state| { });
// Fn(i32, i32)
从站 EtherCAT 状态变化时触发。由 master.events().on_slave_state_changed() 自动路由。
示例:
slave.events().on_state_changed(|old_state, new_state| {
println!("状态变化: {:?} → {:?}", old_state, new_state);
});
on_emergency
slave.events().on_emergency(|error_code, error_reg, b1, w1, w2| { });
// Fn(u16, u16, u8, u16, u16)
CoE Emergency 紧急消息。由 master.events().on_emergency_event() 自动路由。
回调参数:
// Fn(u16, u16, u8, u16, u16)
error_code: u16, // 错误代码(CANopen Emergency Error Code)
error_reg: u16, // 错误寄存器(对象 0x1001)
b1: u8, // 制造商特定数据(字节 3)
w1: u16, // 制造商特定数据(字节 4-5)
w2: u16, // 制造商特定数据(字节 6-7)
示例:
slave.events().on_emergency(|error_code, _error_reg, _b1, _w1, _w2| {
println!("紧急消息: 错误码=0x{:04X}", error_code);
});
on_offline
slave.events().on_offline(|| { });
// Fn()
从站离线(热插拔断开)。由 master.events().on_slave_offline() 自动路由。
示例:
slave.events().on_offline(|| {
println!("从站离线");
});
on_online
slave.events().on_online(|| { });
// Fn()
从站上线(热插拔恢复)。由 master.events().on_slave_online() 自动路由。
示例:
slave.events().on_online(|| {
println!("从站上线");
});
on_dc_sync_lost
slave.events().on_dc_sync_lost(|diff_ns| { });
// Fn(i32)
DC 同步丢失。由 master.events().on_dc_sync_lost() 自动路由。
示例:
slave.events().on_dc_sync_lost(|diff_ns| {
println!("DC 同步丢失: 偏差 {}ns", diff_ns);
});
输入数据变化
on_input_changed
slave.events().on_input_changed(|| { });
// Fn()
从站输入 PDO 数据变化时触发。仅当输入数据与上一周期不同时回调。由 master.events().on_input_data_changed() 自动路由。
零开销
- 无变化时:不触发回调,零性能开销
- 有变化时:仅变化的从站收到回调,其他从站不受影响
示例:
slave.events().on_input_changed(|| {
let position = slave.read_input_i32(2);
println!("位置变化: {}", position);
});
线程安全
事件回调在 PDO 线程上触发,非主线程。在 Rust 中,回调闭包必须满足 Send + 'static 约束。使用共享数据时需要同步:
use std::sync::{Arc, Mutex};
let position = Arc::new(Mutex::new(0i32));
let pos_clone = position.clone();
slave.events().on_input_changed(move || {
let actual = slave.read_input_i32(2);
*pos_clone.lock().unwrap() = actual;
});
注意
回调中避免执行耗时操作。如需处理大量数据,将数据发送到 channel 异步处理:
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
slave.events().on_input_changed(move || {
let pos = slave.read_input_i32(2);
let _ = tx.send(pos); // 快速发送
});
完整示例
use ethercat::EcState;
let slave = master.slave(1);
// ===== 从站状态事件 =====
slave.events().on_state_changed(|old, new| {
println!("状态: {:?} → {:?}", old, new);
});
slave.events().on_emergency(|ec, _er, _b1, _w1, _w2| {
println!("紧急消息: 0x{:04X}", ec);
});
slave.events().on_offline(|| println!("从站离线"));
slave.events().on_online(|| println!("从站上线"));
slave.events().on_dc_sync_lost(|diff_ns| {
println!("DC 同步丢失: {}ns", diff_ns);
});
// ===== 输入数据变化 =====
slave.events().on_input_changed(|| {
let position = slave.read_input_i32(2);
println!("位置: {}", position);
});