事件
通过 master.Events 订阅所有主站级事件。
所有事件回调和日志系统在 Build() 时自动注册,无需手动调用。
从站 PDO 数据变化通知、FSoE 安全事件请参考 从站事件。
所有事件(PDO 周期回调除外)触发时系统均自动记录日志,无论是否订阅。确保关键运行状态不会被静默丢失。
PDO 周期回调(ProcessDataCyclicAsync、ProcessDataCyclicSync)为高频回调,不记录日志。
功能概览
| 类别 | 事件 | 说明 | 自动日志 |
|---|---|---|---|
| PDO 周期回调 | master.Events.ProcessDataCyclicAsync | PDO 周期回调(异步,推荐) | — |
| master.Events.ProcessDataCyclicSync | PDO 周期回调(同步,慎用) | — | |
| 状态事件 | master.Events.StateChanged | 主站 EtherCAT 状态变化(含异常降级) | ✓ |
| master.Events.SlaveStateChanged | 从站 EtherCAT 状态变化 | ✓ | |
| 热插拔事件 | master.Events.SlaveOffline | 从站离线(断开) | ✓ |
| master.Events.SlaveOnline | 从站上线(恢复) | ✓ | |
| 异常事件 | master.Events.EmergencyEvent | CoE Emergency 紧急消息 | ✓ |
| master.Events.PDOFrameLoss | PDO 连续丢帧(按组独立跟踪) | ✓ | |
| master.Events.DCSyncLost | DC 同步丢失 | ✓ | |
| 输入数据变化 | 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}");
};
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");
};
slave.Diagnostics.DC.IsInSync 和 slave.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变化");
});
InputDataChanged 在 ProcessDataCyclicSync/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 数据
};