跳到主要内容

ESI 文件管理 (EsiManager)

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

通过 EsiManager 类访问。

加载和管理

LoadPath()

int LoadPath(const std::string& dirPath,
std::function<void(int, int, const std::string&)> progressCallback = {});

加载指定路径下的所有 ESI 文件。

参数:

  • dirPath (std::string) — ESI 文件目录路径
  • progressCallback (std::function) — 进度回调 (current, total, fileName), 可省略

返回值:

  • int — 成功加载的文件数

示例:

EsiManager mgr(&dll);
mgr.LoadPath("C:/EtherCAT/ESI",
[](int current, int total, const std::string& fileName) {
printf("加载 ESI 文件: %d/%d - %s\n", current, total, fileName.c_str());
});

LoadPathIncremental()

int LoadPathIncremental(const std::string& dirPath,
std::function<void(int, int, const std::string&)> progressCallback = {});

增量加载 ESI 文件 (仅加载未加载的文件)。

参数:

  • dirPath (std::string) — ESI 文件目录路径
  • progressCallback (std::function) — 进度回调 (current, total, fileName), 可省略

返回值:

  • int — 本次新加载的文件数

示例:

mgr.LoadPath("C:/ESI/Standard");
mgr.LoadPathIncremental("C:/ESI/Custom");

PreloadDefault()

int PreloadDefault(std::function<void(int, int, const std::string&)> progressCallback = {});

预加载默认 ESI 文件 (从默认路径)。

返回值:

  • int — 加载的文件数

示例:

mgr.PreloadDefault([](int current, int total, const std::string& fileName) {
int percent = current * 100 / total;
printf("\r加载 ESI: %d%% - %s", percent, fileName.c_str());
});
printf("\nESI 文件加载完成\n");

AddFile()

int AddFile(const std::string& filePath);

添加单个 ESI 文件到仓库。

参数:

  • filePath (std::string) — ESI 文件完整路径

返回值:

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

示例:

mgr.AddFile("C:/CustomDevices/MyDevice.xml");

Clear()

void Clear();

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

示例:

mgr.Clear();
mgr.LoadPath("C:/NewESI");

查询和检索

GetDeviceInfo()

std::optional<EsiDeviceInfo> GetDeviceInfo(uint32_t vendorId, uint32_t productId, uint32_t revisionId) const;

根据设备标识获取 ESI 设备信息。

参数:

  • vendorId (uint32_t) — 厂商 ID
  • productId (uint32_t) — 产品代码
  • revisionId (uint32_t) — 修订号

返回值:

  • std::optional<EsiDeviceInfo> — 设备信息, 未找到返回 std::nullopt

相关结构:

struct EsiDeviceInfo {
std::string product_name; // 产品名称
std::string device_class; // 设备类型
std::string group_type; // 分组类型
uint32_t vendor_id; // 厂商 ID
uint32_t product_id; // 产品代码
uint32_t revision_id; // 修订号
std::string physics; // 物理层类型 (如 "YKY", "KK")
};

示例:

auto info = mgr.GetDeviceInfo(0x00000002, 0x03F03052, 0x00120000);
if (info) {
printf("设备名称: %s\n", info->product_name.c_str());
printf("类型: %s\n", info->device_class.c_str());
printf("分组: %s\n", info->group_type.c_str());
}

GetMatchingDevice()

std::shared_ptr<EsiDevice> GetMatchingDevice(uint32_t vendorId, uint32_t productCode, uint32_t revisionId) const;

获取匹配的设备完整 ESI 数据。

返回值:

  • std::shared_ptr<EsiDevice> — ESI 设备节点, 未找到返回空指针

示例:

auto device = mgr.GetMatchingDevice(0x00000002, 0x03F03052, 0x00120000);
if (device) {
printf("设备: %s\n", device->name().c_str());
printf("RxPDO 数: %zu\n", device->rx_pdo().size());
}

FindEsiFile()

std::string FindEsiFile(const std::string& fileName) const;

根据文件名查找 ESI 文件完整路径。

参数:

  • fileName (std::string) — ESI 文件名

返回值:

  • std::string — 完整路径, 未找到返回空字符串

示例:

std::string path = mgr.FindEsiFile("Beckhoff EL1008.xml");
if (!path.empty()) {
printf("找到文件: %s\n", path.c_str());
}

状态查询

GetLoadedFileCount()

int GetLoadedFileCount() const;

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

示例:

int count = mgr.GetLoadedFileCount();
printf("已加载 %d 个 ESI 文件\n", count);

IsFileLoaded()

bool IsFileLoaded(const std::string& fileName) const;

检查指定 ESI 文件是否已加载。

示例:

if (mgr.IsFileLoaded("MyDevice.xml")) {
printf("自定义设备 ESI 已加载\n");
}

GetFiles()

std::vector<std::string> GetFiles() const;

获取所有已加载文件的路径列表。

返回值:

  • std::vector<std::string> — 已加载 ESI 文件路径

示例:

auto files = mgr.GetFiles();
printf("已加载的 ESI 文件:\n");
for (const auto& file : files) {
printf(" - %s\n", file.c_str());
}

GetAllFiles()

std::map<std::string, std::shared_ptr<EsiDevice>> GetAllFiles() const;

获取所有已加载文件的排序映射。

示例:

auto files = mgr.GetAllFiles();
for (const auto& [name, dev] : files) {
printf("%s\n", name.c_str());
}

默认路径

DefaultPath()

static std::string DefaultPath();

获取默认 ESI 文件搜索路径 (Windows: .\\ESI, Linux: ./ESI)。

返回值:

  • std::string — 默认 ESI 目录路径

示例:

std::string path = EsiManager::DefaultPath();
printf("默认 ESI 路径: %s\n", path.c_str());

ESI 绑定到从站

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

BindToSlave()

bool BindToSlave(uint16_t masterIndex, uint16_t slaveIndex, const std::string& esiFilePath);

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

参数:

  • masterIndex (uint16_t) — 主站编号
  • slaveIndex (uint16_t) — 从站索引 (1-based, 与从站编号一致)
  • esiFilePath (std::string) — ESI XML 文件完整路径

返回值:

  • bool — 绑定成功返回 true; 从站不存在 / 文件无效 / 索引越界返回 false

示例:

bool ok = mgr.BindToSlave(master.MasterNumber(), 2, "D:/esi/Vendor_Drive_v3.xml");
if (!ok) printf("绑定失败, 检查从站索引和文件路径\n");

ApplyAllSlaves()

int ApplyAllSlaves(uint16_t masterIndex);

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

参数:

  • masterIndex (uint16_t) — 主站编号

返回值:

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

示例:

mgr.LoadPath("D:/esi");
int matched = mgr.ApplyAllSlaves(master.MasterNumber());
printf("已为 %d / %u 个从站匹配 ESI\n", matched, master.SlaveCount());
与扫描流程的关系

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

ESI 版本匹配

MatchRevision()

static bool MatchRevision(uint32_t actual, uint32_t expected,
EsiRevisionCheckStrategy strategy = EsiRevisionCheckStrategy::EQ_OR_G);

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

参数:

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

相关结构:

enum class EsiRevisionCheckStrategy : uint8_t {
None = 0, // 不检查版本号
EQ = 1, // 完全匹配
EQ_OR_G = 2, // 等于或大于 (默认推荐)
LW_EQ = 3, // 低 16 位匹配
HW_EQ = 4, // 高 16 位匹配
LW_EQ_OR_G = 5, // 低 16 位等于或大于
HW_EQ_OR_G = 6 // 高 16 位等于或大于
};

返回值:

  • bool — 匹配成功返回 true

示例:

uint32_t slaveRev = 0x00130000;
uint32_t esiRev = 0x00120000;

bool exact = EsiManager::MatchRevision(slaveRev, esiRev, EsiRevisionCheckStrategy::EQ);
bool compat = EsiManager::MatchRevision(slaveRev, esiRev, EsiRevisionCheckStrategy::EQ_OR_G);
bool hwMatch = EsiManager::MatchRevision(slaveRev, esiRev, EsiRevisionCheckStrategy::HW_EQ);

EEPROM CRC 工具

CalculateEepromCrc()

static uint8_t CalculateEepromCrc(const uint8_t* data, int length);

计算 EEPROM 数据的 CRC-8 校验值 (ETG.2010 CRC-8/ITU 算法)。

参数:

  • data (const uint8_t*) — EEPROM 数据
  • length (int) — 参与计算的数据长度

返回值:

  • uint8_t — CRC-8 校验值

示例:

auto eeprom = slave.ReadEeprom(0, 16);
uint8_t crc = EsiManager::CalculateEepromCrc(eeprom.data(), 14);
printf("CRC: 0x%02X\n", crc);

ValidateEepromCrc()

static bool ValidateEepromCrc(const uint8_t* eepromData, int length);

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

参数:

  • eepromData (const uint8_t*) — 完整 EEPROM 数据
  • length (int) — 数据长度

返回值:

  • bool — CRC 校验通过返回 true

示例:

auto eeprom = slave.ReadEeprom(0, 128);
if (EsiManager::ValidateEepromCrc(eeprom.data(), eeprom.size())) {
printf("EEPROM CRC 校验通过\n");
} else {
printf("EEPROM CRC 校验失败, 数据可能已损坏\n");
}

完整示例

应用启动时预加载 ESI

int main() {
EsiManager mgr(&dll);
printf("正在加载 ESI 文件...\n");

int total = 0;
mgr.PreloadDefault([&total](int current, int t, const std::string& fileName) {
total = t;
int percent = current * 100 / t;
printf("\r进度: %d%% (%d/%d) - %s", percent, current, t, fileName.c_str());
});
printf("\n成功加载 %d 个 ESI 文件\n", total);
return 0;
}

从站识别和信息显示

void DisplaySlaveInfo(EsiManager& mgr, const Slave& slave) {
auto info = mgr.GetDeviceInfo(slave.VendorId(), slave.ProductId(), slave.RevId());
if (info) {
printf("从站信息:\n");
printf(" 名称: %s\n", info->product_name.c_str());
printf(" 类型: %s\n", info->device_class.c_str());
printf(" 分组: %s\n", info->group_type.c_str());
} else {
printf("未找到 ESI: VID=%X, PID=%X\n", slave.VendorId(), slave.ProductId());
}
}

ESI 管理器封装

class ESIManagerWrapper {
public:
void Initialize(const std::string& customPath = "") {
if (initialized_) return;
printf("初始化 ESI 管理器...\n");
mgr_.PreloadDefault(ProgressCallback);
if (!customPath.empty()) {
printf("\n加载自定义 ESI: %s\n", customPath.c_str());
mgr_.LoadPathIncremental(customPath, ProgressCallback);
}
initialized_ = true;
printf("\nESI 初始化完成, 共加载 %d 个文件\n", mgr_.GetLoadedFileCount());
}

void Reload() {
mgr_.Clear();
initialized_ = false;
Initialize();
}

private:
static void ProgressCallback(int current, int total, const std::string& fileName) {
int percent = current * 100 / total;
printf("\r 进度: %d%% (%d/%d) - %s", percent, current, total, fileName.c_str());
}

EsiManager mgr_;
bool initialized_ = false;
};

注意事项

性能优化

ESI 文件加载可能需要几秒钟。建议在应用启动时使用 PreloadDefault() 预加载, 避免运行时延迟。

文件路径

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

增量加载

使用 LoadPathIncremental() 可以避免重复加载相同文件, 提高效率。

相关功能

ESI 设备信息在 主站构造从站管理 和 ENI 配置中均有使用。