跳到主要内容

从站事件

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

事件路由

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

协议专属事件

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

功能概览

类别事件说明
从站状态SlaveStateChanged从站 EtherCAT 状态变化
EmergencyEventCoE Emergency 紧急消息
SlaveOffline从站离线(热插拔断开)
SlaveOnline从站上线(热插拔恢复)
DCSyncLostDC 同步丢失
输入数据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);
});
}