跳到主要内容

从站事件

通过 master.Events() 的主站级事件按从站索引过滤。​

事件路由

从站事件由主站事件系统自动路由,无需手动注册 DLL 回调。订阅主站级事件 master.Events().add*Listener() 后,对应从站的事件会自动触发。

协议专属事件

FSoE 安全事件通过 slave.FSoE() 订阅,请参考 FSoE。

功能概览​

类别事件注册方法读写说明
从站状态SlaveStateChangedaddSlaveStateChangedListener只写从站 EtherCAT 状态变化
EmergencyEventaddEmergencyEventListener只写CoE Emergency 紧急消息
SlaveOfflineaddSlaveOfflineListener只写从站离线(热插拔断开)
SlaveOnlineaddSlaveOnlineListener只写从站上线(热插拔恢复)
DCSyncLostaddDCSyncLostListener只写DC 同步丢失
输入数据变化事件已移除(2026-05-21 架构变更)

InputDataChanged 输入 PDO 数据变化事件已移除。底层不再每周期做 memcmp。需感知输入变化请自行 copyInputPdo(或 SlavePdo.Inputs() 快照)并与上一周期比对。

从站状态事件​

SlaveStateChanged​

master.Events().addSlaveStateChangedListener((masterIndex, slaveIndex, oldState, newState) -> {
// ...
});

从站 EtherCAT 状态变化时触发。

回调参数:(接口 MasterEvents.SlaveStateChangedListener.onSlaveStateChanged)

  • masterIndex (short) — 主站索引
  • slaveIndex (short) — 从站索引
  • 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 紧急消息。

回调参数:(接口 MasterEvents.EmergencyEventListener.onEmergency)

  • masterIndex (short) — 主站索引
  • slaveIndex (short) — 从站索引
  • errorCode (short) — 错误代码(CANopen Emergency Error Code)
  • errorReg (short) — 错误寄存器(对象 0x1001)
  • b1 (byte) — 制造商特定数据(字节 3)
  • w1 (short) — 制造商特定数据(字节 4-5)
  • w2 (short) — 制造商特定数据(字节 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) -> {
// ...
});

从站离线(热插拔断开)。

回调参数:(接口 MasterEvents.SlaveOfflineListener.onSlaveOffline)

  • slaveIndex (short) — 从站索引

示例:

master.Events().addSlaveOfflineListener((si) -> {
System.out.println("从站 " + si + " 离线");
});

SlaveOnline​

master.Events().addSlaveOnlineListener((slaveIndex) -> {
// ...
});

从站上线(热插拔恢复)。

回调参数:(接口 MasterEvents.SlaveOnlineListener.onSlaveOnline)

  • slaveIndex (short) — 从站索引

示例:

master.Events().addSlaveOnlineListener((si) -> {
System.out.println("从站 " + si + " 上线");
});

DCSyncLost​

master.Events().addDCSyncLostListener((masterIndex, slaveIndex, diffNs) -> {
// ...
});

DC 同步丢失。

回调参数:(接口 MasterEvents.DCSyncLostListener.onDCSyncLost)

  • masterIndex (short) — 主站索引
  • slaveIndex (short) — 从站索引
  • diffNs (int) — DC 偏差(纳秒)

示例:

master.Events().addDCSyncLostListener((mi, si, d) -> {
System.out.println("从站 " + si + " DC 同步丢失: " + d + "ns");
});

输入数据变化(已移除)​

2026-05-21 架构变更

InputDataChanged 输入数据变化事件已移除。该事件原靠底层每周期对输入 PDO 做 memcmp 检测变化。

如何感知输入变化: 自行起一个应用线程周期调用 copyInputPdo(或 SlavePdo.Inputs() 快照),与上一周期比对。

byte[] prev = null;
while (running) {
byte[] cur = master.getSlave(1).SlavePdo().Inputs(); // 本周期输入整帧快照
if (prev != null && !java.util.Arrays.equals(prev, cur)) {
System.out.println("从站 1 输入变化: " + java.util.Arrays.toString(cur));
}
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);
});

// ===== 输入数据: 自起线程轮询 SlavePdo 快照 =====
new Thread(() -> {
byte[] prev = null;
while (running) {
byte[] cur = master.getSlave(1).SlavePdo().Inputs();
if (prev != null && !java.util.Arrays.equals(prev, cur)) {
System.out.println("从站 1 输入变化: " + java.util.Arrays.toString(cur));
}
prev = cur;
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);
});
}