跳到主要内容

FSoE 功能安全

FSoE (Functional Safety over EtherCAT) 是 ETH 主站内的 FSoE 状态机源码与协议接口,在标准 PDO 通道上叠加安全协议层。周期路径曾 fail-closed。这不是已认证安全产品,也不是 PLC 能力;周期最短 125µs(柔性)。

通过 slave.FSoE 访问。从站不支持 FSoE 时为 null。​

FSoE 设备检测

slave.FSoE 在从站初始化时自动检测。检测过程:

  1. CoE 前置条件 — 从站必须支持 CoE 邮箱协议
  2. 0xF980:01 检测 — 尝试 SDO 读取设备级 FSoE 安全地址,成功则确认支持
  3. 0x9001:02 检测 — 若上步失败,尝试读取 MDP 连接参数(适用于多模块安全设备)

仅检查 CoE 支持是不够的,因为很多非安全设备(如普通伺服驱动器)也支持 CoE。FSoE 设备必须实现特定的安全对象索引才能被正确识别。

快速开始​

FSoE 工作流程​

FSoE 在 EtherCAT PDO 通道上建立独立的安全连接。主站负责协议管理,从站负责安全逻辑。

状态机流程:

  • Reset — 初始状态,等待主站发起会话
  • Session — 会话建立,交换连接 ID
  • Connection — 连接建立,验证安全地址
  • Parameter — 参数下载阶段(SRA CRC 校验)
  • Data — 正常安全数据交换
  • Failsafe — 失效安全,从站输出安全值
自动状态推进

BindSafeIO 一行完成初始化 + 启动数据交换,中间状态(Session → Connection → Parameter)由 DLL 自动推进。

推荐方式

使用 Darra EtherCAT Master Tools 的代码导出功能,可一键生成安全数据结构体和绑定代码。

最简示例(数字量安全 IO)​

// 安全数据结构体(通过 Darra 配置工具一键导出)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDigitalInput // EL19xx: 2 字节
{
public byte InputBits; // 安全输入状态位(每位对应一个通道)
public byte DiagBits; // 输入诊断状态
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDigitalOutput // EL29xx: 1 字节
{
public byte OutputBits; // 安全输出控制位
}
var slave = master.Slaves[0];  // EL1904 安全输入 + EL2904 安全输出

// 1. 一行绑定 + exchange 回调:自动推断数据大小、启动数据交换
// exchange 在 PDO 周期路径中调用,以 ref 方式传入安全输入/输出(并非标配每周期自动交换安全数据)
slave.FSoE.BindSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0100,
exchange: (ref SafeDigitalInput input, ref SafeDigitalOutput output) =>
{
bool ch0 = (input.InputBits & 0x01) != 0;
output.OutputBits = ch0 ? (byte)0x01 : (byte)0x00;
});

// 2. 订阅事件(自动触发,无需手动调用)
slave.FSoE.ErrorOccurred += (s, e) =>
Console.WriteLine($"FSoE 错误: {e.Description}");
PDO 周期时序

绑定后,PDO 周期路径上可读写安全数据(并非标配每周期自动交换安全数据):

  1. 周期开始 — 刷新所有 FSoE 从站的安全输入缓冲区、检查状态变化并触发事件
  2. DataExchange 回调 — 从站级别回调,读取 ref 输入(已是最新数据),修改 ref 输出
  3. ProcessDataCyclicSync 回调 — 主站级别回调(普通 PDO 读写)
  4. 周期结束 — 提交所有 FSoE 从站的安全输出缓冲区到 DLL

FSoE 与普通 PDO 的区别​

FSoE 安全数据不能像普通 PDO 那样用 CopyInputPdo / WriteOutputPdo 当用户安全 IO。原因在于协议层的差异:

使用体验对比

尽管底层机制不同,BindSafeIO + DataExchange 的使用体验已经非常接近普通 PDO:

普通 PDO​

过程数据走金路径映射钉页(slave.PDO.InputsMapping / OutputsMapping),需要快照时用 CopyInputPdo / WriteOutputPdo 整帧复制。IOmap 中的数据就是用户数据。

FSoE PDO​

IOmap 中存放的是 FSoE 协议帧,用户的安全数据被包裹在帧内部,且需要经过校验

SDK 的 FSoE 协议引擎负责:

  • CRC 校验 — 验证输入帧完整性、计算输出帧 CRC
  • 状态机管理 — Session → Connection → Parameter → Data 自动推进
  • 看门狗 — 监控通信超时,超时自动进入 Failsafe
  • 序列号 — 检测帧丢失和重复

绕过 DLL 直接读写 IOmap 会破坏安全协议完整性,因此 FSoE 必须通过独立缓冲区访问。

简化绑定​

BindSafeIO<TIn, TOut>()​

public bool BindSafeIO<TIn, TOut>(ushort safetyAddress,
SafeDataExchangeHandler<TIn, TOut>? exchange = null,
uint watchdogMs = 100, ushort connectionId = 0)
where TIn : struct where TOut : struct

一行完成 FSoE 初始化 + 启动数据交换。自动从结构体推断 SafeInputSize / SafeOutputSize,PDO 偏移固定为站内偏移 0(该从站 PDO 起始,不是 IOmap 全局偏移),connectionId 为 0 时自动分配。

exchange 回调在 PDO 周期路径中调用,以 ref 方式传入安全输入/输出,零拷贝(并非标配每周期自动交换安全数据)。

参数:

  • safetyAddress (ushort) — 从站安全地址(硬件拨码)
  • exchange (SafeDataExchangeHandler<TIn, TOut>?) — 数据交换回调(可选)
  • watchdogMs (uint) — 看门狗超时(毫秒),默认 100
  • connectionId (ushort) — 连接 ID,0 则自动分配

返回值:

  • bool — 成功返回 true

示例:

slave.FSoE.BindSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0100,
exchange: (ref SafeDigitalInput input, ref SafeDigitalOutput output) =>
{
output.OutputBits = input.InputBits;
});

BindSafeInput<TIn>()​

public bool BindSafeInput<TIn>(ushort safetyAddress,
SafeInputExchangeHandler<TIn>? exchange = null,
uint watchdogMs = 100, ushort connectionId = 0)
where TIn : struct

仅绑定安全输入(SafeOutputSize = 0),适用于纯安全输入设备(如 EL1904)。

BindSafeOutput<TOut>()​

public bool BindSafeOutput<TOut>(ushort safetyAddress,
SafeOutputExchangeHandler<TOut>? exchange = null,
uint watchdogMs = 100, ushort connectionId = 0)
where TOut : struct

仅绑定安全输出(SafeInputSize = 0),适用于纯安全输出设备(如 EL2904)。

BindMdpSafeIO<TIn, TOut>()​

// 自动偏移(从 DENI PDO 配置自动计算)
public bool BindMdpSafeIO<TIn, TOut>(ushort safetyAddress,
SafeDataExchangeHandler<TIn, TOut>? exchange = null, uint watchdogMs = 100)
where TIn : struct where TOut : struct

// 显式偏移(用户指定模块站内 PDO 偏移,不是 IOmap 全局偏移)
public bool BindMdpSafeIO<TIn, TOut>(ushort safetyAddress,
uint pdoInputOffset, uint pdoOutputOffset,
SafeDataExchangeHandler<TIn, TOut>? exchange = null, uint watchdogMs = 100)
where TIn : struct where TOut : struct

MDP 多连接绑定。每次调用添加一个独立 FSoE 连接,自动从结构体推断安全数据大小。所有连接在首个 PDO 周期自动启动,无需手动调用。

自动偏移版本通过 slave.MDP.GetModulePdoLayout() 从 DENI 的 PDO Assignment/Mapping 配置自动计算每个模块的站内 PDO 偏移(相对 slave.Ioffset / slave.Ooffset 的偏移,不是 IOmap 全局偏移),按调用顺序依次分配(对应槽位顺序)。需要从站已完成 DENI 配置(ConfigMap 后)且 MDP 已检测到模块。

示例:

// 自动偏移 + exchange 回调(推荐)
slave.FSoE.BindMdpSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0100,
exchange: (ref SafeDigitalInput input, ref SafeDigitalOutput output) =>
{
output.OutputBits = input.InputBits;
});
slave.FSoE.BindMdpSafeIO<SafeDigitalInput, SafeDigitalOutput>(safetyAddress: 0x0200);

// 显式偏移 — 用户指定模块站内 PDO 偏移(相对该从站 PDO 起始,不是 IOmap 全局偏移)
slave.FSoE.BindMdpSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0100, pdoInputOffset: 0, pdoOutputOffset: 0);
slave.FSoE.BindMdpSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0200, pdoInputOffset: 7, pdoOutputOffset: 6);
PDO 偏移自动计算

自动偏移依赖 slave.MDP.GetModulePdoLayout(),该方法通过 CoE SDORead 读取 PDO Assignment (0x1C12/0x1C13) 和 PDO Mapping 条目,累加各 Entry 的 BitLen 计算模块大小,再按槽位顺序累积为字节偏移(相对于slave.Ioffset/slave.Ooffset)。

需要从站已完成 DENI 配置(ConfigMap 后),不强依赖 ESI 文件:ESI 可用时按各模块 PDO 数量精确分配,不可用时按均分估算(可能不准确)。如果 MDP 模块未检测到或 CoE 不可用,自动偏移回退为站内偏移 0(该从站 PDO 起始),此时应使用显式偏移版本。

BindMdpSafeInput<TIn>() / BindMdpSafeOutput<TOut>()​

public bool BindMdpSafeInput<TIn>(ushort safetyAddress,
SafeInputExchangeHandler<TIn>? exchange = null, uint watchdogMs = 100)
where TIn : struct
public bool BindMdpSafeOutput<TOut>(ushort safetyAddress,
SafeOutputExchangeHandler<TOut>? exchange = null, uint watchdogMs = 100)
where TOut : struct

MDP 多连接绑定(仅安全输入 / 仅安全输出),自动偏移。

BindMdpDriveAxis<TIn, TOut>()​

// 自动偏移
public bool BindMdpDriveAxis<TIn, TOut>(int axisNumber, ushort safetyAddress,
SafeDataExchangeHandler<TIn, TOut>? exchange = null, uint watchdogMs = 100)
where TIn : struct where TOut : struct

// 显式偏移(站内 PDO 偏移,不是 IOmap 全局偏移)
public bool BindMdpDriveAxis<TIn, TOut>(int axisNumber, ushort safetyAddress,
uint pdoInputOffset, uint pdoOutputOffset,
SafeDataExchangeHandler<TIn, TOut>? exchange = null, uint watchdogMs = 100)
where TIn : struct where TOut : struct

MDP 驱动轴安全连接绑定。每轴一个独立 FSoE 连接。首个 PDO 周期自动启动。

示例:

slave.FSoE.BindMdpDriveAxis<SafeDriveInput, SafeDriveOutput>(
axisNumber: 0, safetyAddress: 0x0100,
exchange: (ref SafeDriveInput input, ref SafeDriveOutput output) =>
{
if ((input.SafetyStatusWord & 0x0100) != 0)
output.SafetyControlWord |= 0x0080; // 确认故障
});
slave.FSoE.BindMdpDriveAxis<SafeDriveInput, SafeDriveOutput>(
axisNumber: 1, safetyAddress: 0x0200);

DataExchanged 事件​

public event EventHandler<FSoEDataExchangeEventArgs>? DataExchanged;

从站级别的安全数据交换事件(多订阅者,用 += 注册)。每个 PDO 周期中,在安全输入刷新后、安全输出提交前自动触发,事件参数携带当前 FSoE 状态、安全输入/输出、失效安全标志、周期计数和时间戳。

slave.FSoE.DataExchanged += (sender, e) =>
{
// e.CurrentState / e.SafeInputs / e.SafeOutputs / e.InFailsafe / e.CycleCount / e.Timestamp
};
DataExchange 已废弃

旧的 public event Action? DataExchange(无上下文回调)已标注 [Obsolete],新代码请改用上面的 DataExchanged(携带 FSoEDataExchangeEventArgs 完整上下文)。两者都是多订阅者事件,必须用 += 注册,不能用 = 覆盖式赋值。

推荐使用 exchange 回调

BindSafeIO / BindMdpSafeIO 的 exchange 参数会自动注册到内部回调,并以类型化的 ref 方式传入安全数据。直接订阅 DataExchanged 事件适合需要手动管理多个从站数据的场景。

时序:

PDO 周期回调
├─ FSoE 前处理
│ └─ 对每个 FSoE 从站:
│ ├─ 刷新安全输入缓冲区 ← 输入已刷新
│ ├─ 检查状态变化和错误事件
│ └─ DataExchange 回调 ← 【在此读写 ref】
├─ ProcessDataCyclicSync ← 普通 PDO 读写
└─ FSoE 后处理
└─ 提交安全输出缓冲区 ← 自动提交输出

属性​

类别属性类型读写说明
状态IsInitializedbool只读是否已初始化
StateFSoEState只读当前 FSoE 状态
InFailsafebool只读是否处于失效安全模式
WatchdogExpiredbool只读看门狗是否过期
LastErrorFSoEError只读最后的错误代码
连接ConfigFSoEConnectionConfig?只读当前连接配置
StatusFSoEConnectionStatus只读连接状态详情(通信统计)
StatusValidbool只读最近一次状态读取是否有效;false 表示无连接或读取失败,Status / InFailsafe / WatchdogExpired 等值不可信
多连接IsMdpModebool只读是否为 MDP 多连接模式
ConnectionCountint只读连接数量(单连接模式为 1,多连接模式为模块数)
ManagerSafetyManager?只读MDP 多连接管理器(非 MDP 模式为 null)
FSoEState 枚举
public enum FSoEState : int
{
Reset = 0x100, // 初始/重置状态
Session = 0x101, // 会话建立
Connection = 0x102, // 连接建立
Parameter = 0x103, // 参数下载
Data = 0x104, // 数据交换(正常工作)
Failsafe = 0x105 // 失效安全
}
FSoEError 枚举
public enum FSoEError : int
{
None = 0x0000, // 无错误
WrongCommand = 0x0001, // 错误的命令
UnknownCommand = 0x0002, // 未知命令
WrongConnectionId = 0x0003, // 连接ID不匹配
CrcError = 0x0004, // CRC校验失败
Watchdog = 0x0005, // 看门狗超时
WrongAddress = 0x0006, // 错误的FSoE地址
WrongData = 0x0007, // 无效数据
CommParamLength = 0x0008, // 通信参数长度错误
CommParam = 0x0009, // 通信参数错误
AppParamLength = 0x000A, // 应用参数长度错误
AppParam = 0x000B, // 应用参数错误
UnexpectedSession = 0x000C, // 意外的会话命令
FailsafeData = 0x000D, // 收到失效安全数据
OutputBindingUnsupported = 0x000E, // 缺少从站绑定的版本化 PDO 事务(fail-closed)
NotInitialized = 0x0100, // FSoE未初始化(SDK 层错误)
MaxConnections = 0x0101, // 达到最大连接数(SDK 层错误)
InvalidStateTransition = 0x0102 // 无效状态转换(SDK 层错误)
}
FSoEConnectionStatus
类别属性类型读写说明
状态StateFSoEState只读当前 FSoE 状态
LastErrorFSoEError只读最后的错误代码
统计ErrorCountuint只读总错误计数
FramesSentuint只读发送帧数
FramesReceiveduint只读接收有效帧数
CrcErrorsuint只读CRC 错误计数
WatchdogErrorsuint只读看门狗超时计数
状态WatchdogExpiredbool只读看门狗是否过期
InFailsafebool只读是否处于失效安全模式
ToggleBitbyte只读当前切换位值
IsInitializedbool只读连接是否已初始化
FSoEConnectionConfig
类别属性类型读写说明
连接配置ConnectionIdushort读写唯一连接标识符
SafetyAddressushort读写FSoE 从站安全地址(拨码开关设定)
WatchdogTimeMsuint读写看门狗超时(毫秒),默认 100;合法值 0 或 ≥ 2(0 沿用默认),其余抛 ArgumentOutOfRangeException
SafeInputSizeushort读写安全输入数据大小(字节),由设备决定
SafeOutputSizeushort读写安全输出数据大小(字节),由设备决定
PdoInputOffsetuint读写该从站 TxPDO 内 FSoE 帧站内偏移(相对 slave.Ioffset 的偏移,不是 IOmap 全局偏移)
PdoOutputOffsetuint读写该从站 RxPDO 内 FSoE 帧站内偏移(相对 slave.Ooffset 的偏移,不是 IOmap 全局偏移)

安全数据结构​

推荐方式

使用 Darra EtherCAT Master Tools 的代码导出功能,可一键生成安全数据结构体和绑定代码。 导出结果包含正确的结构体大小、字段布局和 FSoE 绑定调用,避免手动定义出错。

安全数据结构体示例:

// EL19xx 数字量安全输入(2 字节安全数据)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDigitalInput
{
public byte InputBits; // 安全输入状态位(每位对应一个通道)
public byte DiagBits; // 输入诊断状态
}

// EL29xx 数字量安全输出(1 字节安全数据)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDigitalOutput
{
public byte OutputBits; // 安全输出控制位
}

// ETG.6100 安全驱动输入(10 字节安全数据)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDriveInput
{
public ushort SafetyStatusWord; // 安全状态字
public int SafeActualPosition; // 安全实际位置
public int SafeActualVelocity; // 安全实际速度
}

// ETG.6100 安全驱动输出(2 字节安全数据)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDriveOutput
{
public ushort SafetyControlWord; // 安全控制字
}

// 安全编码器输入(6 字节安全数据)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeEncoderInput
{
public uint SafePosition; // 安全位置值
public ushort StatusWord; // 状态字
}
结构体大小必须匹配

SafeInputSize / SafeOutputSize 必须与结构体的 Marshal.SizeOf<T>() 一致,否则读写会失败。

状态控制​

RequestState(FSoEState targetState)​

public bool RequestState(FSoEState targetState)

请求 FSoE 状态转换。自动验证状态转换有效性。

参数:

返回值:

  • bool — 成功返回 true

EnterFailsafe()​

public bool EnterFailsafe()

主动进入失效安全模式。从站将输出安全值(由 SetFailsafeOutput 预设)。

Reset (FSoE 协议状态)​

public bool Reset()

重置 FSoE 安全连接到 Reset 状态, 重新开始 FSoE 状态机握手。仅作用于本从站的 FSoE 协议层, 与诊断计数器复位 (master.Diagnostics.Reset) 无关。

Close()​

public bool Close()

关闭 FSoE 连接,释放资源。

参数和配置​

DownloadParameters(byte[] parameterData, out uint sraCrc)​

public bool DownloadParameters(byte[] parameterData, out uint sraCrc)

下载安全参数。

备注

仅在 Parameter 状态下可调用。参数内容由安全配置工具生成。

参数:

  • parameterData (byte[]) — 参数数据
  • sraCrc (out uint) — 输出 SRA CRC32 校验值

返回值:

  • bool — 成功返回 true

SetFailsafeOutput(byte[] data) / SetFailsafeOutput<T>(T data)​

public bool SetFailsafeOutput(byte[] data)
public bool SetFailsafeOutput<T>(T data) where T : struct

预设失效安全时的输出值。当 FSoE 进入 Failsafe 状态时,从站自动切换到此值。

示例:

// 失效安全时所有输出关闭
slave.FSoE.SetFailsafeOutput(new byte[] { 0x00 });

// 或使用结构体
var failsafe = new SafeDriveOutput();
failsafe.SafetyControlWord |= 0x0001; // 请求 STO(安全扭矩关闭)
slave.FSoE.SetFailsafeOutput(failsafe);

事件​

事件在每个 PDO 周期自动检测并触发。

FSoE.StateChanged​

public event EventHandler<FSoEStateChangedEventArgs>? StateChanged;

FSoE 安全连接状态机变化事件。每次安全连接 (Reset / Session / Connection / Parameter / Data / Failsafe) 之间转换时触发,与从站 EtherCAT 状态 (slave.Events.StateChanged) / 主站 EtherCAT 状态 (master.Events.StateChanged) 互相独立。

相关结构:

public class FSoEStateChangedEventArgs
{
public ushort SlaveIndex { get; } // 从站索引
public int ConnectionIndex { get; } // 连接索引(MDP 模式下区分连接)
public FSoEState OldState { get; } // 变化前的状态
public FSoEState NewState { get; } // 变化后的状态
public DateTime Timestamp { get; } // 时间戳
}

示例:

slave.FSoE.StateChanged += (sender, e) =>
{
Console.WriteLine($"FSoE 从站{e.SlaveIndex} 连接{e.ConnectionIndex}: " +
$"{e.OldState} → {e.NewState}");
};

ErrorOccurred​

public event EventHandler<FSoEErrorEventArgs>? ErrorOccurred;

FSoE 错误事件。检测到新错误时触发,包含错误代码和自动生成的中文描述。

相关结构:

public class FSoEErrorEventArgs
{
public ushort SlaveIndex { get; } // 从站索引
public int ConnectionIndex { get; } // 连接索引
public FSoEError Error { get; } // 错误代码
public string Description { get; } // 错误描述(中文,自动生成)
public FSoEState CurrentState { get; } // 发生错误时的状态
public DateTime Timestamp { get; } // 时间戳
}

示例:

slave.FSoE.ErrorOccurred += (sender, e) =>
{
Console.WriteLine($"FSoE 错误: 从站{e.SlaveIndex}, " +
$"代码=0x{(int)e.Error:X4}, {e.Description}, " +
$"当前状态={e.CurrentState}");

if (e.Error == FSoEError.CrcError)
slave.FSoE.ClearError();
};

FailsafeTriggered​

public event EventHandler<FSoEFailsafeEventArgs>? FailsafeTriggered;

失效安全事件。进入或退出失效安全模式时触发。

相关结构:

public class FSoEFailsafeEventArgs
{
public ushort SlaveIndex { get; } // 从站索引
public int ConnectionIndex { get; } // 连接索引
public FSoEFailsafeReason Reason { get; } // 触发原因
public bool EnteringFailsafe { get; } // true=进入失效安全,false=恢复到数据交换
public DateTime Timestamp { get; } // 时间戳
}
public enum FSoEFailsafeReason
{
WatchdogTimeout, // 看门狗超时
CrcError, // CRC错误
CommunicationError, // 通信错误
ApplicationRequest, // 应用主动请求
SlaveRequest, // 从站请求
MasterRequest, // 主站请求
RecoveryToData // 恢复到数据模式(退出失效安全)
}

示例:

slave.FSoE.FailsafeTriggered += (sender, e) =>
{
if (e.EnteringFailsafe)
{
Console.WriteLine($"进入失效安全! 原因: {e.Reason}");
// 应用层安全处理:停止运动、切断气源等
}
else
{
Console.WriteLine($"恢复数据交换");
}
};

SafeDataUpdated​

public event EventHandler<FSoEDataUpdatedEventArgs>? SafeDataUpdated;

安全数据更新事件。每次安全数据刷新时触发。

相关结构:

public class FSoEDataUpdatedEventArgs : EventArgs
{
public ushort SlaveIndex { get; } // 从站索引
public int ConnectionIndex { get; } // 连接索引
public bool IsInput { get; } // 是否为输入数据
public byte[] Data { get; } // 数据内容
public DateTime Timestamp { get; } // 时间戳
}

示例:

slave.FSoE.SafeDataUpdated += (sender, e) =>
{
string direction = e.IsInput ? "输入" : "输出";
Console.WriteLine($"安全数据更新: 从站{e.SlaveIndex} 连接{e.ConnectionIndex} {direction}, {e.Data.Length}字节");
};

诊断​

ClearError()​

public void ClearError()

清除 FSoE 错误。清除后可尝试重新建立连接。

CheckWatchdog()​

public bool CheckWatchdog()

检查 FSoE 看门狗状态(单连接模式)。

返回值:

  • bool — 看门狗正常返回 true,超时返回 false

GetTotalConnectionCount()​

public ushort GetTotalConnectionCount()

获取主站级别 FSoE 总连接数(所有从站的连接总和)。

返回值:

  • ushort — 连接数量

GetSlaveConnectionCount()​

public ushort GetSlaveConnectionCount()

获取当前从站的 FSoE 连接数量(MDP 模式下一个从站可能有多个连接)。

返回值:

  • ushort — 从站连接数

DetectConnections()​

public ushort DetectConnections()

自动检测从站的 FSoE 连接。扫描从站支持的安全模块并返回检测到的连接数。

返回值:

  • ushort — 检测到的连接数

示例:

// 检查看门狗
if (!slave.FSoE.CheckWatchdog())
Console.WriteLine("FSoE 看门狗超时!");

// 获取连接数
ushort totalConns = slave.FSoE.GetTotalConnectionCount();
ushort slaveConns = slave.FSoE.GetSlaveConnectionCount();
Console.WriteLine($"总连接: {totalConns}, 从站连接: {slaveConns}");

// 自动检测连接
ushort detected = slave.FSoE.DetectConnections();
Console.WriteLine($"检测到 {detected} 个 FSoE 连接");

MDP 多连接模式​

MDP 设备(安全 IO 耦合器、多轴驱动器等)包含多个安全模块。多连接模式为每个模块创建独立的 FSoE 连接,各连接有独立的状态机、看门狗和安全数据缓冲区。

选择指南
  • 单连接 (BindSafeIO) — 简单设备、单轴驱动器
  • 多连接 (BindMdpSafeIO) — 模块需要独立监控/恢复时(如多轴驱动器各轴独立安全连接)

GetConnection(int index)​

public FSoEConnection? GetConnection(int index)

获取指定索引的 MDP 连接。也可通过索引器 slave.FSoE[index] 访问。

参数:

  • index (int) — 连接索引(0-based)

返回值:

  • FSoEConnection? — 连接实例,索引无效返回 null

FindConnectionByAddress(ushort safetyAddress)​

public FSoEConnection? FindConnectionByAddress(ushort safetyAddress)

通过安全地址查找 MDP 连接。

参数:

  • safetyAddress (ushort) — FSoE 安全地址

返回值:

  • FSoEConnection? — 连接实例,未找到返回 null

SafetyManager 类​

MDP 模式下的连接管理器,通过 slave.FSoE.Manager 访问。

类别属性类型读写说明
连接管理ConnectionCountint只读连接数量
ConnectionModeFSoEConnectionMode只读连接模式
ConnectionsIReadOnlyList<FSoEConnection>只读所有连接列表
PrimaryConnectionFSoEConnection?只读主连接(索引 0)
FSoEConnectionMode 枚举
public enum FSoEConnectionMode
{
Single, // 单连接模式 - 所有模块共用一个 FSoE 连接
Multiple // 多连接模式 - 每个模块独立 FSoE 连接
}
SafeModuleProfile 枚举
public enum SafeModuleProfile : ushort
{
DigitalInput = 190, // FSoE 数字量输入
DigitalInOut = 195, // FSoE 数字量输入/输出
DigitalOutput = 290, // FSoE 数字量输出
DriveConnection = 790, // FSoE 驱动连接 (CiA402)
Master = 6900 // FSoE 主站模块
}

AllInDataState()​

public bool AllInDataState()

检查是否所有连接都处于数据交换状态。

AnyInFailsafe()​

public bool AnyInFailsafe()

检查是否有任何连接处于失效安全状态。

EnterAllFailsafe()​

public bool EnterAllFailsafe()

将所有连接进入失效安全模式。

ResetAll()​

public bool ResetAll()

重置所有连接。

GetStatusSummary()​

public string GetStatusSummary()

获取所有连接的状态摘要(诊断用)。

FSoEConnection 类​

MDP 模式下的单个 FSoE 连接实例。每个 FSoEConnection 拥有独立的状态机、看门狗和安全数据缓冲区。

通过 slave.FSoE[index]、slave.FSoE.GetConnection(index) 或 slave.FSoE.Manager.Connections[index] 访问。

类别属性类型读写说明
标识Indexint只读连接索引(0-based)
状态IsInitializedbool只读是否已初始化
StateFSoEState只读当前 FSoE 状态
InFailsafebool只读是否处于失效安全模式
WatchdogExpiredbool只读看门狗是否过期
LastErrorFSoEError只读最后的错误代码
配置ConfigFSoEConnectionConfig?只读连接配置
ModuleProfileSafeModuleProfile只读模块配置文件类型
AxisNumberint只读轴编号(仅驱动连接)

RequestState / EnterFailsafe / Reset / ClearError / Close​

public bool RequestState(FSoEState targetState)
public bool EnterFailsafe()
public bool Reset()
public void ClearError()
public bool Close()

连接级别的状态控制和错误处理。每个连接可独立控制。

DownloadParameters(byte[] parameterData, out uint sraCrc)​

public bool DownloadParameters(byte[] parameterData, out uint sraCrc)

下载该连接的安全参数。

SetFailsafeOutput(byte[] data)​

public bool SetFailsafeOutput(byte[] data)

设置该连接的失效安全输出值。

完整示例​

数字量安全 IO​

var safeInput = master.Slaves[0];   // EL1904 安全输入
var safeOutput = master.Slaves[1]; // EL2904 安全输出

// 一行绑定 + exchange 回调
SafeDigitalInput lastInput = default;
safeInput.FSoE.BindSafeInput<SafeDigitalInput>(
safetyAddress: 0x0100,
exchange: (ref SafeDigitalInput input) =>
{
lastInput = input; // 捕获最新输入
});

safeOutput.FSoE.BindSafeOutput<SafeDigitalOutput>(
safetyAddress: 0x0200,
exchange: (ref SafeDigitalOutput output) =>
{
output.OutputBits = lastInput.InputBits; // 输入直通输出
});

// 事件(自动触发)
safeInput.FSoE.FailsafeTriggered += (s, e) =>
Console.WriteLine($"安全输入 失效安全: {e.Reason}");

safeOutput.FSoE.ErrorOccurred += (s, e) =>
Console.WriteLine($"安全输出 错误: {e.Description}");

// 预设失效安全输出
safeOutput.FSoE.SetFailsafeOutput(new byte[] { 0x00 });

安全驱动器​

var drive = master.Slaves[0];  // 带 FSoE 的伺服驱动器

// 一行绑定 + exchange 回调
drive.FSoE.BindSafeIO<SafeDriveInput, SafeDriveOutput>(
safetyAddress: 0x0100,
exchange: (ref SafeDriveInput driveIn, ref SafeDriveOutput driveOut) =>
{
if ((driveIn.SafetyStatusWord & 0x0100) != 0) // Bit8: 安全故障
{
bool sto = (driveIn.SafetyStatusWord & 0x0001) != 0;
Console.WriteLine($"安全故障: STO={sto}, 位置={driveIn.SafeActualPosition}");
driveOut.SafetyControlWord |= 0x0080; // 确认故障
}
else
{
driveOut.SafetyControlWord &= unchecked((ushort)~0x0001); // 清除 STO 请求
driveOut.SafetyControlWord &= unchecked((ushort)~0x0080); // 清除故障确认
}
});

drive.FSoE.FailsafeTriggered += (s, e) =>
{
if (e.EnteringFailsafe)
Console.WriteLine($"驱动器安全停止! 原因: {e.Reason}");
};

多连接 IO(MDP 模式)​

每个模块有独立 FSoE 连接,可独立监控状态和错误。PDO 偏移从 DENI PDO 配置自动计算:

// 安全数据结构体(通过 Darra 配置工具一键导出)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDigitalInput // 2 字节
{
public byte InputBits; // 安全输入状态位
public byte DiagBits; // 输入诊断状态
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDigitalOutput // 1 字节
{
public byte OutputBits; // 安全输出控制位
}
var coupler = master.Slaves[0];  // 安全 IO 耦合器,2 个安全模块

// 绑定 2 个独立连接 + exchange 回调(自动计算 PDO 偏移)
coupler.FSoE.BindMdpSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0100,
exchange: (ref SafeDigitalInput input, ref SafeDigitalOutput output) =>
{
output.OutputBits = input.InputBits;
});
coupler.FSoE.BindMdpSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0200,
exchange: (ref SafeDigitalInput input, ref SafeDigitalOutput output) =>
{
output.OutputBits = input.InputBits;
});
// 首个 PDO 周期自动启动所有连接

// 每个模块独立监控状态
coupler.FSoE.StateChanged += (s, e) =>
Console.WriteLine($"模块{e.ConnectionIndex}: {e.OldState} → {e.NewState}");

多轴安全驱动器(MDP 模式)​

// 安全数据结构体(通过 Darra 配置工具一键导出)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDriveInput // 10 字节 (ETG.6100)
{
public ushort SafetyStatusWord; // 安全状态字
public int SafeActualPosition; // 安全实际位置
public int SafeActualVelocity; // 安全实际速度
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SafeDriveOutput // 2 字节 (ETG.6100)
{
public ushort SafetyControlWord; // 安全控制字
}
var drive = master.Slaves[0];  // 双轴伺服驱动器,每轴独立 FSoE 连接

// 绑定 2 个轴 + exchange 回调(自动计算 PDO 偏移)
SafeDataExchangeHandler<SafeDriveInput, SafeDriveOutput> driveExchange =
(ref SafeDriveInput input, ref SafeDriveOutput output) =>
{
if ((input.SafetyStatusWord & 0x0100) != 0) // 安全故障
output.SafetyControlWord |= 0x0080; // 确认故障
else
output.SafetyControlWord &= unchecked((ushort)~0x0001); // 清除 STO
};

drive.FSoE.BindMdpDriveAxis<SafeDriveInput, SafeDriveOutput>(
axisNumber: 0, safetyAddress: 0x0100, exchange: driveExchange);
drive.FSoE.BindMdpDriveAxis<SafeDriveInput, SafeDriveOutput>(
axisNumber: 1, safetyAddress: 0x0200, exchange: driveExchange);
// 首个 PDO 周期自动启动

// 每轴独立监控
drive.FSoE.FailsafeTriggered += (s, e) =>
Console.WriteLine($"轴{e.ConnectionIndex} 失效安全: {e.Reason}");

Connection ID 唯一性校验​

FSoE 协议用 Connection ID (ConnID) 标识每条独立的安全通信连接。ETG.5120 §5.2.3 要求每个 ConnID 在整个 Master 范围内必须全局唯一, 且保留 0 作为非法值。配置重复 ConnID 会导致连接初始化失败、从站握手不通过,最终进入 Failsafe。

典型使用场景:

  • 配置工具 — 分配新 ConnID 前先校验, 避免与现有连接冲突
  • 多从站 / 多连接场景 — MDP 设备每个模块 / 每个安全轴都有独立 ConnID, 需逐个核对
  • 动态接入 — 运行时热插拔新的 FSoE 从站, 分配前确保不撞号

IsConnectionIdAvailable(ushort connectionId)​

// FSoEHelper 静态类上的静态方法
public static bool IsConnectionIdAvailable(ushort connectionId)

校验候选 ConnID 是否尚未被其他 FSoE 连接使用。该方法是顶层静态类 DarraEtherCAT_Master.FSoEHelper 的成员(与 DarraEtherCAT 平级,不是 DarraEtherCAT.Slave.FSoEInstance 的成员)。

参数:

  • connectionId (ushort) — 候选的 Connection ID (1..65535; 0 视为非法, 直接返回 false)

返回值:

  • bool — true 表示可用 (未被占用且非 0); false 表示已占用或为 0

示例:

ushort myConnId = 0x1234;
if (!FSoEHelper.IsConnectionIdAvailable(myConnId))
{
Console.WriteLine($"ConnID 0x{myConnId:X4} 已被占用, 请选其他值");
return;
}

// 校验通过, 再绑定安全 IO
slave.FSoE.BindSafeIO<SafeDigitalInput, SafeDigitalOutput>(
safetyAddress: 0x0100,
connectionId: myConnId,
exchange: (ref SafeDigitalInput input, ref SafeDigitalOutput output) =>
{
output.OutputBits = input.InputBits;
});

批量分配示例​

// 从 1 开始扫描, 找第一个可用 ConnID
ushort AllocateNextFreeConnId()
{
for (ushort candidate = 1; candidate != 0; candidate++)
{
if (FSoEHelper.IsConnectionIdAvailable(candidate))
return candidate;
}
throw new InvalidOperationException("ConnID 池已耗尽");
}

// 给 3 个安全模块各分配一个独立 ConnID
var id1 = AllocateNextFreeConnId();
var id2 = AllocateNextFreeConnId();
var id3 = AllocateNextFreeConnId();

注意事项​

  • 该方法是静态方法, 不需要从站实例, 校验范围是整个 Master
  • BindSafeIO(..., connectionId: 0) 会让 SDK 内部自动分配连接 ID,多数场景无需显式管理
  • 仅在自定义安全网络规划或配置工具场景下才需手动分配 ConnID