跳到主要内容

属性与状态机

提示

属性​

命名风格

下表用 PascalCase 列出(SlaveCount() 等)。jar 同时提供 camelCase 同义入口(slaveCount() 等),取数语义一致;PascalCase 入口在源码中标 @Deprecated 仅为兼容保留,新代码建议用 camelCase。State() / ErrorCode() 等返回枚举(EcState / EcALState),不是原始整数。

类别属性类型读写说明
基本信息MasterIndexshort (public final)只读主站编号(构造后不变)
MasterNumber()short只读主站编号(MasterIndex 别名)
SlaveCount()int只读网络中检测到的从站数量
Slaves()List<Slave>只读从站列表(不可修改)
IsDisposed()boolean只读主站是否已被释放
IsStarted()boolean只读PDO 循环是否已启动
网络与链路PrimaryNicName()String只读主网卡名称
RedundantNicName()String只读冗余网卡名称
LinkStatus()EcLinkState只读网络链路状态
LinkState()EcLinkState只读链路状态枚举值
错误码ErrorCode()EcALState只读异常时的自动报警错误码(AL Status Code)
ErrorCnt()int只读错误计数
WKCWKC()short只读上一周期 PDO 交换的实际工作计数器
ExpectedWKC()short只读期望工作计数器(映射后固定不变)
ExpectedWKC(short)void只写覆盖期望 WKC(高级用途)
从站分组ActiveGroupCount()byte只读活跃组数量(映射后有效)
IO 映射Obytes() / Ibytes()int只读总输出/输入字节数
Obits() / Ibits()short只读总输出/输入位数
DCHasDC()boolean只读网络中是否有支持 DC 的从站
DCtime()long只读DC 时间戳(纳秒)

EcLinkState 枚举​

public enum EcLinkState {
DISCONNECTED(0), // 无连接
CONNECTED(1), // 主网口和冗余网口都连接
PRIMARY_ONLY(3), // 仅主网口连接
SECONDARY_ONLY(4); // 仅冗余网口连接
}

EcALState 枚举​

public enum EcALState {
NO_ERROR(0x0000), // 无错误
UNSPECIFIED_ERROR(0x0001), // 未指定错误
NO_MEMORY(0x0002), // 内存不足
INVALID_DEVICE_SETUP(0x0003), // 无效的设备设置 (如总线耦合器无物理模块连接)
INVALID_REVISION(0x0004), // 输出/输入映射与硬件或软件修订版不匹配
SII_EEPROM_MISMATCH(0x0006), // SII/EEPROM 信息 (过程数据描述或修订号) 与固件不匹配
FIRMWARE_UPDATE_FAILED(0x0007), // 固件更新未成功, 旧固件仍在运行 (重新进入 BOOT 状态执行 FoE 下载)
LICENSE_ERROR(0x000E), // 许可证错误 (联系从站厂商激活功能许可)
INVALID_STATE_CHANGE(0x0011), // 无效的状态转换
UNKNOWN_REQUESTED_STATE(0x0012), // 请求了未知状态
BOOTSTRAP_NOT_SUPPORTED(0x0013), // 不支持 Bootstrap
NO_VALID_FIRMWARE(0x0014), // 无有效固件
INVALID_MAILBOX_CONFIG(0x0015), // 无效的邮箱配置 (SM0)
INVALID_MAILBOX_CONFIG2(0x0016), // 无效的邮箱配置 (SM1)
INVALID_SYNC_MANAGER_CONFIG(0x0017), // 无效的同步管理器配置
NO_VALID_INPUTS(0x0018), // 无有效输入
NO_VALID_OUTPUTS(0x0019), // 无有效输出
SYNC_ERROR(0x001A), // 同步错误
SYNC_MANAGER_WATCHDOG(0x001B), // 同步管理器看门狗
INVALID_SYNC_MANAGER_TYPES(0x001C), // 无效的同步管理器类型
INVALID_OUTPUT_CONFIG(0x001D), // 无效的输出配置
INVALID_INPUT_CONFIG(0x001E), // 无效的输入配置
INVALID_WATCHDOG_CONFIG(0x001F), // 无效的看门狗配置
NEEDS_COLD_START(0x0020), // 需要冷启动
NEEDS_INIT(0x0021), // 需要 Init 状态
NEEDS_PREOP(0x0022), // 需要 PreOp 状态
NEEDS_SAFEOP(0x0023), // 需要 SafeOp 状态
INVALID_INPUT_MAPPING(0x0024), // 无效的输入映射
INVALID_OUTPUT_MAPPING(0x0025), // 无效的输出映射
INCONSISTENT_SETTINGS(0x0026), // 不一致的设置
FREERUN_NOT_SUPPORTED(0x0027), // 不支持自由运行
SYNC_NOT_SUPPORTED(0x0028), // 不支持同步
FREERUN_NEEDS_3BUFFER(0x0029), // 自由运行需要三重缓冲
BACKGROUND_WATCHDOG(0x002A), // 后台看门狗
NO_VALID_IO(0x002B), // 无有效 I/O
FATAL_SYNC_ERROR(0x002C), // 致命同步错误
NO_SYNC_ERROR(0x002D), // 无同步错误
CYCLE_TIME_TOO_SMALL(0x002E), // 周期时间过小 (主站周期 < 从站支持的最小周期时间)
INVALID_DC_SYNC_CONFIG(0x0030), // 无效的 DC 同步配置
INVALID_DC_LATCH(0x0031), // 无效的 DC 锁存
PLL_ERROR(0x0032), // PLL 错误
DC_SYNC_IO_ERROR(0x0033), // DC 同步 I/O 错误
DC_SYNC_TIMEOUT(0x0034), // DC 同步超时
INVALID_DC_SYNC_CYCLE(0x0035), // 无效的 DC 同步周期
INVALID_SYNC0_CYCLE(0x0036), // 无效的 SYNC0 周期
INVALID_SYNC1_CYCLE(0x0037), // 无效的 SYNC1 周期
MAILBOX_AOE(0x0041), // 邮箱 AoE 错误
MAILBOX_EOE(0x0042), // 邮箱 EoE 错误
MAILBOX_COE(0x0043), // 邮箱 CoE 错误
MAILBOX_FOE(0x0044), // 邮箱 FoE 错误
MAILBOX_SOE(0x0045), // 邮箱 SoE 错误
MAILBOX_VOE(0x004F), // 邮箱 VoE 错误
EEPROM_NO_ACCESS(0x0050), // EEPROM 无访问权限
EEPROM_ERROR(0x0051), // EEPROM 错误
EXTERNAL_HARDWARE_NOT_READY(0x0052), // 外部硬件未就绪 (从站因外部连接或信号缺失拒绝状态切换)
SLAVE_RESTARTED(0x0060), // 从站重启
DEVICE_ID_UPDATED(0x0061), // 设备 ID 已更新
MODULE_IDENT_LIST_MISMATCH(0x0070), // 模块标识列表不匹配
SUPPLY_VOLTAGE_TOO_LOW(0x0080), // 供电电压过低
SUPPLY_VOLTAGE_TOO_HIGH(0x0081), // 供电电压过高
TEMPERATURE_TOO_LOW(0x0082), // 温度过低
TEMPERATURE_TOO_HIGH(0x0083), // 温度过高
APP_CONTROLLER_AVAILABLE(0x00F0), // 应用控制器可用
UNKNOWN(0xFFFF); // 未知错误
}
Java 特有语法糖

Java 可用 master.Slaves().stream().filter(...) 流式筛选从站集合, 配合 MasterStreams sugar 提供空安全的 slavesInState / slavesByVendor / slavesByName / slavesAsMap / stateCounts 等常用过滤器与索引视图, 比手写 for 循环更短更安全。 详见 Stream API.

状态机管理​

State()​

EtherCAT 当前状态 (只读)。切换状态请调用 setState(...)。

相关结构:

public enum EcState {
NONE(0x00), // 无状态
INIT(0x01), // 初始化
PRE_OP(0x02), // 预操作
BOOT(0x03), // 引导
SAFE_OP(0x04), // 安全操作
OP(0x08), // 运行
ACK(0x10), // 确认
ERROR(0x10); // 错误
}

示例:

if (master.State() == EcState.OP) {
System.out.println("主站处于运行状态");
}
提示
  • SDK 中的所有切换状态机方法,均符合 EtherCAT 标准状态机流程,协议层自动处理。
  • 请在启动前导入或设置正确的配置。
  • 切换失败后可遍历 master.Slaves() 逐个读 slave.ErrorCode() 定位卡点,或调用 State.VerifyStartupConfiguration() 取得中文失败明细。

setState(EcState state)​

public boolean setState(EcState state)

切换主站到目标状态。内部自动按 ETG 状态机链 (INIT↔PRE_OP↔SAFE_OP↔OP) 跨多级一次到位,自动应用各步启动参数。进入 SAFE_OP/OP 时自动启动 PDO 线程。

每步超时按启动参数工作量计算,不是固定 5s:4s 握手余量 + Σ(startup SDO 条数 × 2.5s),下限 max(4s, 站数×5s)(目标 OP 时至少 30s),上限 45s。

参数:

  • state (EcState) — 目标状态

返回值:

  • boolean — true 切换成功,false 失败
内部自动重试

非超时的瞬时失败(PHY PLL 抖动等)SDK 自动重试,共 3 次尝试、间隔 1.5s;重试只续跑已确认态之后的失败步,不整段重跑。用户不需要自行包装重试逻辑。

某步真超时(本次尝试耗时已达上述预算)则当步即失败,不再叠剩余重试,避免卡死步把超时放大成 3× 预算。

进入 SAFE_OP 自动配置同步策略

切换到 SAFE_OP 时,SDK 自动按 ETG 标准为非 DC 从站配置同步循环计数阈值与 SyncManager 同步类型 (FreeRun 兜底),应用层无需手动写。

DC 从站跳过此自动配置 (避免与 DC 时基冲突)。如需自定义同步策略,应在切到 SAFE_OP 之后再覆盖。写入失败时容忍继续,不阻塞 SAFE_OP 转换。

示例:

if (!master.setState(EcState.OP)) {
System.out.println("状态切换失败");
}

setStateAsync(EcState state)​

public CompletableFuture<Boolean> setStateAsync(EcState state)

异步版状态切换,不阻塞调用线程。重试与 SAFE_OP 自动同步行为同上。

参数:

  • state (EcState) — 目标状态

返回值:

  • CompletableFuture<Boolean> — 完成时返回 true(成功)/ false(失败)

示例:

master.setStateAsync(EcState.OP).thenAccept(ok -> {
if (!ok) System.out.println("状态切换失败");
});

// 阻塞等待结果
boolean ok = master.setStateAsync(EcState.OP).get();

Stop()​

public void Stop()

停止主站,切换到 PRE_OP 状态。

Start()​

public void Start()

启动 PDO 循环。setState(SAFE_OP) 与 setState(OP) 内部已自动启动,通常无需显式调用。

State.abort()​

public static void abort()

中断协议层所有阻塞操作(如 setState(...)、build() 中的 setNetwork 等)。可安全地从任意线程调用。

示例:

import com.darra.ethercat.master.State;

// 用户点击取消按钮时中断正在进行的状态切换
State.abort();

State.waitForState(EcState target, int timeoutMs, int pollIntervalMs)​

public boolean waitForState(EcState target, int timeoutMs, int pollIntervalMs)
public boolean waitForState(EcState target) // 默认 timeoutMs=10000, pollIntervalMs=50

State 子模块上的方法。轮询等待主站到达指定状态。不发起状态切换,仅等待已请求的转换完成 / PDO 流稳定确认。常用于 setStateAsync 异步触发后的确认窗口,或 PDO 启动后短暂等待 OP 稳定。

参数:

  • target (EcState) — 目标状态
  • timeoutMs (int) — 最大等待时间(毫秒),默认 10000ms
  • pollIntervalMs (int) — 轮询间隔(毫秒),默认 50ms

返回值:

  • boolean — 在超时前到达目标状态返回 true

示例:

master.setState(EcState.OP);

// 最多等 5s 主站进入 OP
State stateMgr = new State(master.MasterIndex);
if (!stateMgr.waitForState(EcState.OP, 5000, 50)) {
System.out.println("主站未在 5s 内进入 OP");
}

State.waitForSlaveState(int slaveIndex, EcState target, int timeoutMs, int pollIntervalMs)​

public boolean waitForSlaveState(int slaveIndex, EcState target, int timeoutMs, int pollIntervalMs)

State 子模块上的方法。轮询等待单个从站到达指定状态。适用于 PDO 启动后对关键从站的状态确认。

参数:

  • slaveIndex (int) — 从站编号(1-based)
  • target (EcState) — 目标状态
  • timeoutMs (int) — 最大等待时间(毫秒)
  • pollIntervalMs (int) — 轮询间隔(毫秒)

返回值:

  • boolean — 在超时前到达目标状态返回 true

示例:

// 等从站 3 进入 OP, 最多 3s
State stateMgr = new State(master.MasterIndex);
if (!stateMgr.waitForSlaveState(3, EcState.OP, 3000, 50)) {
System.out.println("从站 3 当前: " + master.getSlave(3).State());
}
Java 特有语法糖

Java 可用 master.setStateAsync(state).orTimeout(30, TimeUnit.SECONDS) 异步串行/并行组合状态切换。 MasterAsync sugar 进一步提供 bootToOpAsync boot、scanAsync 并行多 NIC 扫描、configureDCAllAsync 异步 DC 配置, 内部用 4 线程 daemon 池, 不阻塞 PDO 周期。 详见 CompletableFuture 异步.

过程数据看门狗​

setAllSlaveWatchdog(int timeoutMs)​

public int setAllSlaveWatchdog(int timeoutMs)

批量设置所有从站的过程数据看门狗超时。

参数:

  • timeoutMs (int) — 超时时间(毫秒),0 = 禁用,最大 6553ms

返回值:

  • int — 成功设置的从站数量
备注

单个从站的看门狗配置请使用 slave.SetWatchdog()。

示例:

int count = master.setAllSlaveWatchdog(100);
System.out.println("已设置 " + count + " 个从站看门狗超时为 100ms");

setAllSlavePdiWatchdog(int timeoutMs)​

public int setAllSlavePdiWatchdog(int timeoutMs)

批量设置所有从站的 PDI 看门狗超时。

参数:

  • timeoutMs (int) — 超时时间(毫秒),0 = 禁用,最大 6553ms

返回值:

  • int — 成功设置的从站数量
备注

单个从站的看门狗配置请使用 slave.SetPdiWatchdog()。

示例:

int count = master.setAllSlavePdiWatchdog(100);
System.out.println("已设置 " + count + " 个从站 PDI 看门狗超时为 100ms");

诊断控制方法​

resetDiagnostics()​

public void resetDiagnostics()

重置主站诊断计数器。等同于调用 master.Diagnostics().reset() 中的诊断重置部分。

resetSlavePortErrorCounters(short slaveIndex)​

public boolean resetSlavePortErrorCounters(short slaveIndex)

重置指定从站的端口错误计数器。

参数:

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

返回值:

  • boolean — 成功返回 true
重置

master.Diagnostics().reset() 会同时重置主站诊断统计、所有从站端口错误计数器和 PDO 丢帧统计。

热插拔自修复​

在断电重插/更换从站的场景下,SDK 自动识别身份不符并进入保护状态,避免错误设备被误纳入控制循环。事件流程见 SlaveIdentityMismatch 事件。

AcknowledgeSlaveReplacement(int slaveNum)​

public boolean AcknowledgeSlaveReplacement(int slaveNum)

用户确认从站替换完毕,触发 EtherCAT 识别状态机重新探测该从站。

调用时机: 订阅 master.Events().addSlaveIdentityMismatchListener 接收到身份不符报警后,操作员检查/更换设备完毕,调用本方法让 SDK 重新检测。

行为:

  • 若身份已纠正(换回正确设备 / 同型号升级 Revision)→ 自动恢复并触发 SlaveOnline 事件
  • 若身份仍不匹配 → 再次触发 SlaveIdentityMismatch,回到 IDENT_REJECTED 状态

参数:

  • slaveNum (int) — 从站编号(1-based,与配置一致)

返回值:

  • boolean — true=已接受并复位 FSM;false=参数无效,或从站当前不在 IDENT_REJECTED / FAILED 状态
必须先收到事件

在 SlaveIdentityMismatch 事件触发之前调用本方法无效。从站未进入保护状态时 SDK 会持续正常探测,无需手动确认。

示例:

master.Events().addSlaveIdentityMismatchListener((args) -> {
System.out.printf("从站 %d 身份不符: 期望 Vendor=0x%08X, 实际 Vendor=0x%08X%n",
args.slaveIndex, args.expectedVendor, args.actualVendor);

// UI 弹窗: "请检查从站 N 的设备, 换回正确型号后点击确认"
if (showReplacementDialog(args.slaveIndex)) {
boolean ok = master.AcknowledgeSlaveReplacement(args.slaveIndex);
if (!ok) {
System.out.println("确认失败: 从站不在 IDENT_REJECTED 状态");
}
}
});
与 SlaveOffline/SlaveOnline 的区别
  • SlaveOffline — 从站断电/断线(身份仍匹配),自动恢复并触发 SlaveOnline
  • SlaveIdentityMismatch — 从站身份变了(换错设备 / 换同型号旧版本固件),需手动调 AcknowledgeSlaveReplacement

主站身份与拓扑诊断 (Phase 2)​

GetMasterIdentity()​

public DarraCore.MasterIdentity GetMasterIdentity()

读取主站自身的身份信息(ETG.1510 对象 0x1018),包含厂商号、产品号、Revision、序列号。配置面板/远程运维使用本接口区分多主机环境下的多个 EtherCATMaster 实例。

返回值:

  • DarraCore.MasterIdentity — 身份结构体,失败返回 null

示例:

DarraCore.MasterIdentity id = master.GetMasterIdentity();
if (id != null) {
System.out.printf("主站 Vendor=0x%08X Product=0x%08X%n", id.VendorId, id.ProductCode);
}

GetMasterDiagData()​

public DarraCore.MasterDiagData GetMasterDiagData()

读取主站诊断数据(ETG.1510 对象 0xF120),含周期/非周期帧丢失数、每秒周期/非周期帧数与主站当前状态(DarraCore.MasterDiagData)。与 master.Diagnostics() 的差异:本接口对应 ETG.1510 标准对象,便于和符合 ETG.1510 标准的诊断工具对齐字段语义。

返回值:

  • DarraCore.MasterDiagData — 诊断结构体,失败返回 null

rebuildTopology()​

public void rebuildTopology()

重建主站持有的拓扑视图(重新解析 slave[i].topology / parentport / activeports 生成父子关系)。

调用时机:

  • 热插拔/SlaveIdentityMismatch 修复后,确认 TopologyRoots() / TopologyChildren(parent) 返回的列表与最新链路一致
  • 切换冗余模式(环网 ↔ 线性)后

示例:

master.rebuildTopology();
List<Integer> roots = master.TopologyRoots();
System.out.println("拓扑根: " + roots);

Topology() / TopologyChildren(int parent) / TopologyRoots()​

public List<DarraCore.TopologyNode> Topology()
public List<Integer> TopologyChildren(int parent)
public List<Integer> TopologyRoots()

读取主站持有的拓扑树。根节点计数请用 master.SlaveTopology().Count()(入口端口类型为 MII,或 ParentStation()==0),不是 master.SlaveCount()。

  • Topology() — 返回完整拓扑节点列表(DarraCore.TopologyNode,含各从站父子端口关系)
  • TopologyChildren(parent) — 返回指定从站编号的子节点编号列表
  • TopologyRoots() — 返回拓扑根节点编号列表
  • master.SlaveTopology().Count() — 根节点数(EBUS 模块不算根;从站级 slave.Topology().Count() 返回 0)

示例:

for (DarraCore.TopologyNode node : master.Topology()) {
List<Integer> children = master.TopologyChildren(node.SlaveIndex);
System.out.printf("从站 %d 的子节点: %s%n", node.SlaveIndex, children);
}

resetAllSyncWindowStats()​

public void resetAllSyncWindowStats()

清零所有从站的 DC 同步窗口统计(jitter min/max/avg、越界次数)。通常在切换工况或长时间运行后基线复位。

静态: getActiveInstanceCount() / getMaxInstanceCount() / isAdapterAvailable(String)​

public static int getActiveInstanceCount()
public static int getMaxInstanceCount()
public static boolean isAdapterAvailable(String adapterName)
  • getActiveInstanceCount() — 当前已构建未释放的主站实例数
  • getMaxInstanceCount() — DLL 允许的主站实例上限,受授权和可用隔离核心数限制
  • isAdapterAvailable(adapterName) — 该网卡是否可被本进程内的新主站实例使用,用于"开第二个主站前先问一句会不会撞车"
    • 返回 false:本进程里已有一个仍然活着、且尚未真正释放的主站实例绑着这张网卡
    • 返回 true:没有这样的占用者(从未绑过,或占用者已 close() / 已被回收)

判据来自进程内的绑卡登记表:setNetwork() 成功绑卡即登记(主网卡 + 冗余网卡),close() 完成释放即注销; 占用者被回收后登记自动失效。这是进程内协调,不跨进程,也不探测 NDIS 绑定与链路状态 (跨进程独占由原生驱动层裁决)。adapterName 传 null 或空字符串恒返回 false;网卡名区分大小写。

示例:

if (EtherCATMaster.getActiveInstanceCount() >= EtherCATMaster.getMaxInstanceCount()) {
throw new IllegalStateException("已达主站实例上限");
}
if (EtherCATMaster.isAdapterAvailable("\\Device\\NPF_{GUID-A}")) {
EtherCATMaster master = EtherCATMaster.create();
master.setNetwork("\\Device\\NPF_{GUID-A}");
}

静态: enablePDOMonitoring / isPDOMonitoringEnabled​

public static void enablePDOMonitoring(boolean enable)
public static boolean isPDOMonitoringEnabled()

进程级 PDO 监控开关,开启后 SDK 在每周期记录 WKC / actual vs expected / 帧时延 并写入 Diagnostics 计数器。默认关闭(生产环境零开销),仅在调试 / IDE 监视场景开启。

示例:

EtherCATMaster.enablePDOMonitoring(true);   // 调试模式
// ... 运行 ...
boolean on = EtherCATMaster.isPDOMonitoringEnabled();
EtherCATMaster.enablePDOMonitoring(false); // 还原

资源生命周期​

close() — try-with-resources​

@Override
public void close()

EtherCATMaster 实现了 AutoCloseable,推荐用 try-with-resources 自动释放:

try (EtherCATMaster master = EtherCATMaster.create()) {
master.SetNetwork("\\Device\\NPF_{...}", "");
master.setENI("config.xml").build();
master.setState(EcState.OP); // 内部自动按 ETG 状态机链跨级到位
master.start();
// ... PDO 操作 ...
} // close() 自动调用:Stop → 释放底层槽位 → 注销回调 → 清空从站列表

close() 流程:

  1. 标记 disposed,避免后续方法被调用
  2. 若已 start():调用底层停止接口
  3. 停止 MasterDiagnosticsInfo 采样线程、邮箱网关
  4. 释放底层主站槽位
  5. 清空所有 JNA 回调引用(防止 GC 在释放后被调)
释放后访问

释放后调用任何方法(除 IsDisposed())会抛 IllegalStateException。请勿对已 close() 的实例调用 start() / setNetwork()。

静态: EmergencyCleanup()​

public static void EmergencyCleanup()

紧急释放所有主站资源,跳过常规 stop/dispose 流程,直接关闭网卡句柄。仅用于异常退出钩子(如 Runtime.getRuntime().addShutdownHook / 未捕获异常处理器),常规释放请使用 close()。

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
EtherCATMaster.EmergencyCleanup();
}));
Java 特有语法糖

Java 推荐 try (var master = EtherCATMaster.create()) { ... } 自动释放主站资源, JVM 保证 LIFO 顺序 close. 长跑 Service 可用 MasterScope 包装, 退出前自动把硬件回到 INIT 并 LIFO 释放业务挂载的 DiagnosticQueue / SlaveStateQueue 等子资源, 避免下次启动撞 PDI watchdog. 详见 try-with-resources (RAII).

启动配置验证​

State.VerifyStartupConfiguration()​

public Map<String, Object> VerifyStartupConfiguration()

State 子模块上的方法,遍历所有从站,逐项检查启动配置的完整性:主站状态/链路 → 各从站 ESC 状态 → AL 状态码 → DLL 配置校验。在 start() 前后均可调用,建议在 setState(OP) 失败后调用以定位卡点。

返回值: Map<String, Object>

  • valid (boolean) — 是否全部通过
  • slave_count (int) — 已检测到的从站数量
  • issues (List<String>) — 失败明细(中文,可直接展示给用户)

示例:

State stateMgr = new State(master.MasterIndex);
Map<String, Object> result = stateMgr.VerifyStartupConfiguration();

if (!(boolean) result.get("valid")) {
@SuppressWarnings("unchecked")
List<String> issues = (List<String>) result.get("issues");
issues.forEach(System.err::println);
}
与 master.Validate() 的区别
  • master.Validate() — 仅检查 SDK 调用链(ENI 路径/网口已配),尚未启动 SDK 时使用
  • State.VerifyStartupConfiguration() — 必须 SDK 已 build(),检查实际硬件状态

ESM 状态超时常量​

EtherCATTypes.EsmTimeouts​

public static final class EsmTimeouts {
public static final int INIT_TO_PREOP = 2000;
public static final int PREOP_TO_SAFEOP = 3000;
public static final int SAFEOP_TO_OP = 8000;
public static final int OP_TO_SAFEOP = 200;
public static final int SAFEOP_TO_PREOP = 200;
public static final int PREOP_TO_INIT = 200;
public static final int TO_BOOT = 3000;
public static final int BOOT_TO_INIT = 3000;
public static final int SDO_TIMEOUT = 3000;
public static final int MAILBOX_TIMEOUT = 5000;
public static final int STATE_STABILIZE_DELAY = 100;
}

ETG.1020 推荐的状态机超时常量(毫秒)。在调用 slave.SetState(target, timeoutMs) 时建议优先用本表的常量,而不是硬编码数值,便于未来跟随标准调整。

单从站设置自定义超时:

import com.darra.ethercat.data.EtherCATTypes.EsmTimeouts;

slave.SetEsmTimeouts(
EsmTimeouts.INIT_TO_PREOP, // ip Init -> PreOp
EsmTimeouts.PREOP_TO_SAFEOP, // ps PreOp -> SafeOp
EsmTimeouts.SAFEOP_TO_OP, // so SafeOp -> OP
EsmTimeouts.OP_TO_SAFEOP, // os OP -> SafeOp
EsmTimeouts.SAFEOP_TO_PREOP, // sp SafeOp -> PreOp
EsmTimeouts.PREOP_TO_INIT, // pi PreOp -> Init
EsmTimeouts.TO_BOOT, // bi Any -> Boot
EsmTimeouts.BOOT_TO_INIT // ib Boot -> Init
);

缓存池防篡改检查​

checkBufferIntegrity() / isCachePoolReady() / flushCachePool()​

public boolean checkBufferIntegrity()   // 校验本地缓存池完整性
public boolean isCachePoolReady() // 缓存池是否就绪
public void flushCachePool() // 清空缓存池

checkBufferIntegrity() 触发完整的本地缓冲完整性检查。运行时建议在 PLC 任务空闲期周期性巡检(≥ 4 小时一次);PDO 周期内如需检查,用单次查询的 isCachePoolReady()。

示例:

ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(() -> {
if (!master.checkBufferIntegrity()) {
System.err.println("本地缓存池完整性校验失败");
}
}, 4, 4, TimeUnit.HOURS);

主站构建器辅助 (Builder)​

Validate()​

public ValidationResult Validate()
public static class ValidationResult {
public final boolean IsValid;
public final List<String> Errors;
}

在调用 build() 前预检查 Builder 链是否完整(SetNetwork / setENI 已设置)。失败时 Errors 包含中文错误说明,可直接显示在 UI。

EtherCATMaster master = EtherCATMaster.create()
.SetNetwork("\\Device\\NPF_{...}")
.setENI("config.xml")
.enableAutoStartup();

ValidationResult vr = master.Validate();
if (!vr.IsValid) {
System.err.println(vr); // "配置无效: ENI路径为空字符串; ..."
master.close();
return;
}
master.build();

Deconstruct() / BuildResult​

public BuildResult Deconstruct()
public static class BuildResult {
public final boolean success;
public final int slaveCount;
public final short masterIndex;
public final String message;
public final EtherCATMaster master;
}

返回主站构建摘要,便于日志/上报:

EtherCATMaster.BuildResult r = master.Deconstruct();
System.out.printf("master=%d slaves=%d ok=%s%n",
r.masterIndex, r.slaveCount, r.success);