FSoE 功能安全
FSoE (Functional Safety over EtherCAT) 安全通信接口,在标准 PDO 通道上叠加安全协议层,实现 SIL3/PLe 等级的功能安全通信。
通过 slave.FSoE() 访问。
slave.FSoE() 在从站初始化时自动检测。检测过程:
- CoE 前置条件 — 从站必须支持 CoE 邮箱协议
- 0xF980:01 检测 — 尝试 SDO 读取设备级 FSoE 安全地址,成功则确认支持
- 0x9001:02 检测 — 若上步失败,尝试读取 MDP 连接参数(适用于多模块安全设备)
SDK 提供三个层级的 FSoE 支持:
slave.FSoE()— 基础接口,通过从站访问,提供单连接 FSoE 操作- MDP — 多连接管理,支持单从站上的多个 FSoE 连接
FSoEManager— 多从站多连接协调器,提供BindSafeIO一步初始化
快速开始
FSoE 工作流程
FSoE 在 EtherCAT PDO 通道上建立独立的安全连接。主站负责协议管理,从站负责安全逻辑。
状态机流程:
- Reset — 初始状态,等待主站发起会话
- Session — 会话建立,交换连接 ID
- Connection — 连接建立,验证安全地址
- Parameter — 参数下载阶段(SRA CRC 校验)
- Data — 正常安全数据交换
- Failsafe — 失效安全,从站输出安全值
FSoEManager.BindSafeIO() 一行完成初始化 + 启动数据交换,中间状态(Session -> Connection -> Parameter)自动推进。
最简示例
FSoE fsoe = slave.FSoE();
// 1. 检查能力
if (!fsoe.IsCapable()) return;
// 2. 配置连接
FSoE.FSoEConnectionConfig config = new FSoE.FSoEConnectionConfig();
config.ConnectionId = 0x0100;
config.SafetyAddress = 0x0100;
config.WatchdogTimeMs = 100;
config.SafeInputSize = 2;
config.SafeOutputSize = 1;
// 3. 初始化
fsoe.Initialize(config);
// 4. 启动数据交换
fsoe.RequestData();
// 5. 周期性读写安全数据
byte[] safeInput = fsoe.SafeInputData();
fsoe.SetSafeOutputData(new byte[]{0x01});
FSoE 与普通 PDO 的区别
FSoE 安全数据不能像普通 PDO 那样直接映射。IOmap 中存放的是 FSoE 协议帧,用户的安全数据被包裹在帧内部,且需要经过校验。
SDK 的 FSoE 协议引擎负责:
- CRC 校验 — 验证输入帧完整性、计算输出帧 CRC
- 状态机管理 — Session -> Connection -> Parameter -> Data 自动推进
- 看门狗 — 监控通信超时,超时自动进入 Failsafe
- 序列号 — 检测帧丢失和重复
能力检测
IsCapable()
public boolean IsCapable()
检测从站是否支持 FSoE 功能安全协议。
返回值:
boolean— 支持返回true
属性
| 属性 | 类型 | 说明 |
|---|---|---|
| IsInitialized() | boolean | 是否已初始化 |
| ConnectionState() | FSoEState | 当前 FSoE 状态 |
| ConnectionId() | int | 当前连接 ID |
| InFailsafe() | boolean | 是否处于失效安全模式 |
| WatchdogExpired() | boolean | 看门狗是否过期 |
| LastError() | FSoEError | 最后的错误代码 |
| ConnectionCount() | int | 连接数量 |
| CrcErrorCount() | int | CRC 错误计数 |
| WatchdogErrorCount() | int | 看门狗超时计数 |
| FramesSent() | int | 发送帧数 |
| FramesReceived() | int | 接收有效帧数 |
连接管理
Initialize(FSoEConnectionConfig config)
public boolean Initialize(FSoEConnectionConfig config)
初始化 FSoE 连接。
参数:
config(FSoEConnectionConfig) — 连接配置
返回值:
boolean— 成功返回true
相关结构:
public static class FSoEConnectionConfig {
public short ConnectionId; // 唯一连接标识符
public short SafetyAddress; // FSoE 从站安全地址(拨码开关设定)
public int WatchdogTimeMs; // 看门狗超时(毫秒),默认 100
public short SafeInputSize; // 安全输入数据大小(字节)
public short SafeOutputSize; // 安全输出数据大小(字节)
public int PdoInputOffset; // IOmap 中输入偏移
public int PdoOutputOffset; // IOmap 中输出偏移
}
示例:
FSoE fsoe = slave.FSoE();
FSoE.FSoEConnectionConfig config = new FSoE.FSoEConnectionConfig();
config.ConnectionId = 0x0100;
config.SafetyAddress = 0x0100;
config.WatchdogTimeMs = 100;
config.SafeInputSize = 2;
config.SafeOutputSize = 1;
boolean ok = fsoe.Initialize(config);
Close()
public boolean Close()
关闭 FSoE 连接,释放资源。
Dispose()
public void Dispose()
释放所有资源。
安全数据交换
SafeInputData(int bufferSize)
public byte[] SafeInputData(int bufferSize)
public byte[] SafeInputData()
读取安全输入数据。
参数:
bufferSize(int) — 缓冲区大小(可选,默认 64 字节)
返回值:
byte[]— 安全输入数据,失败返回null
SetSafeOutputData(byte[] data)
public boolean SetSafeOutputData(byte[] data)
设置安全输出数据。
参数:
data(byte[]) — 安全输出数据
返回值:
boolean— 成功返回true
示例:
FSoE fsoe = slave.FSoE();
// 读取安全输入
byte[] safeInput = fsoe.SafeInputData();
if (safeInput != null) {
boolean ch0 = (safeInput[0] & 0x01) != 0;
System.out.println("通道0: " + (ch0 ? "安全" : "触发"));
}
// 写入安全输出
fsoe.SetSafeOutputData(new byte[]{0x01});
WriteOutputFrame(byte[] frameData)
public boolean WriteOutputFrame(byte[] frameData)
直接写入 FSoE 输出帧数据。
状态控制
RequestState(FSoEState targetState)
public boolean RequestState(FSoEState targetState)
请求 FSoE 状态转换。
参数:
targetState(FSoEState) — 目标状态
返回值:
boolean— 成功返回true
RequestData()
public boolean RequestData()
请求进入数据交换状态(DATA)。
RequestFailsafe()
public boolean RequestFailsafe()
主动进入失效安全模式。
ResetConnection()
public boolean ResetConnection()
重置 FSoE 连接到初始状态。
CheckWatchdog()
public boolean CheckWatchdog()
检查看门狗状态。
ClearError()
public void ClearError()
清除错误状态。
示例:
FSoE fsoe = slave.FSoE();
fsoe.RequestState(FSoE.FSoEState.DATA);
fsoe.RequestData();
fsoe.RequestFailsafe();
fsoe.ResetConnection();
统计信息
Status()
public FSoEConnectionStatus Status()
获取连接状态详情。
返回值:
FSoEConnectionStatus— 连接状态信息
相关结构:
public static class FSoEConnectionStatus {
public FSoEState State; // 当前 FSoE 状态
public FSoEError LastError; // 最后的错误代码
public int ErrorCount; // 总错误计数
public int FramesSent; // 发送帧数
public int FramesReceived; // 接收有效帧数
public int CrcErrors; // CRC 错误计数
public int WatchdogErrors; // 看门狗超时计数
public boolean WatchdogExpired; // 看门狗是否过期
public boolean InFailsafe; // 是否处于失效安全模式
public boolean IsInitialized; // 连接是否已初始化
}
示例:
FSoE.FSoEConnectionStatus status = slave.FSoE().Status();
System.out.println("状态: " + status.State);
System.out.println("CRC 错误: " + status.CrcErrors);
System.out.println("发送帧: " + status.FramesSent);
System.out.println("接收帧: " + status.FramesReceived);
System.out.println("看门狗: " + (status.WatchdogExpired ? "过期" : "正常"));
MDP 多连接
MDP(Modular Device Profile)支持单个从站包含多个独立的 FSoE 安全连接,每个模块有自己的安全地址和数据通道。
InitializeMdp(FSoEModuleConfig[] moduleConfigs)
public boolean InitializeMdp(FSoEModuleConfig[] moduleConfigs)
初始化 MDP 多连接模式。
参数:
moduleConfigs(FSoEModuleConfig[]) — 模块配置数组
返回值:
boolean— 成功返回true
相关结构:
public static class FSoEModuleConfig {
public int SlotIndex; // 模块槽位索引
public int AxisNumber; // 轴编号
public FSoEModuleProfile Profile;// 模块配置文件
public short SafetyAddress; // 安全地址
public short ConnectionId; // 连接 ID
public FSoEConnectionType ConnectionType; // 连接类型
public short SafeInputSize; // 安全输入数据大小
public short SafeOutputSize; // 安全输出数据大小
public int PdoInputOffset; // PDO 输入偏移
public int PdoOutputOffset; // PDO 输出偏移
public int WatchdogTimeMs; // 看门狗超时(毫秒)
public FSoEConnectionConfig toConnectionConfig(); // 转换为连接配置
}
MDP 数据读写
// 读取指定连接的安全输入
byte[] mdpInput = fsoe.MdpSafeInputData(connectionIndex);
// 写入指定连接的安全输出
fsoe.SetMdpSafeOutputData(connectionIndex, outputData);
// 查询指定连接的状态
FSoE.FSoEState mdpState = fsoe.MdpConnectionState(connectionIndex);
示例:
FSoE fsoe = slave.FSoE();
int connCount = fsoe.ConnectionCount();
for (int i = 0; i < connCount; i++) {
FSoE.FSoEState state = fsoe.MdpConnectionState(i);
byte[] input = fsoe.MdpSafeInputData(i);
System.out.printf("连接 %d: 状态=%s, 输入=%s%n", i, state,
input != null ? String.format("%d 字节", input.length) : "null");
}
MDP 设备配置
FSoEMdpDeviceConfig
用于配置 MDP 多连接设备的完整配置结构。
public static class FSoEMdpDeviceConfig {
public FSoEConnectionMode ConnectionMode; // 连接模式
public short DeviceSafetyAddress; // 设备级安全地址
public boolean IsDrive; // 是否为驱动设备
public int AxisCount; // 轴数量
public List<FSoEModuleConfig> Modules; // 模块列表
public FSoEModuleConfig addModule(FSoEModuleProfile profile, short safetyAddress);
public FSoEModuleConfig addDriveAxis(int axisNumber, short safetyAddress);
public int getConnectionCount();
}
示例:
FSoE.FSoEMdpDeviceConfig devConfig = new FSoE.FSoEMdpDeviceConfig();
devConfig.ConnectionMode = FSoE.FSoEConnectionMode.MULTIPLE;
// 添加数字量安全 IO 模块
devConfig.addModule(FSoE.FSoEModuleProfile.DIGITAL_IN_OUT, (short) 0x0100);
devConfig.addModule(FSoE.FSoEModuleProfile.DIGITAL_INPUT, (short) 0x0200);
// 添加驱动轴
devConfig.addDriveAxis(0, (short) 0x0300);
事件监听
FSoE 提供多种事件监听器接口,用于监控安全连接状态。
事件接口
// 状态变化事件
@FunctionalInterface
public interface OnStateChanged {
void onStateChanged(FSoEStateChangedEventArgs args);
}
// 错误事件
@FunctionalInterface
public interface OnErrorOccurred {
void onErrorOccurred(FSoEErrorEventArgs args);
}
// 失效安全事件
@FunctionalInterface
public interface OnFailsafeTriggered {
void onFailsafeTriggered(FSoEFailsafeEventArgs args);
}
// 安全数据更新事件
@FunctionalInterface
public interface OnSafeDataUpdated {
void onSafeDataUpdated(FSoEDataUpdatedEventArgs args);
}
事件参数结构:
public static class FSoEStateChangedEventArgs {
public int SlaveIndex; // 从站索引
public int ConnectionIndex; // 连接索引
public FSoEState OldState; // 旧状态
public FSoEState NewState; // 新状态
}
public static class FSoEErrorEventArgs {
public int SlaveIndex; // 从站索引
public int ConnectionIndex; // 连接索引
public FSoEError Error; // 错误代码
public FSoEState CurrentState; // 当前状态
public String getDescription(); // 错误描述
}
public static class FSoEFailsafeEventArgs {
public int SlaveIndex; // 从站索引
public int ConnectionIndex; // 连接索引
public FSoEFailsafeReason Reason; // 触发原因
public boolean EnteringFailsafe; // 是否进入失效安全
}
安全数据结构体
安全数据使用 Structure 映射,确保内存布局与设备匹配:
// EL19xx 数字量安全输入(2 字节安全数据)
public static class SafeDigitalInput extends Structure {
public byte inputBits; // 安全输入状态位(每位对应一个通道)
public byte diagBits; // 输入诊断状态
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("inputBits", "diagBits");
}
}
// EL29xx 数字量安全输出(1 字节安全数据)
public static class SafeDigitalOutput extends Structure {
public byte outputBits; // 安全输出控制位
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("outputBits");
}
}
// ETG.6100 安全驱动输入(10 字节安全数据)
public static class SafeDriveInput extends Structure {
public short safetyStatusWord; // 安全状态字
public int safeActualPosition; // 安全实际位置
public int safeActualVelocity; // 安全实际速度
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("safetyStatusWord", "safeActualPosition", "safeActualVelocity");
}
}
// ETG.6100 安全驱动输出(2 字节安全数据)
public static class SafeDriveOutput extends Structure {
public short safetyControlWord; // 安全控制字
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("safetyControlWord");
}
}
SafeInputSize / SafeOutputSize 必须与结构体的实际大小一致,否则读写会失败。
FSoE 管理器
FSoEManager
多连接管理器,管理多个 FSoE 连接的生命周期。
public static class FSoEManager {
public boolean addConnection(short slaveIndex, int connectionId,
short safetyAddress, int watchdogMs,
short safeInputSize, short safeOutputSize,
int pdoInputOffset, int pdoOutputOffset);
public boolean removeConnection(int slaveIndex, int connectionId);
public void processCycle();
public void closeAll();
public int getConnectionCount();
public FSoEMdp FindByAddress(short safetyAddress);
public FSoEMdp FindByConnectionId(int connectionId);
public boolean BindSafeIO(short slaveIndex, int safeInputOffset, int safeInputSize,
int safeOutputOffset, int safeOutputSize,
short safetyAddress, int watchdogMs);
public boolean BindMdpDriveAxis(short slaveIndex, int axisIndex,
short safetyAddress, int watchdogMs,
short safeInputSize, short safeOutputSize,
int pdoInputOffset, int pdoOutputOffset);
public String GetStatusSummary();
}
BindSafeIO()
public boolean BindSafeIO(short slaveIndex, int safeInputOffset, int safeInputSize,
int safeOutputOffset, int safeOutputSize,
short safetyAddress, int watchdogMs)
一步绑定安全 IO。自动完成连接初始化 + 状态推进到 DATA。
参数:
slaveIndex(short) — 从站索引(1-based)safeInputOffset(int) — 输入 PDO 中安全数据的字节偏移safeInputSize(int) — 安全输入数据大小(字节)safeOutputOffset(int) — 输出 PDO 中安全数据的字节偏移safeOutputSize(int) — 安全输出数据大小(字节)safetyAddress(short) — 安全地址(硬件拨码)watchdogMs(int) — 看门狗超时(毫秒)
示例:
FSoE.FSoEManager mgr = new FSoE.FSoEManager(dll, masterIndex);
// 一步绑定(自动 Session -> Connection -> Parameter -> Data)
mgr.BindSafeIO((short) 1, 0, 2, 0, 1, (short) 0x0100, 100);
BindMdpDriveAxis()
public boolean BindMdpDriveAxis(short slaveIndex, int axisIndex,
short safetyAddress, int watchdogMs,
short safeInputSize, short safeOutputSize,
int pdoInputOffset, int pdoOutputOffset)
MDP 驱动轴安全连接绑定。每轴一个独立 FSoE 连接。
示例:
// 双轴安全驱动器
mgr.BindMdpDriveAxis((short) 1, 0, (short) 0x0100, 100,
(short) 10, (short) 2, 0, 0); // 轴 0
mgr.BindMdpDriveAxis((short) 1, 1, (short) 0x0200, 100,
(short) 10, (short) 2, 0, 0); // 轴 1
批量操作
// 检查所有连接是否在 DATA 状态
// (通过遍历连接状态判断)
// 获取状态摘要
System.out.println(mgr.GetStatusSummary());
// 关闭所有连接
mgr.closeAll();
SafetyManager — MDP 模块化绑定
SafetyManager 是 FSoE 内的多连接管理器。FSoEManager 是其历史别名(API 等价)。下面三个 MDP 专用方法适配 ETG.5001 模块化设备协议(如 Beckhoff EL69x0、安全 IO 模块),通过 moduleNumber / axisIndex 自动映射 PDO 偏移,无需手算 safeInputOffset / safeOutputOffset。
BindMdpSafeInput()
public boolean BindMdpSafeInput(short slaveIndex, short safetyAddress,
short inputSize, int moduleNumber, int watchdogMs)
绑定一个只输入的 MDP 安全模块(典型场景:紧急停止按钮 / 光幕反馈)。
参数:
moduleNumber(int) — MDP 模块编号(0-based),SDK 自动从该模块 SI 描述推导 PDO 字节偏移- 其余参数同
BindSafeIO
BindMdpSafeOutput()
public boolean BindMdpSafeOutput(short slaveIndex, short safetyAddress,
short outputSize, int moduleNumber, int watchdogMs)
绑定一个只输出的 MDP 安全模块(典型:安全继电器输出 / 安全使能信号)。
BindMdpDriveAxis() — 推荐
public boolean BindMdpDriveAxis(short slaveIndex, int axisIndex,
short safetyAddress, int watchdogMs)
绑定 MDP 驱动轴的 STO/SS1/SS2 安全连接。输入/输出 size 由 SDK 从 MDP 模块描述自动发现,无需手填。每轴独立 ConnID = axisIndex + 1。
示例(双轴 MDP 安全驱动器 + 一个 STO 输入):
FSoE.SafetyManager mgr = new FSoE.SafetyManager(dll, masterIndex);
// 紧急停止按钮 (输入)
mgr.BindMdpSafeInput((short) 2, (short) 0x0001, (short) 2, 0, 100);
// 双轴驱动器
mgr.BindMdpDriveAxis((short) 1, 0, (short) 0x0100, 100); // 轴 0
mgr.BindMdpDriveAxis((short) 1, 1, (short) 0x0200, 100); // 轴 1
System.out.println(mgr.GetStatusSummary());
BindSafeIO— 必须手填safeInputOffset / safeOutputOffset,适合非 MDP 安全从站(裸 FSoE 帧)BindMdpSafeInput / Output / DriveAxis— SDK 从 MDP 0xF050/0xF030 模块列表自动定位 PDO 区域,参数更少
Connection ID 校验
isConnectionIdAvailable(int connId)
public static boolean isConnectionIdAvailable(int connId)
检查指定的 FSoE Connection ID 是否可用(未被当前 Master 占用)。静态方法,无需实例化 FSoE 即可调用,供配置工具在分配新 ConnID 前做冲突校验,避免 Initialize 因重复而失败。
参数:
connId(int) — 候选 Connection ID,有效范围1..65535
返回值:
boolean—true可用(未被占用且非 0);false已被占用,或为0(保留值)
ConnID必须>= 1,0为保留值ConnID在同一 Master 内必须全局唯一
示例:
int myConnId = 0x1234;
if (!FSoE.isConnectionIdAvailable(myConnId)) {
System.out.println("ConnID 0x" + Integer.toHexString(myConnId) + " 已被占用");
return;
}
// 冲突校验通过后再 Initialize
FSoE.FSoEConnectionConfig config = new FSoE.FSoEConnectionConfig();
config.ConnectionId = (short) myConnId;
config.SafetyAddress = 0x0100;
config.WatchdogTimeMs = 100;
config.SafeInputSize = 2;
config.SafeOutputSize = 1;
slave.FSoE().Initialize(config);
Java 的静态方法 FSoE.isConnectionIdAvailable(int) 对应 C# 的 FSoE.IsConnectionIdAvailable(ushort),语义一致,仅遵循 Java camelCase 命名惯例。
CRC 计算
fsoeCrc16(byte[] data)
public static int fsoeCrc16(byte[] data)
计算 FSoE CRC16 校验值(CCITT-False 算法)。
fsoeCrc16Fast(byte[] data)
public static int fsoeCrc16Fast(byte[] data)
使用查找表加速计算 FSoE CRC16。
FSoECrc16 类
可配置的 CRC-16 实现类(默认 CRC-16/CCITT-FALSE)。
public static class FSoECrc16 {
public FSoECrc16(); // 默认参数
public FSoECrc16(int polynomial, int initial, int xorOut); // 自定义参数
public int compute(byte[] data); // 计算 CRC
}
FSoEState 枚举
public enum FSoEState {
RESET(0x100), // 初始/重置状态
SESSION(0x101), // 会话建立
CONNECTION(0x102), // 连接建立
PARAMETER(0x103), // 参数下载
DATA(0x104), // 数据交换(正常工作)
FAILSAFE(0x105); // 失效安全
}
FSoEError 枚举
public enum FSoEError {
NONE(0x0000), // 无错误
WRONG_COMMAND(0x0001), // 错误的命令
UNKNOWN_COMMAND(0x0002), // 未知命令
WRONG_CONNECTION_ID(0x0003), // 连接 ID 不匹配
CRC_ERROR(0x0004), // CRC 校验失败
WATCHDOG(0x0005), // 看门狗超时
WRONG_ADDRESS(0x0006), // 错误的 FSoE 地址
WRONG_DATA(0x0007), // 无效数据
COMM_PARAM_LENGTH(0x0008), // 通信参数长度错误
COMM_PARAM(0x0009), // 通信参数错误
APP_PARAM_LENGTH(0x000A), // 应用参数长度错误
APP_PARAM(0x000B), // 应用参数错误
UNEXPECTED_SESSION(0x000C), // 意外的会话命令
FAILSAFE_DATA(0x000D), // 收到失效安全数据
NOT_INITIALIZED(0x0100), // FSoE 未初始化
MAX_CONNECTIONS(0x0101), // 达到最大连接数
INVALID_STATE_TRANSITION(0x0102) // 无效状态转换
}
其他枚举
FSoECommand
public enum FSoECommand {
RESET((byte) 0x00), // 重置命令
SESSION((byte) 0x01), // 会话请求/响应
CONNECTION((byte) 0x02), // 连接请求/响应
PARAMETER((byte) 0x03), // 参数下载
FAILSAFE((byte) 0x04), // 失效安全模式
DATA((byte) 0x05); // 正常数据交换
}
FSoEFailsafeReason
public enum FSoEFailsafeReason {
WATCHDOG_TIMEOUT(0), // 看门狗超时
CRC_ERROR(1), // CRC 错误
COMMUNICATION_ERROR(2), // 通信错误
APPLICATION_REQUEST(3), // 应用请求
SLAVE_REQUEST(4), // 从站请求
MASTER_REQUEST(5), // 主站请求
RECOVERY_TO_DATA(6); // 恢复到数据模式
}
FSoEModuleProfile
public enum FSoEModuleProfile {
DIGITAL_INPUT(190), // FSoE 数字输入
DIGITAL_IN_OUT(195), // FSoE 数字输入/输出
DIGITAL_OUTPUT(290), // FSoE 数字输出
DRIVE_CONNECTION(790), // FSoE MDP 驱动连接
MASTER(6900); // FSoE 主站
}
完整示例
数字量安全 IO
FSoE fsoe = slave.FSoE();
if (fsoe.IsCapable()) {
// 初始化连接
FSoE.FSoEConnectionConfig config = new FSoE.FSoEConnectionConfig();
config.ConnectionId = 0x0100;
config.SafetyAddress = 0x0100;
config.WatchdogTimeMs = 100;
config.SafeInputSize = 2;
config.SafeOutputSize = 1;
fsoe.Initialize(config);
// 请求数据交换
fsoe.RequestData();
// 注册事件
fsoe.addOnErrorOccurred(args -> {
System.out.printf("FSoE 错误: 0x%04X, %s%n",
args.Error.Value(), args.getDescription());
});
fsoe.addOnFailsafeTriggered(args -> {
if (args.EnteringFailsafe) {
System.out.println("进入失效安全! 原因: " + args.Reason);
}
});
// 周期性数据交换
while (running) {
byte[] safeInput = fsoe.SafeInputData();
if (safeInput != null) {
boolean ch0 = (safeInput[0] & 0x01) != 0;
fsoe.SetSafeOutputData(new byte[]{ ch0 ? (byte) 0x01 : (byte) 0x00 });
}
// 检查状态
if (fsoe.InFailsafe()) {
System.out.println("安全模式已触发!");
break;
}
Thread.sleep(1);
}
// 查看统计
FSoE.FSoEConnectionStatus status = fsoe.Status();
System.out.printf("发送: %d, 接收: %d, CRC错误: %d%n",
status.FramesSent, status.FramesReceived, status.CrcErrors);
fsoe.Close();
}
FSoEManager 一步绑定
FSoE.FSoEManager mgr = new FSoE.FSoEManager(dll, masterIndex);
// 一步绑定(自动 Session -> Connection -> Parameter -> Data)
mgr.BindSafeIO((short) 1, 0, 2, 0, 1, (short) 0x0100, 100);
// 检查状态
System.out.println(mgr.GetStatusSummary());
// PDO 回调中读写安全数据
master.addOnPdoCyclicSync(mi -> {
mgr.processCycle();
// 读取安全输入
byte[] data = slave.FSoE().SafeInputData();
if (data != null) {
boolean ch0 = (data[0] & 0x01) != 0;
slave.FSoE().SetSafeOutputData(new byte[]{ ch0 ? (byte) 0x01 : (byte) 0x00 });
}
});
// ... 运行后关闭
mgr.closeAll();
安全驱动器
FSoE fsoe = slave.FSoE();
if (fsoe.IsCapable()) {
// 初始化安全驱动器连接(10 字节输入 + 2 字节输出)
FSoE.FSoEConnectionConfig config = new FSoE.FSoEConnectionConfig();
config.ConnectionId = 0x0100;
config.SafetyAddress = 0x0100;
config.WatchdogTimeMs = 100;
config.SafeInputSize = 10; // SafeDriveInput: u16 + i32 + i32
config.SafeOutputSize = 2; // SafeDriveOutput: u16
fsoe.Initialize(config);
fsoe.RequestData();
// 失效安全事件
fsoe.addOnFailsafeTriggered(args -> {
if (args.EnteringFailsafe) {
System.out.println("驱动器安全停止! 原因: " + args.Reason);
}
});
// 周期性安全驱动控制
while (running) {
byte[] safeInput = fsoe.SafeInputData();
if (safeInput != null && safeInput.length >= 10) {
// 解析安全状态字 (前2字节, 小端)
int safetyStatus = (safeInput[0] & 0xFF) | ((safeInput[1] & 0xFF) << 8);
byte[] output = new byte[2];
if ((safetyStatus & 0x0100) != 0) {
// Bit8: 安全故障 -> 确认故障
output[0] = (byte) 0x80;
output[1] = 0x00;
// 读取安全实际位置 (字节2-5)
int position = (safeInput[2] & 0xFF)
| ((safeInput[3] & 0xFF) << 8)
| ((safeInput[4] & 0xFF) << 16)
| ((safeInput[5] & 0xFF) << 24);
System.out.printf("安全故障: 位置=%d%n", position);
} else {
// 正常运行: 清除 STO 请求
output[0] = 0x00;
output[1] = 0x00;
}
fsoe.SetSafeOutputData(output);
}
Thread.sleep(1);
}
fsoe.Close();
}
MDP 多连接模式
FSoE fsoe = slave.FSoE();
if (fsoe.IsCapable()) {
// 配置多个模块
FSoE.FSoEModuleConfig[] modules = new FSoE.FSoEModuleConfig[2];
modules[0] = new FSoE.FSoEModuleConfig();
modules[0].SafetyAddress = 0x0100;
modules[0].ConnectionId = 0x0001;
modules[0].SafeInputSize = 2;
modules[0].SafeOutputSize = 1;
modules[0].WatchdogTimeMs = 100;
modules[0].Profile = FSoE.FSoEModuleProfile.DIGITAL_IN_OUT;
modules[1] = new FSoE.FSoEModuleConfig();
modules[1].SafetyAddress = 0x0200;
modules[1].ConnectionId = 0x0002;
modules[1].SafeInputSize = 2;
modules[1].SafeOutputSize = 0;
modules[1].WatchdogTimeMs = 100;
modules[1].Profile = FSoE.FSoEModuleProfile.DIGITAL_INPUT;
fsoe.InitializeMdp(modules);
// 按连接索引访问数据
int connCount = fsoe.ConnectionCount();
for (int i = 0; i < connCount; i++) {
FSoE.FSoEState state = fsoe.MdpConnectionState(i);
byte[] input = fsoe.MdpSafeInputData(i);
System.out.printf("连接 %d: %s%n", i, state);
}
}
多轴安全驱动器(MDP 模式)
FSoE.FSoEManager mgr = new FSoE.FSoEManager(dll, masterIndex);
// 绑定 2 个轴(每轴独立 FSoE 连接)
mgr.BindMdpDriveAxis((short) 1, 0, (short) 0x0100, 100,
(short) 10, (short) 2, 0, 0); // 轴 0
mgr.BindMdpDriveAxis((short) 1, 1, (short) 0x0200, 100,
(short) 10, (short) 2, 0, 0); // 轴 1
System.out.println(mgr.GetStatusSummary());
// PDO 回调中处理各轴安全数据
master.addOnPdoCyclicSync(mi -> {
mgr.processCycle();
// 各轴独立读写安全数据...
});
// 事件监控: 每轴独立监控失效安全
slave.FSoE().addOnFailsafeTriggered(args -> {
System.out.printf("轴%d 失效安全: %s%n",
args.ConnectionIndex, args.Reason);
});
// 运行后关闭
mgr.closeAll();