跳到主要内容

ESI 文件管理 (Esi)

EtherCAT Slave Information (ESI) 文件包含从站的完整配置信息,包括设备标识、对象字典、PDO 映射、DC 配置和启动参数 (InitCmd)。Rust SDK 提供两套互补的 ESI 接口:

  • EsiLoader —— 每个从站一个的加载器实例,通过 slave.esi() 获取,把 ESI 文件解析后自动把启动参数注入该从站。
  • EsiManager —— 进程级 ESI 仓库(位于 darra_ethercat::utils::esi),静态方法管理 ESI 文件目录、批量绑定从站。
  • 一组模块级自由函数 —— load_esi_file() / load_and_apply_startup() / match_revision() / calculate_eeprom_crc() / validate_eeprom_crc() / load_eni() / save_eni()

EsiLoader —— 从站级 ESI 加载器

EsiLoader 通过 slave.esi() 获取(直接返回实例,不是 Option)。它持有对应从站句柄,load() 时解析 ESI 文件并把其中的 InitCmd 启动参数自动添加到该从站。

方法类别读写说明
load(file_path)ESI 加载操作解析 ESI 文件并注入从站启动参数
is_loaded()状态只读ESI 文件是否已加载
file_path()状态只读已加载的 ESI 文件路径
device_info()设备信息只读解析后的设备信息

load()

pub fn load(&mut self, file_path: &str) -> bool

加载 ESI XML 文件:解析设备信息并提取 InitCmd 启动参数 (SDO init),自动添加到从站的启动参数列表。成功返回 true,文件不存在或解析失败返回 false

is_loaded() / file_path() / device_info()

pub fn is_loaded(&self) -> bool
pub fn file_path(&self) -> Option<&str>
pub fn device_info(&self) -> Option<&EsiDeviceInfo>

示例:

let slave = master.slave(1);
let mut esi = slave.esi(); // 直接返回 EsiLoader (非 Option)

if esi.load(r"C:\EtherCAT\ESI\Beckhoff EL1008.xml") {
if let Some(info) = esi.device_info() {
println!("设备: {} ({})", info.product_name, info.device_class);
}
println!("ESI 路径: {:?}", esi.file_path());
}

EsiLoader 也实现 Display,可直接 println!("{}", esi)

EsiManager —— 进程级 ESI 仓库

EsiManagerdarra_ethercat::utils::esi::EsiManager)是一组静态方法,管理整个进程的 ESI 文件缓存,并可批量绑定从站。

方法类别读写说明
default_path()路径只读默认 ESI 目录 (<cwd>/ESI)
add_file(file_path)加载操作加载单个 ESI 文件,返回 Device 数
load_path(dir_path)加载操作加载目录下全部 .xml/.esi 文件
bind_to_slave(mi, si, file_path)绑定操作给指定从站绑定 ESI 文件
apply_all_slaves(mi)绑定操作自动给主站下所有从站匹配并应用 ESI
get_loaded_count()状态只读已加载 ESI 文件数
get_files()状态只读已加载文件路径快照
clear()维护操作清空 managed + DLL 缓存
match_revision(actual, expected, strategy)版本只读版本号匹配判断

default_path()

pub fn default_path() -> String

返回默认 ESI 目录,即 <进程当前目录>/ESI(不存在时会自动创建)。

add_file() / load_path()

pub fn add_file(file_path: &str) -> i32
pub fn load_path(dir_path: &str) -> i32

add_file() 加载单个 ESI 文件,load_path() 加载目录下所有 .xml / .esi 文件。两者均返回 DLL 报告的 Device 数(FFI 不可用时返回 0)。

示例:

use darra_ethercat::utils::esi::EsiManager;

// 加载默认目录
let n = EsiManager::load_path(&EsiManager::default_path());
println!("从默认目录加载了 {} 个设备", n);

// 加载指定目录 / 单个文件
EsiManager::load_path(r"C:\EtherCAT\ESI");
EsiManager::add_file(r"C:\CustomDevices\MyDevice.xml");

bind_to_slave() / apply_all_slaves()

pub fn bind_to_slave(master_index: u16, slave_index: u16, file_path: &str) -> i32
pub fn apply_all_slaves(master_index: u16) -> i32

bind_to_slave() 给单个从站绑定指定 ESI 文件;apply_all_slaves() 自动给主站下所有从站匹配并应用 ESI。

get_loaded_count() / get_files() / clear()

pub fn get_loaded_count() -> i32
pub fn get_files() -> Vec<String>
pub fn clear()

get_loaded_count() 返回已加载 ESI 文件数;get_files() 返回已加载文件路径快照 (Vec<String>);clear() 清空 managed 与 DLL 两侧缓存。

示例:

use darra_ethercat::utils::esi::EsiManager;

EsiManager::load_path(r"C:\EtherCAT\ESI");
println!("已加载 {} 个 ESI 文件", EsiManager::get_loaded_count());
for path in EsiManager::get_files() {
println!(" - {}", path);
}

// 重新加载
EsiManager::clear();
EsiManager::load_path(r"C:\EtherCAT\ESI");

EsiDeviceInfo

EsiLoader::device_info()load_esi_file() 返回的设备信息结构(节选主要字段):

pub struct EsiDeviceInfo {
pub product_name: String, // 产品名称
pub product_text: String, // 产品描述文本
pub product_url: String, // 产品 URL
pub device_class: String, // 设备类型
pub group_type: String, // 分组类型
pub file_name: String, // 来源 ESI 文件名
pub product_id: u32, // 产品代码
pub revision_id: u32, // 修订号
pub vendor_id: u32, // 厂商 ID
pub vendor_name: String, // 厂商名称
pub vendor_comment: String, // 厂商备注
pub physics: String, // 物理层类型 (如 "YY", "KK")
pub profile_number: String, // CoE Profile 号
pub revision_check_strategy: EsiRevisionCheckStrategy, // 版本检查策略
pub supports_coe: bool, // 支持 CoE
pub supports_eoe: bool, // 支持 EoE
pub supports_foe: bool, // 支持 FoE
pub supports_soe: bool, // 支持 SoE
pub supports_voe: bool, // 支持 VoE
pub supports_frame_repeat: bool, // 支持帧重复
// 以下为可选嵌套结构 (Option<...>):
pub coe_details: Option<EsiCoEDetails>,
pub dc_configuration: Option<EsiDcConfiguration>,
pub eeprom_configuration: Option<EsiEepromConfiguration>,
pub electrical: Option<EsiElectricalInfo>,
pub identification: Option<EsiIdentification>,
pub mailbox_timeout: Option<EsiMailboxTimeout>,
pub physics_info: Option<EsiPhysicsInfo>,
pub ports: Vec<EsiPortInfo>,
}
字段说明

EsiDeviceInfo 同时携带 product_id / revision_idvendor_id / vendor_name / vendor_comment(厂商信息由 ESI 文件的 Vendor 节点解析填入)。dc_assign_activate() / supports_dc() / protocol_summary() 是其便捷方法。

模块级自由函数

load_esi_file()

pub fn load_esi_file(path: &str) -> Result<EsiDeviceInfo, String>

直接解析单个 ESI XML 文件为 EsiDeviceInfo,不绑定任何从站。失败返回 Err(String)

use darra_ethercat::load_esi_file;

let info = load_esi_file(r"C:\EtherCAT\ESI\Beckhoff EL1008.xml")?;
println!("产品: {} (PC=0x{:08X}, Rev=0x{:08X})",
info.product_name, info.product_id, info.revision_id);

load_and_apply_startup()

pub fn load_and_apply_startup(mi: u16, si: u16, path: &str) -> Result<i32, String>

解析 ESI 文件,提取 InitCmd 中的 SDO 启动参数,通过 FFI 添加到指定从站。成功返回添加的参数数量。

use darra_ethercat::load_and_apply_startup;

let count = load_and_apply_startup(master.index(), 1,
r"C:\EtherCAT\ESI\MyDrive.xml")?;
println!("已应用 {} 条启动参数", count);

extract_startup_parameters()

pub fn extract_startup_parameters(xml: &str) -> Vec<EsiStartupParam>

从 ESI XML 文本中提取所有 InitCmd 启动参数。

pub struct EsiStartupParam {
pub index: u16, // 对象索引
pub subindex: u8, // 子索引
pub data: Vec<u8>, // 数据
pub transition: u8, // 状态转换类型
pub timing: u8, // 写入时序
pub complete_access: bool, // 完整访问标志
}

ESI 版本匹配

match_revision()

pub fn match_revision(actual: u32, expected: u32,
strategy: EsiRevisionCheckStrategy) -> bool

根据指定策略判断从站实际版本号与 ESI 期望版本号是否匹配。match_revision 是 crate 根重新导出的自由函数,EsiManager::match_revision() 是其同名转调。

#[derive(Debug, Clone, Copy)]
pub enum EsiRevisionCheckStrategy {
None, // 不检查版本号
Eq, // 完全匹配
EqOrG, // 等于或大于
LwEq, // 低 16 位匹配
HwEq, // 高 16 位匹配
LwEqOrG, // 低 16 位等于或大于
HwEqOrG, // 高 16 位等于或大于
}

示例:

use darra_ethercat::{match_revision, EsiRevisionCheckStrategy};

let slave_rev: u32 = 0x00130000; // actual: 从站实际版本
let esi_rev: u32 = 0x00120000; // expected: ESI 期望版本

// 严格匹配 — 不通过
let exact = match_revision(slave_rev, esi_rev, EsiRevisionCheckStrategy::Eq);

// 允许更高版本 — 通过 (actual >= expected)
let compat = match_revision(slave_rev, esi_rev, EsiRevisionCheckStrategy::EqOrG);

ENI 配置文件

ENI (EtherCAT Network Information, ETG.2100) 是网络级配置文件,描述完整的从站列表 / PDO 映射 / DC 设置 / 启动 SDO。load_eni() / save_eni()darra_ethercat::utils::esi 下的自由函数。

load_eni()

pub fn load_eni(path: &str) -> Result<MasterXmlConfiguration, String>

从 XML 文件解析 ENI/DENI 网络配置(内部走 utils::xml::load_xml_configuration,同时识别 <EtherCATConfig> 根 (ENI) 与旧版 DENI 根)。失败返回 Err(String)

use darra_ethercat::utils::esi::load_eni;

let cfg = load_eni(r"C:\projects\machine_a.eni")?;
// MasterXmlConfiguration 描述主站 + 从站配置

save_eni()

pub fn save_eni(path: &str, config: &MasterXmlConfiguration) -> Result<(), String>

把内存中的 MasterXmlConfiguration 序列化回 ENI XML 写入文件。失败返回 Err(String)

ENI vs JSON

日常推荐用 Darra 配置工具导出的 DENI 文件配合 EtherCATMaster::builder().set_eni(...) 加载;load_eni / save_eni 适合在 SDK 端做 ENI XML 的检查与互通。

EEPROM CRC 工具

calculate_eeprom_crc()

pub fn calculate_eeprom_crc(data: &[u8], length: usize) -> u8

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

validate_eeprom_crc()

pub fn validate_eeprom_crc(eeprom_data: &[u8]) -> bool

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

示例:

use darra_ethercat::validate_eeprom_crc;

let eeprom = slave.read_eeprom(0, 128)?; // read_eeprom 返回 Result<Vec<u8>>
if validate_eeprom_crc(&eeprom) {
println!("EEPROM CRC 校验通过");
} else {
println!("EEPROM CRC 校验失败,数据可能已损坏");
}

完整示例

应用启动时加载 ESI 仓库

use darra_ethercat::utils::esi::EsiManager;

println!("加载 ESI 文件...");
let n = EsiManager::load_path(&EsiManager::default_path());
println!("从默认目录加载了 {} 个设备", n);
println!("ESI 仓库共 {} 个文件", EsiManager::get_loaded_count());

给从站绑定 ESI 并应用启动参数

let slave = master.slave(1);
let mut esi = slave.esi();

if esi.load(r"C:\EtherCAT\ESI\MyDrive.xml") {
// load() 已自动把 InitCmd 启动参数注入从站
if let Some(info) = esi.device_info() {
println!("已绑定 ESI: {} (CoE={})", info.product_name, info.supports_coe);
}
} else {
println!("ESI 加载失败");
}

批量给所有从站匹配 ESI

use darra_ethercat::utils::esi::EsiManager;

EsiManager::load_path(r"C:\EtherCAT\ESI");
let applied = EsiManager::apply_all_slaves(master.index());
println!("已为 {} 个从站匹配并应用 ESI", applied);

注意事项

性能

ESI 文件解析可能耗时较长。建议在应用启动阶段一次性 EsiManager::load_path() 预加载整个目录。

文件路径

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