ESI 文件管理 (EsiManager / ESI)
EsiManager 与 ESI 工具类提供 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 同一文件不会重复解析。