ESI 文件管理 (ESI)
ESI 类提供 EtherCAT Slave Information (ESI) 文件的加载、管理和查询功能。ESI文件包含从站的完整配置信息,包括设备标识、对象字典、PDO映射、DC配置等。
通过 ESI 类访问。
加载和管理
ESI.LoadPath()
public static void LoadPath()
public static void LoadPath(string path, Action<int, int, string>? progressCallback = null)
加载指定路径下的所有ESI文件。
参数:
path(string, 可选) — ESI文件目录路径,不指定则使用默认路径progressCallback(Action<int, int, string>?, 可选) — 进度回调(current, total, fileName)
示例:
// 加载默认路径
ESI.LoadPath();
// 加载指定路径,显示进度
ESI.LoadPath(@"C:\EtherCAT\ESI", (current, total, fileName) =>
{
Console.WriteLine($"加载ESI文件: {current}/{total} - {fileName}");
});
ESI.LoadPathIncremental()
public static void LoadPathIncremental(string path, Action<int, int, string>? progressCallback = null)
增量加载ESI文件(仅加载未加载的文件)。
参数:
path(string) — ESI文件目录路径progressCallback(Action<int, int, string>?, 可选) — 进度回调(current, total, fileName)
示例:
// 首次加载
ESI.LoadPath(@"C:\ESI\Standard");
// 增量加载新增的ESI文件
ESI.LoadPathIncremental(@"C:\ESI\Custom");
ESI.PreloadDefault()
public static void PreloadDefault(Action<int, int, string>? progressCallback = null)
预加载默认ESI文件(从默认路径)。
示例:
// 启动时预加载
ESI.PreloadDefault((current, total, fileName) =>
{
int percent = current * 100 / total;
Console.Write($"\r加载ESI: {percent}% - {fileName}");
});
Console.WriteLine("\nESI文件加载完成");
ESI.AddFile()
public static void AddFile(string filePath)
添加单个ESI文件到仓库。
参数:
filePath(string) — ESI文件完整路径
示例:
// 添加自定义从站的ESI文件
ESI.AddFile(@"C:\CustomDevices\MyDevice.xml");
ESI.Clear()
public static void Clear()
清除所有已加载的ESI文件。
示例:
// 清除并重新加载
ESI.Clear();
ESI.LoadPath(@"C:\NewESI");
查询和检索
ESI.GetDeviceInfo()
public static ESIDeviceInfo? GetDeviceInfo(uint vendorId, uint productId, uint revisionId)
根据设备标识获取ESI设备信息。
参数:
vendorId(uint) — 厂商IDproductId(uint) — 产品代码revisionId(uint) — 修订号
返回值:
ESIDeviceInfo?— 设备信息,未找到返回null
相关结构:
public class ESIDeviceInfo
{
public string ProductName { get; set; } // 产品名称
public string DeviceClass { get; set; } // 设备类型
public string GroupType { get; set; } // 分组类型
public uint VendorId { get; set; } // 厂商ID
public uint ProductId { get; set; } // 产品代码
public uint RevisionId { get; set; } // 修订号
public string Physics { get; set; } // 物理层类型 (如 "YKY", "KK")
}
示例:
// 查询Beckhoff EL1008数字输入模块
var deviceInfo = ESI.GetDeviceInfo(0x00000002, 0x03F03052, 0x00120000);
if (deviceInfo != null)
{
Console.WriteLine($"设备名称: {deviceInfo.ProductName}");
Console.WriteLine($"类型: {deviceInfo.DeviceClass}");
Console.WriteLine($"分组: {deviceInfo.GroupType}");
}
ESI.GetMatchingDevice()
public static dynamic? GetMatchingDevice(uint vendorId, uint productCode, uint revisionId)
获取匹配的设备完整ESI数据(动态对象)。
返回值:
dynamic?— ESI XML数据的动态对象表示
示例:
var device = ESI.GetMatchingDevice(0x00000002, 0x03F03052, 0x00120000);
if (device != null)
{
// 访问ESI XML内容
Console.WriteLine($"设备: {device.Name}");
Console.WriteLine($"RxPDO数: {device.RxPdo?.Count ?? 0}");
}
ESI.FindEsiFile()
public static string FindEsiFile(string fileName)
根据文件名查找ESI文件完整路径。
参数:
fileName(string) — ESI文件名
返回值:
string— 完整路径,未找到返回空字符串
示例:
string path = ESI.FindEsiFile("Beckhoff EL1008.xml");
if (!string.IsNullOrEmpty(path))
{
Console.WriteLine($"找到文件: {path}");
}
状态查询
ESI.GetLoadedFileCount()
public static int GetLoadedFileCount()
获取已加载的ESI文件数量。
示例:
int count = ESI.GetLoadedFileCount();
Console.WriteLine($"已加载 {count} 个ESI文件");
ESI.IsFileLoaded()
public static bool IsFileLoaded(string fileName)
检查指定ESI文件是否已加载。
示例:
if (ESI.IsFileLoaded("MyDevice.xml"))
{
Console.WriteLine("自定义设备ESI已加载");
}
ESI.GetFiles()
public static IReadOnlyDictionary<string, dynamic> GetFiles()
获取所有已加载文件的只读字典。
返回值:
IReadOnlyDictionary\<string, dynamic\>— 文件名到ESI数据的映射
示例:
var files = ESI.GetFiles();
Console.WriteLine("已加载的ESI文件:");
foreach (var file in files.Keys)
{
Console.WriteLine($" - {file}");
}
ESI.GetAllFiles()
public static SortedList<string, dynamic> GetAllFiles()
获取所有已加载文件的排序列表。
示例:
var files = ESI.GetAllFiles();
foreach (var file in files)
{
Console.WriteLine($"{file.Key}: {file.Value}");
}
默认路径
ESI.DefaultPath()
public static string DefaultPath()
获取默认ESI文件搜索路径。
返回值:
string— 默认ESI目录路径
示例:
string defaultPath = ESI.DefaultPath();
Console.WriteLine($"默认ESI路径: {defaultPath}");
完整示例
应用启动时预加载ESI
class Program
{
static void Main()
{
Console.WriteLine("正在加载ESI文件...");
int totalFiles = 0;
ESI.PreloadDefault((current, total, fileName) =>
{
totalFiles = total;
int percent = current * 100 / total;
Console.Write($"\r进度: {percent}% ({current}/{total}) - {fileName}");
});
Console.WriteLine($"\n成功加载 {totalFiles} 个ESI文件");
// 启动主站...
}
}
从站识别和信息显示
public void DisplaySlaveInfo(DarraEtherCAT.Slave slave)
{
var info = ESI.GetDeviceInfo(
slave.VendorId,
slave.ProductId,
slave.RevId);
if (info != null)
{
Console.WriteLine("从站信息:");
Console.WriteLine($" 名称: {info.ProductName}");
Console.WriteLine($" 类型: {info.DeviceClass}");
Console.WriteLine($" 分组: {info.GroupType}");
}
else
{
Console.WriteLine($"未找到ESI: VID={slave.VendorId:X}, " +
$"PID={slave.ProductId:X}");
}
}
自定义设备支持
public class CustomDeviceManager
{
public static void LoadCustomDevices(string customEsiPath)
{
// 加载标准ESI
ESI.PreloadDefault();
// 增量加载自定义设备ESI
if (Directory.Exists(customEsiPath))
{
ESI.LoadPathIncremental(customEsiPath);
Console.WriteLine("自定义设备ESI已加载");
}
}
public static bool IsCustomDevice(uint vendorId, uint productCode)
{
var info = ESI.GetDeviceInfo(vendorId, productCode, 0);
return info != null && info.DeviceClass.Contains("Custom");
}
}
ESI诊断工具
public class ESIDiagnostics
{
public static void GenerateReport()
{
Console.WriteLine("=== ESI诊断报告 ===");
Console.WriteLine();
// 基本统计
int fileCount = ESI.GetLoadedFileCount();
Console.WriteLine($"已加载文件数: {fileCount}");
Console.WriteLine($"默认路径: {ESI.DefaultPath()}");
Console.WriteLine();
// 列出所有文件
Console.WriteLine("已加载的ESI文件:");
var files = ESI.GetFiles();
foreach (var file in files.Keys)
{
Console.WriteLine($" ✓ {file}");
}
// 检查常见设备
Console.WriteLine();
Console.WriteLine("常见设备检查:");
CheckDevice("Beckhoff EL1008", 0x00000002, 0x03F03052);
CheckDevice("Beckhoff EL2008", 0x00000002, 0x07D83052);
}
static void CheckDevice(string name, uint vid, uint pid)
{
var info = ESI.GetDeviceInfo(vid, pid, 0);
string status = info != null ? "✓ 已加载" : "✗ 未找到";
Console.WriteLine($" {name}: {status}");
}
}
ESI管理器
using System;
using System.IO;
using DarraEtherCAT_Master;
public class ESIManager
{
private static bool _initialized = false;
public static void Initialize(string customPath = null)
{
if (_initialized) return;
Console.WriteLine("初始化ESI管理器...");
// 加载默认ESI文件
Console.WriteLine("加载标准ESI文件...");
ESI.PreloadDefault(ProgressCallback);
// 加载自定义ESI文件
if (!string.IsNullOrEmpty(customPath) && Directory.Exists(customPath))
{
Console.WriteLine($"\n加载自定义ESI文件: {customPath}");
ESI.LoadPathIncremental(customPath, ProgressCallback);
}
_initialized = true;
Console.WriteLine($"\n✓ ESI初始化完成,共加载 {ESI.GetLoadedFileCount()} 个文件");
}
public static ESIDeviceInfo? FindDevice(uint vendorId, uint productCode)
{
if (!_initialized)
{
Console.WriteLine("警告:ESI管理器未初始化");
return null;
}
return ESI.GetDeviceInfo(vendorId, productCode, 0);
}
public static void ListAllDevices()
{
var files = ESI.GetAllFiles();
Console.WriteLine($"ESI库包含 {files.Count} 个设备:");
foreach (var file in files)
{
Console.WriteLine($" - {file.Key}");
}
}
public static void Reload()
{
Console.WriteLine("重新加载ESI文件...");
ESI.Clear();
_initialized = false;
Initialize();
}
private static void ProgressCallback(int current, int total, string fileName)
{
int percent = current * 100 / total;
Console.Write($"\r 进度: {percent}% ({current}/{total}) - {fileName}");
}
}
ESI 绑定到从站
加载到 ESI 缓存后, 还需要将 ESI 设备节点绑定到具体从站, 才能让 SDK 在拓扑/PDO/DC/邮箱协议解析时使用 ESI 信息。
BindToSlave()
public static bool BindToSlave(DarraEtherCAT master, ushort slaveIndex, string esiFilePath)
为单个从站绑定指定 ESI 文件。常用于已知拓扑、需要为某站强制使用某个版本 ESI 的场景。
参数:
master(DarraEtherCAT) — 主站实例slaveIndex(ushort) — 从站索引(1-based, 与从站编号一致)esiFilePath(string) — ESI XML 文件完整路径
返回值:
bool— 绑定成功返回true;从站不存在 / 文件无效 / 索引越界返回false
示例:
// 给从站 #2 绑定特定厂商版本的 ESI
bool ok = ESI.BindToSlave(master, 2, @"D:\esi\Vendor_Drive_v3.xml");
if (!ok) Console.WriteLine("绑定失败, 检查从站索引和文件路径");
ApplyAllSlaves()
public static int ApplyAllSlaves(DarraEtherCAT master)
遍历主站下所有从站, 按 VendorId + ProductId 在已加载缓存中查找匹配的 ESI 设备并自动绑定。调用前应先 LoadPath() / PreloadDefault() 把 ESI 文件加载进缓存。
参数:
master(DarraEtherCAT) — 主站实例
返回值:
int— 成功匹配并绑定的从站数
示例:
// 1. 先把 ESI 库整体加载进缓存
ESI.LoadPath(@"D:\esi");
// 2. 扫描后一键给所有从站绑定匹配的 ESI
int matched = ESI.ApplyAllSlaves(master);
Console.WriteLine($"已为 {matched} / {master.SlaveCount} 个从站匹配 ESI");
ApplyAllSlaves 应在 Build() 完成、从站列表就绪之后调用。SDK 在 PDO 配置 / DC 启用 / 邮箱协议建立阶段会自动消费已绑定的 ESI 信息。
ESI 版本匹配
MatchRevision()
public static bool MatchRevision(uint actual, uint expected, ESIRevisionCheckStrategy strategy)
根据指定策略判断从站实际版本号与 ESI 期望版本号是否匹配。
参数:
actual(uint) — 从站实际上报的版本号expected(uint) — ESI 文件中定义的期望版本号strategy(ESIRevisionCheckStrategy) — 匹配策略
相关结构:
public enum ESIRevisionCheckStrategy
{
None, // 不检查版本号
EQ, // 完全匹配
EQ_OR_G, // 等于或大于
LW_EQ, // 低16位匹配
HW_EQ, // 高16位匹配
LW_EQ_OR_G, // 低16位等于或大于
HW_EQ_OR_G // 高16位等于或大于
}
返回值:
bool— 匹配成功返回true
示例:
uint slaveRev = 0x00130000; // actual: 从站实际版本
uint esiRev = 0x00120000; // expected: ESI期望版本
// 严格匹配 — 不通过
bool exact = ESI.MatchRevision(slaveRev, esiRev, ESIRevisionCheckStrategy.EQ);
// 允许更高版本 — 通过 (actual >= expected)
bool compat = ESI.MatchRevision(slaveRev, esiRev, ESIRevisionCheckStrategy.EQ_OR_G);
// 仅比较高16位 — 不通过 (0x0013 != 0x0012)
bool hwMatch = ESI.MatchRevision(slaveRev, esiRev, ESIRevisionCheckStrategy.HW_EQ);
EEPROM CRC 工具
CalculateEepromCrc()
public static byte CalculateEepromCrc(byte[] data, int length)
计算 EEPROM 数据的 CRC-8 校验值。使用 ETG.2010 定义的 CRC-8/ITU 算法。
参数:
data(byte[]) — EEPROM 数据length(int) — 参与计算的数据长度
返回值:
byte— CRC-8 校验值
示例:
byte[] eepromData = slave.ReadEeprom(0, 16);
byte crc = ESI.CalculateEepromCrc(eepromData, 14); // 前 14 字节参与 CRC
Console.WriteLine($"CRC: 0x{crc:X2}");
ValidateEepromCrc()
public static bool ValidateEepromCrc(byte[] eepromData)
校验 EEPROM 数据的 CRC 是否正确。自动读取数据中的 CRC 字段并与计算值比较。
参数:
eepromData(byte[]) — 完整 EEPROM 数据(至少包含 CRC 字段)
返回值:
bool— CRC 校验通过返回true
示例:
byte[] eeprom = slave.ReadEeprom(0, 128);
if (ESI.ValidateEepromCrc(eeprom))
{
Console.WriteLine("EEPROM CRC 校验通过");
}
else
{
Console.WriteLine("EEPROM CRC 校验失败,数据可能已损坏");
}
注意事项
ESI文件加载可能需要几秒钟。建议在应用启动时使用 PreloadDefault() 预加载,避免运行时延迟。
确保ESI文件路径正确且文件有效。无效的ESI文件会被自动跳过,不会影响其他文件的加载。
使用 LoadPathIncremental() 可以避免重复加载相同文件,提高效率。