跳到主要内容

事件

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

自动初始化

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

从站级事件

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

自动日志

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

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

功能概览​

类别事件读写说明自动日志
PDO 周期回调master.Events.ProcessDataCyclicSync只读PDO 周期回调(同步,供 FSoE 等实时控制)—
状态事件master.Events.StateChanged只读主站 EtherCAT 状态变化(含异常降级)✓
master.Events.SlaveStateChanged只读从站 EtherCAT 状态变化✓
热插拔事件master.Events.SlaveOffline只读从站离线(断开)✓
master.Events.SlaveOnline只读从站上线(恢复)✓
master.Events.SlaveIdentityMismatch只读从站身份不符(换成错误型号/低版本)✓
异常事件master.Events.EmergencyEvent只读CoE Emergency 紧急消息✓
master.Events.PDOFrameLoss只读PDO 连续丢帧(按组独立跟踪)✓
master.Events.DCSyncLost只读DC 同步丢失✓
冗余事件master.Events.RedundancyModeChanged只读冗余模式变化(Inactive/Dual/Degraded)✓
master.Events.SlavePortLinkChanged只读从站端口 link 变化(P0-P3 断开/恢复)✓
日志Logs.Updated只读日志数据更新通知—

PDO 周期回调​

PDO 数据通路

PDO 过程数据由内核 RT 收发。金路径是进 OP 后 InputsMapping / OutputsMapping 映射一次,ref 钉在过程映像上,任意线程里直接读字段(输入方向只读,输出方向写字段即发总线;见 PDO 输入输出)。CopyInputPdo / WriteOutputPdo 是快照备选。ProcessDataCyclicSync 用于需要与周期对齐的应用逻辑(含 FSoE);C# 禁止 lambda 捕获 ref 局部(CS8175),回调里要么现场再映射一次(slave.PDO.InputsMapping<T>(),仍是 0 拷贝),要么把控制律放在你自己的循环里直接读写映射——完整写法见 PDO 输入输出 · 周期变化。

ProcessDataCyclicSync​

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

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

注意

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

示例(钉页 0 拷贝;lambda 不能捕获 ref 局部,映射在回调体内各取一次):

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

快照备选(每拍 Copy,拷出来的结构体不会自己变):

byte[] inputFrame = new byte[slave.InputsByteCount];
byte[] outputFrame = new byte[slave.OutputsByteCount];

master.Events.ProcessDataCyclicSync += (masterIndex) =>
{
if (slave.CopyInputPdo(inputFrame, out int copied, out int required) != PdoIoStatus.Success
|| copied != required)
return;

var input = MemoryMarshal.Read<ServoInput>(inputFrame);
var output = new ServoOutput { ControlWord = 0x000F, TargetPosition = input.ActualPosition };
MemoryMarshal.Write(outputFrame, ref output);
slave.WriteOutputPdo(outputFrame, out _, out _);
};

状态事件​

Master.StateChanged​

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

主站 EtherCAT 状态变化时触发(包括主动切换和异常降级)。始终自动记录日志。该事件只反映主站自身的 EcState,与单个从站状态 (slave.Events.StateChanged) 和 FSoE 安全连接状态 (slave.FSoE.StateChanged) 互相独立。

回调参数:

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 状态变化时触发。后台自动监控。始终自动记录日志。

回调参数:

  • 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} 上线");
};

IsSlaveOffline(ushort slaveIndex)​

public bool IsSlaveOffline(ushort slaveIndex)

查询从站是否已被事件系统确认为离线状态。

参数:

  • slaveIndex (ushort) — 从站索引

返回值:

  • bool — 从站离线返回 true

示例:

if (master.Events.IsSlaveOffline(2))
Console.WriteLine("从站 2 处于离线状态");

HasSlaveEnteredOp(ushort slaveIndex)​

public bool HasSlaveEnteredOp(ushort slaveIndex)

查询从站本次运行是否曾进入过 OP(由从站状态变化回调闩锁,进过一次即为 true,之后掉出 OP 仍保持)。没进过 OP 的从站用 slave.ErrorCode 查具体原因。

参数:

  • slaveIndex (ushort) — 从站索引(1-based)

返回值:

  • bool — 曾进入过 OP 返回 true

示例:

if (!master.Events.HasSlaveEnteredOp(3))
Console.WriteLine($"从站 3 未进过 OP: {master.Slaves[2].ErrorCode}");

SlaveIdentityMismatch​

master.Events.SlaveIdentityMismatch += (sender, args) => { };
// EventHandler<SlaveIdentityMismatchEventArgs>

从站断电重插后身份不符时触发:EtherCAT 识别状态机读取到的 Vendor/Product 与配置不匹配,或 Revision 低于配置(向后兼容:实际 Revision ≥ 配置值视为匹配)。始终自动记录日志。

触发后从站进入 IDENT_REJECTED 状态,不会自动重探测(防止错设备反复刷屏)。操作员检查/更换设备后需调用 master.AcknowledgeSlaveReplacement 让 SDK 重新探测。

回调参数 (SlaveIdentityMismatchEventArgs):

public class SlaveIdentityMismatchEventArgs
{
public ushort MasterIndex { get; } // 主站索引
public ushort SlaveIndex { get; } // 从站索引(1-based)
public uint ExpectedVendor { get; } // 配置期望的厂商 ID
public uint ExpectedProduct { get; } // 配置期望的产品代码
public uint ExpectedRevision { get; } // 配置期望的最低修订号
public uint ActualVendor { get; } // 当前实际厂商 ID
public uint ActualProduct { get; } // 当前实际产品代码
public uint ActualRevision { get; } // 当前实际修订号
}

示例:

master.Events.SlaveIdentityMismatch += (sender, args) =>
{
Console.WriteLine($"从站 {args.SlaveIndex} 身份不符:");
Console.WriteLine($" 期望: Vendor=0x{args.ExpectedVendor:X8}, Product=0x{args.ExpectedProduct:X8}, Rev>=0x{args.ExpectedRevision:X8}");
Console.WriteLine($" 实际: Vendor=0x{args.ActualVendor:X8}, Product=0x{args.ActualProduct:X8}, Rev=0x{args.ActualRevision:X8}");
// UI 弹窗提示用户换回正确设备, 确认后调用 master.AcknowledgeSlaveReplacement(args.SlaveIndex)
};
去重机制

同一从站进入 IDENT_REJECTED 状态仅触发一次事件。调用 AcknowledgeSlaveReplacement 后重置探测,身份仍不匹配会再次触发。

异常事件​

周期异常必须报警

周期按 DENI/LoopCycle 设定,最短 125µs,不是写死 1ms。Diagnostics.RTcnt 应对齐 1e9/LoopCycleNs。连续偏离设定 ±20%,或 max jitter 超过周期的 25%,必须写日志并在 UI 报警。同时核对 PDOFrameLoss、DCSyncLost。隔离未生效只允许抖动变大,不允许周期漂走还不报。不要在 PDO 热路径里做报警 I/O。

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.IsInSync 和 slave.Diagnostics.DC.SyncTimeDifference 可查询从站当前同步状态。详见 从站诊断 - DC 同步。

冗余事件​

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}");
};

SlavePortLinkChanged​

master.Events.SlavePortLinkChanged += (masterIndex, slaveIndex, port, isUp) => { };
// delegate void SlavePortLinkChangedEventHandler(ushort masterIndex, ushort slaveIndex, byte port, bool isUp);

从站 ESC 端口物理链路变化时触发(P0-P3 之一断开或恢复)。每秒诊断周期检测 DL Status 寄存器的 link bit,从 1→0 触发"断开",从 0→1 触发"恢复"。始终自动记录日志。

用于精确定位故障线缆段(相邻从站的对向端口同时报断,说明中间线缆有问题)。

回调参数:

  • masterIndex (ushort) — 主站索引
  • slaveIndex (ushort) — 从站索引(1-based)
  • port (byte) — 端口号 0-3,对应 P0 / P1 / P2 / P3
  • isUp (bool) — true=link 恢复, false=link 断开

示例:

master.Events.SlavePortLinkChanged += (masterIndex, slaveIndex, port, isUp) =>
{
string state = isUp ? "恢复" : "断开";
Console.WriteLine($"从站 {slaveIndex} 端口 P{port} link {state}");
};
配合断点定位

master.Diagnostics.BreakPoint / GetAllBreakPoints() 提供聚合后的故障点视图(断线 + CRC 故障)。SlavePortLinkChanged 是原始事件源,适合做实时告警。详见 主站诊断 - 冗余状态。

清除事件订阅​

Events.ClearAll()​

public void ClearAll()

清除所有主站级事件订阅,防止内存泄漏。在销毁主站或重新初始化前调用。

示例:

// 销毁前清除所有事件
master.Events.ClearAll();
master.Close();
从站级事件

从站也有 slave.Events.ClearAll() 方法,清除该从站的所有事件订阅。

线程安全​

事件回调在非 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.SlavePortLinkChanged += (masterIndex, slaveIndex, port, isUp) =>
Console.WriteLine($"从站 {slaveIndex} P{port} link {(isUp ? "恢复" : "断开")}");

// ===== 热插拔身份不符 =====
master.Events.SlaveIdentityMismatch += (sender, args) =>
Console.WriteLine($"从站 {args.SlaveIndex} 身份不符, 换设备后调用 AcknowledgeSlaveReplacement({args.SlaveIndex})");

// ===== PDO 周期回调(同步,供 FSoE 等实时控制;PDO 数据通常直接轮询过程映像指针)=====
master.Events.ProcessDataCyclicSync += (masterIndex) =>
{
// 当周期实时控制
};