从站事件
通过 master.Events() 的主站级事件按从站索引过滤。
事件路由
从站事件由主站事件系统自动路由,无需手动注册 DLL 回调。订阅主站级事件 master.Events().add*Listener() 后,对应从站的事件会自动触发。
协议专属事件
FSoE 安全事件通过 slave.FSoE() 订阅,请参考 FSoE。
功能概览
| 类别 | 事件 | 说明 |
|---|---|---|
| 从站状态 | SlaveStateChanged | 从站 EtherCAT 状态变化 |
| EmergencyEvent | CoE Emergency 紧急消息 | |
| SlaveOffline | 从站离线(热插拔断开) | |
| SlaveOnline | 从站上线(热插拔恢复) | |
| DCSyncLost | DC 同步丢失 | |
| 输入数据 | InputDataChanged | 输入 PDO 数据变化 |
从站状态事件
SlaveStateChanged
master.Events().addSlaveStateChangedListener((masterIndex, slaveIndex, oldState, newState) -> {
// ...
});
从站 EtherCAT 状态变化时触发。
回调参数:
masterIndex(int) — 主站索引slaveIndex(int) — 从站索引oldState(EcState) — 旧状态newState(EcState) — 新状态
示例:
master.Events().addSlaveStateChangedListener((mi, si, oldState, newState) -> {
System.out.printf("从站 %d 状态: %s -> %s%n", si, oldState, newState);
});
EmergencyEvent
master.Events().addEmergencyEventListener((masterIndex, slaveIndex, errorCode, errorReg, b1, w1, w2) -> {
// ...
});
CoE Emergency 紧急消息。
回调参数:
masterIndex(int) — 主站索引slaveIndex(int) — 从站索引errorCode(int) — 错误代码(CANopen Emergency Error Code)errorReg(int) — 错误寄存器(对象 0x1001)b1(int) — 制造商特定数据(字节 3)w1(int) — 制造商特定数据(字节 4-5)w2(int) — 制造商特定数据(字节 6-7)
示例:
master.Events().addEmergencyEventListener((mi, si, ec, er, b, w1, w2) -> {
System.out.printf("从站 %d 紧急消息: 错误码=0x%04X%n", si, ec);
});
SlaveOffline
master.Events().addSlaveOfflineListener((slaveIndex) -> {
// ...
});
从站离线(热插拔断开)。
回调参数:
slaveIndex(int) — 从站索引
示例:
master.Events().addSlaveOfflineListener((si) -> {
System.out.println("从站 " + si + " 离线");
});
SlaveOnline
master.Events().addSlaveOnlineListener((slaveIndex) -> {
// ...
});
从站上线(热插拔恢复)。
回调参数:
slaveIndex(int) — 从站索引
示例:
master.Events().addSlaveOnlineListener((si) -> {
System.out.println("从站 " + si + " 上线");
});
DCSyncLost
master.Events().addDCSyncLostListener((masterIndex, slaveIndex, diffNs) -> {
// ...
});
DC 同步丢失。
回调参数:
masterIndex(int) — 主站索引slaveIndex(int) — 从站索引diffNs(int) — DC 偏差(纳秒)
示例:
master.Events().addDCSyncLostListener((mi, si, d) -> {
System.out.println("从站 " + si + " DC 同步丢失: " + d + "ns");
});
输入数据变化
InputDataChanged
master.Events().addInputDataChangedListener((masterIndex, slaveIndex) -> {
// ...
});
从站输入 PDO 数据变化时触发。仅当输入数据与上一周期不同时回调。
零开销
- 无变化时:不触发回调,零性能开销
- 有变化时:仅变化的从站收到回调,其他从站不受影响
回调参数:
masterIndex(int) — 主站索引slaveIndex(int) — 从站索引
示例:
master.Events().addInputDataChangedListener((mi, si) -> {
Slave slave = master.getSlave(si);
SlavePdo pdo = slave.SlavePdo();
short statusWord = pdo.ReadInputI16(0);
System.out.printf("从站 %d 输入变化, 状态字=0x%04X%n", si, statusWord & 0xFFFF);
});
InputChanged 使用建议
InputDataChanged-- 仅通知"数据变了",需自行读取数据。开销最低。- 结合
SlavePdo类型化读取 -- 在回调中按偏移读取具体数据。 - 两者可以同时使用,底层共享同一个变化检测机制。
清除所有订阅
ClearAll()
slave.Events().ClearAll()
清除从站级别的所有事件订阅,防止内存泄漏。
示例:
slave.Events().ClearAll();
线程安全
事件回调在非 UI 线程上触发。更新 UI 时需要线程同步:
// Swing
SwingUtilities.invokeLater(() -> label.setText("位置: " + position));
// JavaFX
Platform.runLater(() -> label.setText("位置: " + position));
注意
回调中避免执行耗时操作。如需处理大量数据,将数据放入队列异步处理:
// 使用 ConcurrentLinkedQueue 异步处理
ConcurrentLinkedQueue<short[]> dataQueue = new ConcurrentLinkedQueue<>();
master.Events().addInputDataChangedListener((mi, si) -> {
SlavePdo pdo = master.getSlave(si).SlavePdo();
short sw = pdo.ReadInputI16(0);
int pos = pdo.ReadInputI32(2);
dataQueue.add(new short[]{sw, (short)(pos & 0xFFFF)}); // 快速入队
});
完整示例
// ===== 从站状态事件 =====
master.Events().addSlaveStateChangedListener((mi, si, oldState, newState) -> {
System.out.printf("从站 %d 状态: %s -> %s%n", si, oldState, newState);
// 状态异常时检查错误码
if (newState == EcState.SAFE_OP && oldState == EcState.OP) {
Slave slave = master.getSlave(si);
short errorCode = slave.ErrorCode();
if (errorCode != 0) {
EtherCATTypes.ALErrorCategory category =
EtherCATTypes.classifyALError(errorCode);
System.out.printf(" 错误 0x%04X: %s%n", errorCode, category);
}
}
});
master.Events().addEmergencyEventListener((mi, si, ec, er, b, w1, w2) -> {
System.out.printf("从站 %d 紧急消息: 错误码=0x%04X, 寄存器=0x%04X%n", si, ec, er);
});
master.Events().addSlaveOfflineListener((si) -> {
System.out.println("从站 " + si + " 离线(热插拔断开)");
});
master.Events().addSlaveOnlineListener((si) -> {
System.out.println("从站 " + si + " 上线(热插拔恢复)");
});
master.Events().addDCSyncLostListener((mi, si, d) -> {
System.out.printf("从站 %d DC 同步丢失: 偏差 %dns%n", si, d);
});
// ===== 输入数据变化 =====
master.Events().addInputDataChangedListener((mi, si) -> {
// 按从站索引读取最新 PDO 数据
Slave slave = master.getSlave(si);
SlavePdo pdo = slave.SlavePdo();
short statusWord = pdo.ReadInputI16(0);
int position = pdo.ReadInputI32(2);
System.out.printf("从站 %d 输入变化: SW=0x%04X, Pos=%d%n",
si, statusWord & 0xFFFF, position);
});
// ===== PDO 丢帧事件 =====
master.Events().addPDOFrameLossListener((mi, group, consecutive, total) -> {
System.out.printf("组 %d PDO 丢帧: 连续=%d, 累计=%d%n",
group, consecutive, total);
});
// ===== FSoE 安全事件(如果从站支持) =====
Slave slave = master.getSlave(1);
if (slave.FSoE() != null) {
slave.FSoE().addStateChangedListener((oldState, newState) -> {
System.out.printf("FSoE 状态: %s -> %s%n", oldState, newState);
});
}