从站事件
通过 master.Events() 的主站级事件按从站索引过滤。
事件路由
从站事件由主站事件系统自动路由,无需手动注册 DLL 回调。订阅主站级事件 master.Events().add*Listener() 后,对应从站的事件会自动触发。
协议专属事件
FSoE 安全事件通过 slave.FSoE() 订阅,请参考 FSoE。
功能概览
| 类别 | 事件 | 说明 |
|---|---|---|
| 从站状态 | SlaveStateChanged | 从站 EtherCAT 状态变化 |
| EmergencyEvent | CoE Emergency 紧急消息 | |
| SlaveOffline | 从站离线(热插拔断开) | |
| SlaveOnline | 从站上线(热插拔恢复) | |
| DCSyncLost | DC 同步丢失 |
输入数据变化事件已移除(2026-05-21 架构变更)
InputDataChanged 输入 PDO 数据变化事件已移除。PDO 数据通路改为纯内核共享内存指针轮询
(零拷贝),底层不再每周期做 memcmp 检测变化。需感知输入变化请自行轮询过程映像指针
并与上一周期快照比对。
从站状态事件
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");
});
输入数据变化(已移除)
2026-05-21 架构变更
InputDataChanged 输入数据变化事件已移除。该事件原靠底层每周期对输入 PDO 做
memcmp 检测变化;PDO 数据通路改为纯内核共享内存指针轮询(零拷贝)后,该机制不再驱动。
如何感知输入变化: 自行起一个应用线程周期轮询过程映像指针,与上一周期快照比对。
short[] prev = null;
while (running) {
SlavePdo pdo = master.getSlave(1).SlavePdo();
short statusWord = pdo.ReadInputI16(0);
int position = pdo.ReadInputI32(2);
short[] cur = { statusWord, (short) (position & 0xFFFF) };
if (prev != null && (prev[0] != cur[0] || prev[1] != cur[1])) {
System.out.printf("从站 1 输入变化: SW=0x%04X, Pos=%d%n",
statusWord & 0xFFFF, position);
}
prev = cur;
Thread.sleep(1);
}
清除所有订阅
ClearAll()
slave.Events().ClearAll()
清除从站级别的所有事件订阅,防止内存泄漏。
示例:
slave.Events().ClearAll();
线程安全
事件回调在非 UI 线程上触发。更新 UI 时需要线程同步:
// Swing
SwingUtilities.invokeLater(() -> label.setText("位置: " + position));
// JavaFX
Platform.runLater(() -> label.setText("位置: " + position));
注意
事件回调中避免执行耗时操作。如需处理大量数据,将数据放入队列异步处理:
// 使用 ConcurrentLinkedQueue 异步处理 (EMCY / 状态变化等事件回调)
ConcurrentLinkedQueue<short[]> dataQueue = new ConcurrentLinkedQueue<>();
master.Events().addEmergencyEventListener((mi, si, ec, er, b, w1, w2) -> {
dataQueue.add(new short[]{(short) si, (short) ec}); // 快速入队
});
完整示例
// ===== 从站状态事件 =====
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);
EtherCATTypes.EcALState errorCode = slave.ErrorCode();
if (errorCode != EtherCATTypes.EcALState.NO_ERROR) {
EtherCATTypes.ALErrorCategory category =
EtherCATTypes.classifyALError(errorCode.value);
System.out.printf(" 错误 %s (0x%04X): %s%n",
errorCode, errorCode.value, 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);
});
// ===== 输入数据: 自起线程轮询过程映像 (PDO 已纯内核化, 零拷贝指针) =====
new Thread(() -> {
while (running) {
SlavePdo pdo = master.getSlave(1).SlavePdo();
short statusWord = pdo.ReadInputI16(0);
int position = pdo.ReadInputI32(2);
// 自行与上一周期快照比对判断变化
try { Thread.sleep(1); } catch (InterruptedException e) { break; }
}
}).start();
// ===== PDO 丢帧事件 =====
master.Events().addPDOFrameLossListener((mi, group, consecutive, total) -> {
System.out.printf("组 %d PDO 丢帧: 连续=%d, 累计=%d%n",
group, consecutive, total);
});
// ===== FSoE 安全事件(如果从站支持) =====
// slave.FSoE() 子对象延迟创建, 始终返回实例 (不会为 null);
// 是否支持 FSoE 用 IsCapable() 判断, 不要做 != null 检查
Slave slave = master.getSlave(1);
if (slave.FSoE().IsCapable()) {
// FSoE 周期数据监听 (DataExchanged 是当前唯一开放的事件入口)
slave.FSoE().addDataExchangedListener(ev -> {
System.out.printf("FSoE 周期 %d: 状态=%s, 失效安全=%b%n",
ev.CycleCount, ev.CurrentState, ev.InFailsafe);
});
}