跳到主要内容

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) — 厂商ID
  • productId (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() 可以避免重复加载相同文件,提高效率。

相关功能

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