从站事件
从站级别的事件,用于监控从站状态、PDO 数据变化和协议状态。
事件自动路由
主站级 DLL 回调会自动路由到对应从站的 slave.Events。订阅从站事件无需 masterIndex/slaveIndex 参数,系统已自动过滤到当前从站。
协议专属事件
FSoE 安全事件通过 slave.FSoE 订阅,请参考 FSoE。
功能概览
| 类别 | 事件 / 方法 | 说明 |
|---|---|---|
| 从站状态事件 | slave.Events.StateChanged | 从站 EtherCAT 状态变化 |
| slave.Events.Emergency | CoE Emergency 紧急消息 | |
| slave.Events.Offline | 从站离线(热插拔断开) | |
| slave.Events.Online | 从站上线(热插拔恢复) | |
| slave.Events.DCSyncLost | DC 同步丢失 | |
| 输入数据变化 | slave.Events.InputChanged | 输入 PDO 数据变化(原生检测) |
| PDO 数据变化 | slave.PDO.InputsMapping<T>(onChanged) | 输入 PDO 结构体变化回调(自动) |
| slave.PDO.BindPdoStruct<T>().Changed | PDO 结构体绑定事件 |
从站状态事件
通过 slave.Events 访问。事件参数已过滤到当前从站,签名简洁。
StateChanged
slave.Events.StateChanged += (oldState, newState) => { };
// Action<EcState, EcState>
从站 EtherCAT 状态变化时触发。由 master.Events.SlaveStateChanged 自动路由。
示例:
slave.Events.StateChanged += (oldState, newState) =>
{
Console.WriteLine($"状态变化: {oldState} → {newState}");
};
Emergency
slave.Events.Emergency += (errorCode, errorReg, b1, w1, w2) => { };
// Action<ushort, ushort, byte, ushort, ushort>
CoE Emergency 紧急消息。由 master.Events.EmergencyEvent 自动路由。
回调参数:
// Action<ushort, ushort, byte, ushort, ushort>
errorCode, // ushort — 错误代码(CANopen Emergency Error Code)
errorReg, // ushort — 错误寄存器(对象 0x1001)
b1, // byte — 制造商特定数据(字节 3)
w1, // ushort — 制造商特定数据(字节 4-5)
w2 // ushort — 制造商特定数据(字节 6-7)
示例:
slave.Events.Emergency += (errorCode, errorReg, b1, w1, w2) =>
{
Console.WriteLine($"紧急消息: 错误码=0x{errorCode:X4}");
};
Offline
slave.Events.Offline += () => { };
// Action
从站离线(热插拔断开)。由 master.Events.SlaveOffline 自动路由。
示例:
slave.Events.Offline += () =>
{
Console.WriteLine("从站离线");
};
Online
slave.Events.Online += () => { };
// Action
从站上线(热插拔恢复)。由 master.Events.SlaveOnline 自动路由。
示例:
slave.Events.Online += () =>
{
Console.WriteLine("从站上线");
};
DCSyncLost
slave.Events.DCSyncLost += (diffNs) => { };
// Action<int>
DC 同步丢失。由 master.Events.DCSyncLost 自动路由。
示例:
slave.Events.DCSyncLost += (diffNs) =>
{
Console.WriteLine($"DC 同步丢失: 偏差 {diffNs}ns");
};
输入数据变化
InputChanged
slave.Events.InputChanged += () => { };
// Action
从站输入 PDO 数据变化时触发。仅当输入数据与上一周期不同时回调。由 master.Events.InputDataChanged 自动路由。
零开销
- 无变化时:不触发回调,零性能开销
- 有变化时:仅变化的从站收到回调,其他从站不受影响
示例:
slave.Events.InputChanged += () =>
{
ref var input = ref slave.PDO.InputsMapping<ServoInput>();
Console.WriteLine($"位置变化: {input.ActualPosition}");
};
与 InputsMapping 回调的区别
InputChanged: 仅通知"数据变了",需自行读取数据。开销最低。InputsMapping<T>(onChanged): 自动提供变化前后的结构体值。开销略高,但更方便。- 两者可以同时使用,底层共享同一个变化检测机制。
PDO 数据变化
InputsMapping 回调
使用 slave.PDO.InputsMapping<T>(onChanged) 订阅输入 PDO 数据变化。DLL 层检测到输入数据变化时自动触发结构体比较。
回调参数:
public class PdoStructChangedEventArgs<T>
{
public ushort SlaveIndex { get; } // 从站索引
public bool IsInput { get; } // 是否为输入 PDO
public T Previous { get; } // 变化前的结构体值
public T Current { get; } // 当前结构体值
public DateTime Timestamp { get; } // 变化时间戳
}
示例:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoInput
{
public ushort StatusWord;
public int ActualPosition;
public int ActualVelocity;
}
var inputInstance = slave.PDO.InputsMapping<ServoInput>(e =>
{
Console.WriteLine($"位置变化: {e.Previous.ActualPosition} -> {e.Current.ActualPosition}");
});
PdoStructBinding 事件
使用 slave.PDO.BindPdoStruct<T>() 创建绑定并订阅 Changed 事件。使用完毕后需调用 Dispose() 释放。
事件参数:
public class PdoStructChangedEventArgs<T>
{
public ushort SlaveIndex { get; } // 从站索引
public bool IsInput { get; } // 是否为输入 PDO
public T Previous { get; } // 变化前的结构体值
public T Current { get; } // 当前结构体值
public DateTime Timestamp { get; } // 变化时间戳
}
示例:
var pdoManager = new PDOManager(master.MasterNumber);
var binding = slave.PDO.BindPdoStruct<ServoInput>(pdoManager, isInput: true);
binding.Changed += (sender, e) =>
{
Console.WriteLine($"状态字=0x{e.Current.StatusWord:X4}, 位置={e.Current.ActualPosition}");
};
binding.Dispose();
线程安全
事件回调在非 UI 线程上触发。更新 UI 时需要线程同步:
// WinForms
this.Invoke(() => labelValue.Text = $"位置: {e.Current.ActualPosition}");
// WPF
Dispatcher.Invoke(() => PositionText = $"位置: {e.Current.ActualPosition}");
注意
回调中避免执行耗时操作。如需处理大量数据,将数据放入队列异步处理:
slave.PDO.InputsMapping<ServoInput>(e =>
{
_dataQueue.Enqueue(e.Current); // 快速入队
});
完整示例
var slave = master.Slaves[0];
// ===== 从站状态事件 =====
slave.Events.StateChanged += (oldState, newState) =>
Console.WriteLine($"状态: {oldState} → {newState}");
slave.Events.Emergency += (errorCode, errorReg, b1, w1, w2) =>
Console.WriteLine($"紧急消息: 0x{errorCode:X4}");
slave.Events.Offline += () =>
Console.WriteLine("从站离线");
slave.Events.Online += () =>
Console.WriteLine("从站上线");
slave.Events.DCSyncLost += (diffNs) =>
Console.WriteLine($"DC 同步丢失: {diffNs}ns");
// ===== 输入数据变化 =====
slave.Events.InputChanged += () =>
Console.WriteLine("输入数据变化");
// ===== PDO 数据变化(自动结构体比较) =====
var inputInstance = slave.PDO.InputsMapping<ServoInput>(e =>
{
Console.WriteLine($"位置: {e.Current.ActualPosition}");
});
// ===== FSoE 安全事件(协议专属) =====
if (slave.FSoE != null)
{
slave.FSoE.StateChanged += (sender, e) =>
Console.WriteLine($"FSoE: {e.OldState} → {e.NewState}");
}