ESI 文件管理
主站运行时 ESI 走 DLL EcEsi_LoadFile / EcEsi_LoadDirectory,通过 master.set_esi_file() / master.set_esi_files() 调用。Windows 路径按 mbcs(系统 ANSI 代码页)传入 native。
EsiManager 静态类提供 Python 侧 ESI 文件缓存与查询。ESI 文件包含从站的完整配置信息。
EsiManager(darra_ethercat.utils.esi) — ESI 文件加载 / 缓存的静态类EsiLoader(darra_ethercat.slave.esi) — 解析单个 ESI 文件,提取设备信息与启动参数EsiDeviceInfo/EsiStartupParam(darra_ethercat.slave.esi) —EsiLoader解析出的设备信息 / 启动参数数据类
加载和管理
load_path()
@classmethod
def load_path(cls, path: str,
progress_callback: Optional[Callable[[int, int, str], None]] = None) -> None
加载指定路径下的所有 ESI 文件。内部自动跳过已加载文件(等效增量加载)。
参数:
path(str) — ESI 文件目录路径progress_callback(Callable, 可选) — 进度回调(current, total, file_name)
示例:
from darra_ethercat.utils.esi import EsiManager
# 加载默认路径
EsiManager.load_default()
# 加载指定路径,显示进度
EsiManager.load_path(r"C:\EtherCAT\ESI", lambda cur, total, name:
print(f"加载ESI文件: {cur}/{total} - {name}"))
preload()
@classmethod
def preload(cls, progress_callback: Optional[Callable[[int, int, str], None]] = None) -> None
预加载默认路径的 ESI 文件。
示例:
# 启动时预加载
EsiManager.preload(lambda cur, total, name:
print(f"\r加载ESI: {cur * 100 // total}% - {name}", end=""))
print("\nESI文件加载完成")
load_default()
@classmethod
def load_default(cls,
progress_callback: Optional[Callable[[int, int, str], None]] = None) -> None
从默认路径加载 ESI 文件(与 preload() 等价,显式语义版本)。
示例:
EsiManager.load_default()
clear()
@classmethod
def clear(cls) -> None
清除所有已加载的 ESI 文件。
示例:
# 清除并重新加载
EsiManager.clear()
EsiManager.load_path(r"C:\NewESI")
remove_from_cache()
@classmethod
def remove_from_cache(cls, file_name: str) -> None
从缓存中移除指定 ESI 文件。
示例:
EsiManager.remove_from_cache("MyVendor_EL7211.xml")
查询和检索
要拿到结构化的设备信息(厂商 / 产品 / 版本 / 设备名等),用 EsiLoader 加载某个 ESI 文件后读 loader.device_info(EsiDeviceInfo,见下文 ESI 绑定到从站)。EsiManager 静态类只负责文件级加载 / 缓存与 ConfigData 字节查询(get_config_data_bytes),不解析结构化设备信息。
get_config_data_bytes()
@classmethod
def get_config_data_bytes(cls, vendor_id: int, product_code: int,
revision: int = 0) -> Optional[bytes]
获取 ESI 配置数据的原始字节。在已加载的 ESI 文件中查找匹配的设备,返回其 EEPROM 配置数据。
参数:
vendor_id(int) — 厂商 IDproduct_code(int) — 产品代码revision(int, 可选) — 修订号,0 表示不限
返回值:
Optional[bytes]— 配置数据字节,未找到返回None
示例:
config = EsiManager.get_config_data_bytes(0x00000002, 0x03F03052)
if config is not None:
print(f"配置数据: {config.hex()}")
find_esi_file()
def find_esi_file(filename: str, search_paths: Optional[List[str]] = None) -> Optional[str]
根据文件名查找 ESI 文件完整路径。
参数:
filename(str) — ESI 文件名search_paths(List[str], 可选) — 搜索路径列表,None使用默认路径
返回值:
Optional[str]— 完整路径,未找到返回None
示例:
from darra_ethercat.utils.esi import find_esi_file
path = find_esi_file("Beckhoff EL1008.xml")
if path is not None:
print(f"找到文件: {path}")
状态查询
loaded_file_count()
@classmethod
def loaded_file_count(cls) -> int
获取已加载的 ESI 文件数量。
示例:
count = EsiManager.loaded_file_count()
print(f"已加载 {count} 个ESI文件")
is_file_loaded()
@classmethod
def is_file_loaded(cls, file_name: str) -> bool
检查指定 ESI 文件是否已加载。
示例:
if EsiManager.is_file_loaded("MyDevice.xml"):
print("自定义设备ESI已加载")
is_preloaded()
@classmethod
def is_preloaded(cls) -> bool
检查是否已预加载(即是否有任何已加载文件)。
示例:
if not EsiManager.is_preloaded():
EsiManager.preload()
默认路径
default_path()
@classmethod
def default_path(cls) -> str
获取默认 ESI 文件搜索路径。
default_path() 返回 <site-packages>/darra_ethercat/esi,该目录不随 SDK wheel 发布,pip 全新安装后通常不存在。ESI 由 Windows 安装器投放到 C:\DARRA\EtherCAT_Master\ESI,加载用 EsiManager.load_path(r"C:\DARRA\EtherCAT_Master\ESI")。默认目录缺失时 load_default() / preload() 记 WARNING,不加载任何文件。
返回值:
str— 默认 ESI 目录路径
示例:
path = EsiManager.default_path()
print(f"默认ESI路径: {path}")
ESI 绑定到从站
运行时参与拓扑 / PDO / DC / 邮箱协议解析的 ESI 绑定在 DLL 侧完成:set_esi_file() / set_esi_files() 载入后,build() 扫站时经 EcEsi_AutoMatchAll 自动匹配到从站。Python 侧 EsiLoader 类负责解析单个 ESI 文件:提取设备信息(device_info)与启动参数(startup_params),挂到从站的 slave.esi 后,CoE 对象字典目录会优先读取该 ESI 树(免 SDO 往返)。
EsiLoader.load()
class EsiLoader:
def load(self, file_path: str) -> bool
解析指定 ESI XML 文件,提取设备信息与启动参数。常用于需要为某站读取特定厂商版本 ESI 的场景。
参数:
file_path(str) — ESI XML 文件完整路径
返回值:
bool— 解析成功返回True; 文件无效 / 解析失败返回False
示例:
from darra_ethercat.slave.esi import EsiLoader
# 独立解析;也可用 master[2].esi 挂到从站,CoE 对象字典目录会用到
loader = EsiLoader(slave=master[2])
ok = loader.load(r"D:\esi\Vendor_Drive_v3.xml")
if not ok:
print("加载失败, 检查文件路径和 XML 内容")
EsiLoader.is_loaded / file_path / device_info
解析后查询 ESI 加载状态、文件路径与解析出的设备信息。
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 加载状态 | is_loaded | bool | 只读 | ESI 文件是否已加载 |
| 加载状态 | file_path | Optional[str] | 只读 | 已加载的 ESI 文件路径 |
| 设备信息 | device_info | EsiDeviceInfo | 只读 | 解析出的设备信息(厂商 / 产品 / 版本 / 设备名) |
| 启动参数 | startup_params | List[EsiStartupParam] | 只读 | 解析出的启动参数(SDO 初始化项)列表 |
示例:
loader = EsiLoader(slave=master[2])
loader.load(r"D:\esi\Vendor.xml")
if loader.is_loaded:
print(f"ESI 文件: {loader.file_path}")
print(f"设备名: {loader.device_info.device_name}")
print(f"启动参数数量: {len(loader.startup_params)}")
EsiLoader.load() 只解析文件,不依赖总线状态;CoE 对象字典目录读取时优先用 slave.esi 已加载的 ESI 树。PDO 配置 / DC 启用 / 邮箱协议建立所用的 ESI 绑定由 DLL 自动完成(ESI 经 set_esi_file / set_esi_files 载入,build() 扫站后 EcEsi_AutoMatchAll 匹配)。
ESI 版本匹配
match_revision()
from darra_ethercat.utils.esi import match_revision, ESIRevisionCheckStrategy
def match_revision(actual: int, expected: int,
strategy: ESIRevisionCheckStrategy) -> bool
根据指定策略判断从站实际版本号与 ESI 期望版本号是否匹配(模块级函数)。
ESIRevisionCheckStrategy 枚举:
class ESIRevisionCheckStrategy(IntEnum):
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位等于或大于
EEPROM CRC 工具
calculate_eeprom_crc()
def calculate_eeprom_crc(data: bytes, length: int) -> int
计算 EEPROM 数据的 CRC-8 校验值。使用 ETG.1000.6 / ETG.2010 定义的 CRC-8 算法(多项式 0x07,初始值 0xFF)。
参数:
data(bytes) — EEPROM 数据length(int) — 参与计算的数据长度
返回值:
int— CRC-8 校验值
示例:
from darra_ethercat.utils.esi import calculate_eeprom_crc
eeprom_data = slave.read_eeprom(0, 16)
crc = calculate_eeprom_crc(eeprom_data, 14) # 前 14 字节参与 CRC
print(f"CRC: 0x{crc:02X}")
validate_eeprom_crc()
def validate_eeprom_crc(eeprom_data: bytes) -> bool
校验 EEPROM 数据的 CRC 是否正确。自动读取数据中的 CRC 字段并与计算值比较。
参数:
eeprom_data(bytes) — 完整 EEPROM 数据(至少 15 字节)
返回值:
bool— CRC 校验通过返回True
示例:
from darra_ethercat.utils.esi import validate_eeprom_crc
eeprom = slave.read_eeprom(0, 128)
if validate_eeprom_crc(eeprom):
print("EEPROM CRC 校验通过")
else:
print("EEPROM CRC 校验失败,数据可能已损坏")
完整示例
应用启动时预加载 ESI
from darra_ethercat.utils.esi import EsiManager
print("正在加载ESI文件...")
EsiManager.preload(lambda cur, total, name:
print(f"\r进度: {cur * 100 // total}% ({cur}/{total}) - {name}", end=""))
print(f"\n成功加载 {EsiManager.loaded_file_count()} 个ESI文件")
从站识别 (基础属性)
from darra_ethercat import EtherCATMaster, EcState
from darra_ethercat.utils.esi import EsiManager
# 预加载 ESI
EsiManager.preload()
with EtherCATMaster() as master:
master.set_eni(r"C:\config.deni")
master.set_network(r"\Device\NPF_{GUID}")
master.set_state(EcState.OP)
# 遍历从站,读取从站基础标识属性
for slave in master.slaves:
print(f"[{slave.slave_num}] {slave.name} "
f"VID=0x{slave.vendor_id:08X} PID=0x{slave.product_id:08X} "
f"Rev=0x{slave.rev_id:08X}")
缓存管理
from darra_ethercat.utils.esi import EsiManager
# 检查特定文件是否已加载
if EsiManager.is_file_loaded("MyVendor_EL7211.xml"):
print("自定义设备 ESI 已加载")
# 从缓存中移除特定文件(例如更新 ESI 后重新加载)
EsiManager.remove_from_cache("MyVendor_EL7211.xml")
EsiManager.load_path(r"C:\ESI\Updated")
# 完全清除并重新加载
EsiManager.clear()
EsiManager.load_path(r"C:\ESI")
print(f"重新加载完成: {EsiManager.loaded_file_count()} 个文件")
自定义设备支持
import os
from darra_ethercat.utils.esi import EsiManager
def load_custom_devices(custom_esi_path: str):
# 加载标准 ESI
EsiManager.preload()
# 增量加载自定义设备 ESI
if os.path.isdir(custom_esi_path):
EsiManager.load_path(custom_esi_path)
print("自定义设备ESI已加载")
ESI 诊断工具
from darra_ethercat.utils.esi import EsiManager
def generate_esi_report():
print("=== ESI诊断报告 ===")
print()
# 基本统计
file_count = EsiManager.loaded_file_count()
print(f"已加载文件数: {file_count}")
print(f"默认路径: {EsiManager.default_path()}")
print()
# 检查预加载状态
if EsiManager.is_preloaded():
print("ESI 已预加载")
else:
print("ESI 未预加载")
ESI 管理器封装
from darra_ethercat.utils.esi import EsiManager
import os
class ESIManagerWrapper:
_initialized = False
@classmethod
def initialize(cls, custom_path: str = None):
if cls._initialized:
return
print("初始化ESI管理器...")
# 加载默认 ESI 文件
print("加载标准ESI文件...")
EsiManager.preload(lambda cur, total, name:
print(f"\r 进度: {cur * 100 // total}% ({cur}/{total}) - {name}", end=""))
# 加载自定义 ESI 文件
if custom_path and os.path.isdir(custom_path):
print(f"\n加载自定义ESI文件: {custom_path}")
EsiManager.load_path(custom_path)
cls._initialized = True
print(f"\nESI初始化完成,共加载 {EsiManager.loaded_file_count()} 个文件")
@classmethod
def reload(cls):
print("重新加载ESI文件...")
EsiManager.clear()
cls._initialized = False
cls.initialize()
总线配置加载
用户加载 ENI / DENI / JSON 只走 master.set_eni() 或 master.load_config_json()。两者都把内容交给 DLL LoadConfigJson(XML 或 JSON)。
包 __all__ 不再导出 MasterXMLConfiguration / load_xml_configuration。save_xml / apply_xml / SaveXML 不是产品 API(XML 读写仅 GUI / PLC 内部路径)。保存配置 / 启动快照在主站自愈窗口内采用有限等待(秒级上限,短轮询),超时返回错误,不会长时间挂起。
导出运行时配置为 DENI 文件由 Darra 主界面和 PLC 中间层提供,不通过 SDK 对外开放。
注意事项
ESI 文件加载可能需要几秒钟。建议在应用启动时使用 preload() 预加载,避免运行时延迟。
运行时 ESI 走 master.set_esi_file / set_esi_files → DLL EcEsi_LoadFile / EcEsi_LoadDirectory。Windows 路径按 mbcs(系统 ANSI 代码页)传入 native,不能 UTF-8。EsiManager 缓存加载跳过无效文件,不影响其他文件。
EsiManager.load_path() 自动跳过已加载文件,等同于增量加载,可以避免重复加载相同文件,提高效率。