跳到主要内容

ESI 文件管理 (EsiManager / ESI)

EsiManagerESI 工具类提供 EtherCAT Slave Information (ESI) 文件的加载、管理和查询功能。ESI 文件包含从站的完整配置信息,包括设备标识、对象字典、PDO 映射、DC 配置等。

通过 com.darra.ethercat.utils.EsiManager(库管理)和 com.darra.ethercat.utils.ESI(解析/查询)静态类访问。

加载和管理

EsiManager.loadPath

public static int loadPath(String dirPath)

加载指定路径下的所有 ESI 文件(.xml / .esi)。

参数:

  • dirPath (String) — ESI 文件目录路径

返回值:

  • int — 累计加载的设备数

示例:

// 加载指定路径下的全部 ESI 文件
int total = EsiManager.loadPath("C:\\EtherCAT\\ESI");
System.out.println("已加载设备数: " + total);
加载多个目录

loadPath 可对不同目录多次调用累积加载。同名文件再次 addFile / loadPath 时由 EsiManager 内部去重,不会重复解析。

// 标准 ESI 目录
EsiManager.loadPath("C:\\ESI\\Standard");
// 再追加自定义目录(累积,已加载文件自动跳过)
EsiManager.loadPath("C:\\ESI\\Custom");

EsiManager.addFile

public static int addFile(String filePath)

添加单个 ESI 文件到仓库。

参数:

  • filePath (String) — ESI 文件完整路径

返回值:

  • int — 该文件包含的设备数

示例:

// 添加自定义从站的 ESI 文件
EsiManager.addFile("C:\\CustomDevices\\MyDevice.xml");

EsiManager.clear

public static void clear()

清除所有已加载的 ESI 文件。

示例:

// 清除并重新加载
EsiManager.clear();
EsiManager.loadPath("C:\\NewESI");

查询和检索

ESI 设备查询经 EsiManager.getFiles() 返回的「文件名 → 设备描述列表」视图进行(设备描述类型为 ESI.DeviceDescription)。SDK 不提供按 VID/PID 直接查表的便捷方法,按需在 getFiles() 结果上自行筛选。

ESI.parseEsiFile

public static List<ESI.DeviceDescription> parseEsiFile(String filePath)

解析 ESI XML 文件,提取设备描述列表。

返回值:

  • List<DeviceDescription> — 设备描述列表,失败返回空列表

相关结构:

public static class DeviceDescription {
public int vendorId; // 厂商 ID
public int productCode; // 产品代码
public int revisionNumber; // 修订号
public String deviceName; // 设备名称
public String vendorName; // 厂商名称
public String groupName; // 分组名称
public String physics; // 物理层类型
public int mbxProtocol; // 邮箱协议位图
public int sm2Offset, sm2Length; // SM2 偏移/长度
public int sm3Offset, sm3Length; // SM3 偏移/长度
public int dcAssignActivate; // DC AssignActivate
public int dcSync0CycleNs; // DC SYNC0 周期(纳秒)
public int dcSync1CycleNs; // DC SYNC1 周期(纳秒)
}

示例:

List<ESI.DeviceDescription> devices = ESI.parseEsiFile("C:\\ESI\\Beckhoff EL2008.xml");
for (ESI.DeviceDescription dev : devices) {
System.out.printf("设备: %s (VID=0x%08X, PID=0x%08X)%n",
dev.deviceName, dev.vendorId, dev.productCode);
}

按名称筛选示例(基于 getFiles()):

// 在已加载缓存里查找名称含 "EL" 的设备
for (List<ESI.DeviceDescription> devs : EsiManager.getFiles().values()) {
for (ESI.DeviceDescription d : devs) {
if (d.deviceName != null && d.deviceName.contains("EL")) {
System.out.printf("%s (VID=0x%08X)%n", d.deviceName, d.vendorId);
}
}
}

状态查询

EsiManager.getLoadedCount

public static int getLoadedCount()

获取已加载的 ESI 文件数量。

示例:

int count = EsiManager.getLoadedCount();
System.out.println("已加载 " + count + " 个 ESI 文件");

EsiManager.getFiles

public static Map<String, List<ESI.DeviceDescription>> getFiles()

获取所有已加载文件的只读视图(文件名 → 设备描述列表)。

示例:

Map<String, List<ESI.DeviceDescription>> files = EsiManager.getFiles();
System.out.println("已加载的 ESI 文件:");
for (String fileName : files.keySet()) {
System.out.println(" - " + fileName);
}

遍历全部已解析设备示例:

int total = 0;
for (List<ESI.DeviceDescription> devs : EsiManager.getFiles().values()) {
total += devs.size();
}
System.out.println("ESI 库包含 " + total + " 个设备");

默认路径

EsiManager.defaultPath

public static String defaultPath()

获取默认 ESI 文件搜索路径(进程当前目录下 ESI/)。

返回值:

  • String — 默认 ESI 目录路径

示例:

String defaultPath = EsiManager.defaultPath();
System.out.println("默认 ESI 路径: " + defaultPath);

完整示例

应用启动时加载 ESI

public class Program {
public static void main(String[] args) {
System.out.println("正在加载 ESI 文件...");
// 默认路径 = 进程当前目录下 ESI/
int total = EsiManager.loadPath(EsiManager.defaultPath());
System.out.println("成功加载 " + total + " 个 ESI 设备");

// 启动主站...
}
}

从站识别和信息显示

public void displaySlaveInfo(Slave slave) {
int vid = slave.VendorId();
int pid = slave.ProductId();

// 在已加载缓存里按 VID/PID 匹配设备描述
ESI.DeviceDescription match = null;
for (List<ESI.DeviceDescription> devs : EsiManager.getFiles().values()) {
for (ESI.DeviceDescription d : devs) {
if (d.vendorId == vid && d.productCode == pid) { match = d; break; }
}
if (match != null) break;
}

if (match != null) {
System.out.println("从站信息:");
System.out.println(" 名称: " + match.deviceName);
System.out.println(" 厂商: " + match.vendorName);
System.out.println(" 分组: " + match.groupName);
} else {
System.out.printf("未找到 ESI: VID=0x%X, PID=0x%X%n", vid, pid);
}
}

自定义设备支持

public class CustomDeviceManager {
public static void loadCustomDevices(String customEsiPath) {
// 加载标准 ESI
EsiManager.loadPath(EsiManager.defaultPath());

// 追加加载自定义设备 ESI(同名文件自动去重)
if (new java.io.File(customEsiPath).isDirectory()) {
EsiManager.loadPath(customEsiPath);
System.out.println("自定义设备 ESI 已加载");
}
}
}

ESI 诊断工具

public class ESIDiagnostics {
public static void generateReport() {
System.out.println("=== ESI 诊断报告 ===\n");

int fileCount = EsiManager.getLoadedCount();
System.out.println("已加载文件数: " + fileCount);
System.out.println("默认路径: " + EsiManager.defaultPath());

System.out.println("\n已加载的 ESI 文件:");
Map<String, List<ESI.DeviceDescription>> files = EsiManager.getFiles();
for (String fileName : files.keySet()) {
System.out.println(" - " + fileName);
}
}
}

ESI 管理器封装

import com.darra.ethercat.utils.ESI;
import com.darra.ethercat.utils.EsiManager;
import com.darra.ethercat.master.EtherCATMaster;

public class ESIWrapper {
private static boolean initialized = false;

public static void initialize(String customPath) {
if (initialized) return;

System.out.println("初始化 ESI 管理器...");
EsiManager.loadPath(EsiManager.defaultPath());

if (customPath != null && new java.io.File(customPath).isDirectory()) {
System.out.println("加载自定义 ESI 文件: " + customPath);
EsiManager.loadPath(customPath);
}

initialized = true;
System.out.println("ESI 初始化完成,共加载 "
+ EsiManager.getLoadedCount() + " 个文件");
}

public static ESI.DeviceDescription findDevice(int vendorId, int productCode) {
if (!initialized) {
System.out.println("警告: ESI 管理器未初始化");
return null;
}
for (List<ESI.DeviceDescription> devs : EsiManager.getFiles().values()) {
for (ESI.DeviceDescription d : devs) {
if (d.vendorId == vendorId && d.productCode == productCode) return d;
}
}
return null;
}

public static void reload() {
EsiManager.clear();
initialized = false;
initialize(null);
}
}

ESI 绑定到从站

加载到 ESI 缓存后,还需要将 ESI 设备节点绑定到具体从站,才能让 SDK 在拓扑/PDO/DC/邮箱协议解析时使用 ESI 信息。

EsiManager.bindToSlave

public static int bindToSlave(EtherCATMaster master, int slaveIndex, String esiFilePath)

为单个从站绑定指定 ESI 文件。常用于已知拓扑、需要为某站强制使用某个版本 ESI 的场景。

参数:

  • master (EtherCATMaster) — 主站实例
  • slaveIndex (int) — 从站索引(1-based)
  • esiFilePath (String) — ESI XML 文件完整路径

返回值:

  • int — 绑定成功返回非零值;失败返回 0

示例:

// 给从站 #2 绑定特定厂商版本的 ESI
int ok = EsiManager.bindToSlave(master, 2, "D:\\esi\\Vendor_Drive_v3.xml");
if (ok == 0) {
System.out.println("绑定失败, 检查从站索引和文件路径");
}

EsiManager.applyAllSlaves

public static int applyAllSlaves(EtherCATMaster master)

遍历主站下所有从站,按 VendorId + ProductId 在已加载缓存中查找匹配的 ESI 设备并自动绑定。调用前应先 loadPath() 把 ESI 文件加载进缓存。

参数:

  • master (EtherCATMaster) — 主站实例

返回值:

  • int — 成功匹配并绑定的从站数

示例:

// 1. 先把 ESI 库整体加载进缓存
EsiManager.loadPath("D:\\esi");

// 2. 扫描后一键给所有从站绑定匹配的 ESI
int matched = EsiManager.applyAllSlaves(master);
System.out.printf("已为 %d / %d 个从站匹配 ESI%n", matched, master.SlaveCount());
与扫描流程的关系

applyAllSlaves 应在 build() 完成、从站列表就绪之后调用。SDK 在 PDO 配置 / DC 启用 / 邮箱协议建立阶段会自动消费已绑定的 ESI 信息。

ESI 版本匹配

ESI.matchRevision

public static boolean matchRevision(int actual, int expected, ESI.RevisionCheckStrategy strategy)

根据指定策略判断从站实际版本号与 ESI 期望版本号是否匹配。

参数:

  • actual (int) — 从站实际上报的版本号
  • expected (int) — ESI 文件中定义的期望版本号
  • strategy (RevisionCheckStrategy) — 匹配策略

相关结构:

public enum RevisionCheckStrategy {
NONE, // 不检查版本号
EQ, // 完全匹配
EQ_OR_G, // 等于或大于
LW_EQ, // 低16位匹配
HW_EQ, // 高16位匹配
LW_EQ_OR_G, // 低16位等于或大于
HW_EQ_OR_G // 高16位等于或大于
}

返回值:

  • boolean — 匹配成功返回 true

示例:

int slaveRev = 0x00130000;  // actual: 从站实际版本
int esiRev = 0x00120000; // expected: ESI 期望版本

// 严格匹配 — 不通过
boolean exact = ESI.matchRevision(slaveRev, esiRev, ESI.RevisionCheckStrategy.EQ);

// 允许更高版本 — 通过 (actual >= expected)
boolean compat = ESI.matchRevision(slaveRev, esiRev, ESI.RevisionCheckStrategy.EQ_OR_G);

// 仅比较高16位 — 不通过 (0x0013 != 0x0012)
boolean hwMatch = ESI.matchRevision(slaveRev, esiRev, ESI.RevisionCheckStrategy.HW_EQ);

EEPROM CRC 工具

ESI.calculateEepromCrc

public static byte calculateEepromCrc(byte[] data, int length)

计算 EEPROM 数据的 CRC-8 校验值。使用 ETG.2010 定义的 CRC-8/ITU 算法(多项式 0x07,初始值 0xFF)。

参数:

  • data (byte[]) — EEPROM 数据
  • length (int) — 参与计算的数据长度

返回值:

  • byte — CRC-8 校验值

示例:

byte[] eepromData = /* 从从站读取的 EEPROM 数据 */;
byte crc = ESI.calculateEepromCrc(eepromData, 14); // 前 14 字节参与 CRC
System.out.printf("CRC: 0x%02X%n", crc);

ESI.validateEepromCrc

public static boolean validateEepromCrc(byte[] eepromData)

校验 EEPROM 数据的 CRC 是否正确。自动读取数据中的 CRC 字段并与计算值比较。

参数:

  • eepromData (byte[]) — 完整 EEPROM 数据(至少 15 字节)

返回值:

  • boolean — CRC 校验通过返回 true

示例:

byte[] eeprom = /* 从从站读取的 EEPROM 数据 */;
if (ESI.validateEepromCrc(eeprom)) {
System.out.println("EEPROM CRC 校验通过");
} else {
System.out.println("EEPROM CRC 校验失败,数据可能已损坏");
}

注意事项

性能优化

ESI 文件加载可能需要几秒钟。建议在应用启动时一次性 EsiManager.loadPath(EsiManager.defaultPath()) 加载,避免运行时延迟。

文件路径

确保 ESI 文件路径正确且文件有效。无效的 ESI 文件会被自动跳过,不会影响其他文件的加载。

重复加载

EsiManager 内部对同名文件去重,多次 loadPath / addFile 同一文件不会重复解析。

相关功能

ESI 设备信息在主站构造从站管理和 ENI 配置中均有使用。从站级 ESI 属性请通过 slave.Esi() 访问(HasEsi / EsiVersion / VendorName / HasMDP / SetEsiFile)。