跳到主要内容

事件

通过 master.Events 订阅所有主站级事件。

自动初始化

所有事件回调和日志系统在 Build() 时自动注册,无需手动调用。

从站级事件

从站 PDO 数据变化通知、FSoE 安全事件请参考 从站事件

自动日志

所有事件(PDO 周期回调除外)触发时系统均自动记录日志,无论是否订阅。确保关键运行状态不会被静默丢失。

PDO 周期回调(ProcessDataCyclicAsyncProcessDataCyclicSync)为高频回调,不记录日志。

功能概览

类别事件说明自动日志
PDO 周期回调master.Events.ProcessDataCyclicAsyncPDO 周期回调(异步,推荐)
master.Events.ProcessDataCyclicSyncPDO 周期回调(同步,慎用)
状态事件master.Events.StateChanged主站 EtherCAT 状态变化(含异常降级)
master.Events.SlaveStateChanged从站 EtherCAT 状态变化
热插拔事件master.Events.SlaveOffline从站离线(断开)
master.Events.SlaveOnline从站上线(恢复)
异常事件master.Events.EmergencyEventCoE Emergency 紧急消息
master.Events.PDOFrameLossPDO 连续丢帧(按组独立跟踪)
master.Events.DCSyncLostDC 同步丢失
输入数据变化master.Events.InputDataChanged从站输入PDO数据变化(原生检测)
冗余事件master.Events.RedundancyModeChanged冗余模式变化(Inactive/Dual/Degraded)
日志Logs.Updated日志数据更新通知

PDO 周期回调

ProcessDataCyclicAsync

public event ProcessDataCyclicAsyncEventHandler? ProcessDataCyclicAsync;
// delegate void ProcessDataCyclicAsyncEventHandler(ushort masterIndex);

每个 PDO 周期触发一次(异步模式)。回调不阻塞实时 PDO 线程,适合大多数场景。

示例:

master.Events.ProcessDataCyclicAsync += (masterIndex) =>
{
ushort status = slave.CoE[0x6041][0].Value;
};

ProcessDataCyclicSync

public event ProcessDataCyclicSyncEventHandler? ProcessDataCyclicSync;
// delegate void ProcessDataCyclicSyncEventHandler(ushort masterIndex);

每个 PDO 周期触发一次(同步模式)。回调在实时线程中直接执行,延迟最低但回调必须快速返回。

注意

同步回调中不要执行耗时操作(如 SDO 读写、文件 I/O、锁等待),否则会阻塞实时线程导致丢帧。

示例:

master.Events.ProcessDataCyclicSync += (masterIndex) =>
{
ref var input = ref slave.PDO.InputsMapping<ServoInput>();
ref var output = ref slave.PDO.OutputsMapping<ServoOutput>();
output.ControlWord = 0x000F;
};

状态事件

StateChanged

master.Events.StateChanged += (sender, e) => { };
// EventHandler<StateChangedEventArgs>

主站 EtherCAT 状态变化时触发(包括主动切换和异常降级)。始终自动记录日志。

回调参数:

public class StateChangedEventArgs
{
public EcState OldState { get; } // 变化前的状态
public EcState NewState { get; } // 变化后的状态
}

示例:

master.Events.StateChanged += (sender, e) =>
{
Console.WriteLine($"主站状态: {e.OldState}{e.NewState}");
};

SlaveStateChanged

master.Events.SlaveStateChanged += (masterIndex, slaveIndex, oldState, newState) => { };
// delegate void SlaveStateChangeEventHandler(ushort masterIndex, ushort slaveIndex, EcState oldState, EcState newState);

从站 EtherCAT 状态变化时触发。由 DLL 后台线程监控并回调。始终自动记录日志。

回调参数:

  • masterIndex (ushort) — 主站索引
  • slaveIndex (ushort) — 从站索引(1-based)
  • oldState (EcState) — 变化前的状态
  • newState (EcState) — 变化后的状态

示例:

master.Events.SlaveStateChanged += (masterIndex, slaveIndex, oldState, newState) =>
{
Console.WriteLine($"从站 {slaveIndex}: {oldState}{newState}");

var slave = master.Slaves[slaveIndex - 1];
if (slave.ErrorCode != EcALState.NoError)
Console.WriteLine($" 错误码: {slave.ErrorCode}");
};
ErrorCode

slave.ErrorCode 读取 AL Status Code,返回状态切换失败的具体原因。详见 属性与状态机

热插拔事件

SlaveOffline

master.Events.SlaveOffline += (slaveIndex) => { };
// delegate void SlaveOfflineEventHandler(ushort slaveIndex);

从站离线事件。热插拔断开时触发,PDO 线程内置的恢复状态机会自动恢复从站(零 PDO 影响)。始终自动记录日志。

示例:

master.Events.SlaveOffline += (slaveIndex) =>
{
Console.WriteLine($"从站 {slaveIndex} 离线");
};

SlaveOnline

master.Events.SlaveOnline += (slaveIndex) => { };
// delegate void SlaveOnlineEventHandler(ushort slaveIndex);

从站上线事件。热插拔恢复时触发。始终自动记录日志。

示例:

master.Events.SlaveOnline += (slaveIndex) =>
{
Console.WriteLine($"从站 {slaveIndex} 上线");
};

异常事件

EmergencyEvent

master.Events.EmergencyEvent += (masterIndex, slaveIndex, errorCode, errorReg, b1, w1, w2) => { };
// delegate void EmergencyEventHandler(ushort masterIndex, ushort slaveIndex,
// ushort errorCode, ushort errorReg, byte b1, ushort w1, ushort w2);

CoE Emergency 紧急消息事件。从站固件检测到错误时发送,数据格式遵循 CANopen Emergency 协议(CiA 301)。始终自动记录日志。

回调参数:

  • masterIndex (ushort) — 主站索引
  • slaveIndex (ushort) — 从站索引(1-based)
  • errorCode (ushort) — 错误代码(CANopen Emergency Error Code,对象 0x603F)
  • errorReg (ushort) — 错误寄存器(对象 0x1001)
  • b1 (byte) — 制造商特定数据(字节 3)
  • w1 (ushort) — 制造商特定数据(字节 4-5)
  • w2 (ushort) — 制造商特定数据(字节 6-7)

相关结构:

常见 Emergency Error Code:

  • 0x0000 — 错误已复位
  • 0x1000 — 通用错误
  • 0x2000 — 电流错误
  • 0x3000 — 电压错误
  • 0x4000 — 温度错误
  • 0x5000 — 设备硬件错误
  • 0x6000 — 设备软件错误
  • 0x7000 — 附加模块错误
  • 0x8000 — 监控错误(通信)
  • 0xFF00 — 制造商特定错误

示例:

master.Events.EmergencyEvent += (masterIndex, slaveIndex, errorCode, errorReg, b1, w1, w2) =>
{
Console.WriteLine($"从站 {slaveIndex} 紧急消息: 错误码=0x{errorCode:X4}, 寄存器=0x{errorReg:X2}");
};

PDOFrameLoss

master.Events.PDOFrameLoss += (masterIndex, group, consecutiveLostCount, totalLostCount) => { };
// delegate void PDOFrameLossEventHandler(ushort masterIndex, byte group,
// uint consecutiveLostCount, uint totalLostCount);

PDO 连续丢帧事件。当连续丢帧达到阈值时触发,单次丢帧仅计数不触发。每组独立跟踪。始终自动记录日志。

回调参数:

  • masterIndex (ushort) — 主站索引
  • group (byte) — 发生丢帧的组号(0-7),对应从站分组
  • consecutiveLostCount (uint) — 该组连续丢帧数
  • totalLostCount (uint) — 该组累计丢帧数

示例:

master.Events.PDOFrameLoss += (masterIndex, group, consecutive, total) =>
{
Console.WriteLine($"组 {group} 丢帧: 连续={consecutive}, 累计={total}");
};
丢帧统计

PDO 丢帧的详细统计(按组查询)请参考 主站诊断 - PDO 丢帧

DCSyncLost

master.Events.DCSyncLost += (masterIndex, slaveIndex, diffNs) => { };
// delegate void DCSyncLostEventHandler(ushort masterIndex, ushort slaveIndex, int diffNs);

DC 同步丢失事件。当从站从同步→失同步(超出阈值)时触发一次,持续超出不重复触发。始终自动记录日志。

回调参数:

  • masterIndex (ushort) — 主站索引
  • slaveIndex (ushort) — 失同步的从站索引(1-based)
  • diffNs (int) — 当前与参考时钟的时间差(纳秒)

示例:

master.Diagnostics.SyncWindowThreshold = 500;  // 500ns

master.Events.DCSyncLost += (masterIndex, slaveIndex, diffNs) =>
{
Console.WriteLine($"从站 {slaveIndex} DC 同步丢失: 偏差 {diffNs}ns");
};
单个从站 DC 诊断

slave.Diagnostics.DC.IsInSyncslave.Diagnostics.DC.SyncTimeDifference 可查询从站当前同步状态。详见 从站诊断 - DC 同步

输入数据变化事件

InputDataChanged

master.Events.InputDataChanged += (masterIndex, slaveIndex) => { };
// delegate void InputDataChangedEventHandler(ushort masterIndex, ushort slaveIndex);

从站输入 PDO 数据变化时触发。仅当从站输入数据与上一周期不同时回调。

零开销设计
  • 无变化时: 不触发回调,零开销
  • 有变化时: 仅触发变化从站的回调
  • 检测精度: 逐字节比较,任何一个 bit 变化都能检测到

回调参数:

  • masterIndex (ushort) — 主站索引
  • slaveIndex (ushort) — 输入数据发生变化的从站索引(1-based)

示例:

master.Events.InputDataChanged += (masterIndex, slaveIndex) =>
{
var slave = master.Slaves[slaveIndex - 1];
ref var input = ref slave.PDO.InputsMapping<ServoInput>();
Console.WriteLine($"从站 {slaveIndex} 输入变化: StatusWord=0x{input.StatusWord:X4}");
};

使用方式对比

方式API适用场景
从站事件slave.Events.InputChanged仅需知道"变了",自行读取数据
主站事件master.Events.InputDataChanged统一监控所有从站变化
结构体绑定slave.PDO.InputsMapping<T>(onChanged)自动获取变化前后的结构体值
周期回调master.Events.ProcessDataCyclicSync每周期都需要处理数据

从站级事件

// 仅监控特定从站的输入变化
slave.Events.InputChanged += () =>
{
ref var input = ref slave.PDO.InputsMapping<MyInput>();
// 处理变化的数据...
};

自动结构体变化检测

// 自动检测结构体变化,回调中直接获取前后值
var instance = slave.PDO.InputsMapping<ServoInput>(e =>
{
Console.WriteLine($"StatusWord: 0x{e.Previous.StatusWord:X4} → 0x{e.Current.StatusWord:X4}");
Console.WriteLine($"Position: {e.Previous.ActualPosition}{e.Current.ActualPosition}");
});

// 仍可零拷贝访问当前值
ref var current = ref instance.Value;

MDP 模块化设备(偏移映射)

// MDP 设备的各模块 PDO 在 IOmap 中有不同偏移
var module1 = slave.PDO.InputsSliceMapping<Module1Input>(offset: 0, e =>
{
Console.WriteLine($"模块1变化");
});

var module2 = slave.PDO.InputsSliceMapping<Module2Input>(offset: 8, e =>
{
Console.WriteLine($"模块2变化");
});
与 ProcessDataCyclic 的关系

InputDataChangedProcessDataCyclicSync/Async 之前触发。这意味着在 Sync 回调中可以确信变化事件已经分发完毕。两者可以同时使用,互不影响。

冗余事件

RedundancyModeChanged

master.Events.RedundancyModeChanged += (masterIndex, oldMode, newMode) => { };
// delegate void RedundancyModeChangedEventHandler(ushort masterIndex, int oldMode, int newMode);

冗余运行模式发生变化时触发。oldMode / newMode 对应 RingMode 枚举值(0=Inactive, 1=Dual, 2=Degraded)。

示例:

master.Events.RedundancyModeChanged += (masterIndex, oldMode, newMode) =>
{
Console.WriteLine($"冗余模式: {oldMode}{newMode}");
};

线程安全

事件回调在非 UI 线程上触发。更新 UI 时需要线程同步:

// WinForms
master.Events.SlaveOffline += (idx) =>
this.Invoke(() => labelStatus.Text = $"从站 {idx} 离线");

// WPF
master.Events.SlaveOffline += (idx) =>
Dispatcher.Invoke(() => StatusText = $"从站 {idx} 离线");

完整示例

// ===== 主站状态 =====
master.Events.StateChanged += (sender, e) =>
Console.WriteLine($"主站: {e.OldState}{e.NewState}");

//从站状态
master.Events.SlaveStateChanged += (masterIndex, slaveIndex, oldState, newState) =>
{
var slave = master.Slaves[slaveIndex - 1];
Console.WriteLine($"从站 {slaveIndex}: {oldState}{newState}");
if (slave.ErrorCode != EcALState.NoError)
Console.WriteLine($" 错误码: {slave.ErrorCode}");
};

// ===== 热插拔 =====
master.Events.SlaveOffline += (slaveIndex) =>
Console.WriteLine($"从站 {slaveIndex} 离线");
master.Events.SlaveOnline += (slaveIndex) =>
Console.WriteLine($"从站 {slaveIndex} 上线");

// ===== 异常事件 =====
master.Events.EmergencyEvent += (masterIndex, slaveIndex, errorCode, errorReg, b1, w1, w2) =>
Console.WriteLine($"从站 {slaveIndex} 紧急消息: 0x{errorCode:X4}");

master.Events.PDOFrameLoss += (masterIndex, group, consecutive, total) =>
Console.WriteLine($"组 {group} 丢帧: 连续={consecutive}, 累计={total}");

master.Events.DCSyncLost += (masterIndex, slaveIndex, diffNs) =>
Console.WriteLine($"从站 {slaveIndex} DC 同步丢失: {diffNs}ns");

// ===== 冗余事件 =====
master.Events.RedundancyModeChanged += (masterIndex, oldMode, newMode) =>
Console.WriteLine($"冗余模式变化: {oldMode}{newMode}");

// ===== 输入数据变化 =====
master.Events.InputDataChanged += (masterIndex, slaveIndex) =>
Console.WriteLine($"从站 {slaveIndex} 输入数据变化");

// ===== PDO 周期回调 =====
master.Events.ProcessDataCyclicAsync += (masterIndex) =>
{
// 读写 PDO 数据
};