跳到主要内容

从站事件

从站级别的事件,用于监控从站状态、PDO 数据变化和协议状态。

事件自动路由

主站级 DLL 回调会自动路由到对应从站的 slave.Events。订阅从站事件无需 masterIndex/slaveIndex 参数,系统已自动过滤到当前从站。

协议专属事件

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

功能概览

类别事件 / 方法说明
从站状态事件slave.Events.StateChanged从站 EtherCAT 状态变化
slave.Events.EmergencyCoE Emergency 紧急消息
slave.Events.Offline从站离线(热插拔断开)
slave.Events.Online从站上线(热插拔恢复)
slave.Events.DCSyncLostDC 同步丢失
输入数据变化slave.Events.InputChanged输入 PDO 数据变化(原生检测)
PDO 数据变化slave.PDO.InputsMapping<T>(onChanged)输入 PDO 结构体变化回调(自动)
slave.PDO.BindPdoStruct<T>().ChangedPDO 结构体绑定事件

从站状态事件

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