跳到主要内容

初始化

推荐方式

强烈建议使用 Darra 配置工具导出的 DENI 文件进行初始化。
DENI 文件包含完整的网络配置(网口、从站、PDO 映射、DC 设置等),可确保配置的一致性和正确性。
使用 DENI 文件管理设备硬件设施将获得更好的兼容性与通用性。

构造函数​

DarraEtherCAT()​

public DarraEtherCAT()

无参构造函数,流式 API 入口。必须通过 SetENI() 或 SetNetwork() 设置配置,然后调用 Build() 初始化。

示例:

var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\MyProject.xml")
.Build()
.Master;

if (master == null)
{
Console.WriteLine("初始化失败");
return;
}

master.SetState(EcState.OP);

流式配置​

SetENI(string eniPath)​

public DarraEtherCAT SetENI(string eniPath)

加载 DENI/ENI 配置文件(延迟配置,Build 时生效)。Build() 把文件内容交给 native LoadConfigJson 统一引擎;C# 没有公开的 LoadConfigJson 方法。

参数:

  • eniPath (string) — DENI 或 ENI 配置文件路径

示例:

var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\MyProject.xml");

SetNetwork(NetworkInfo primary, NetworkInfo? redundant = null)​

public DarraEtherCAT SetNetwork(NetworkInfo primary, NetworkInfo? redundant = null)

通过网口信息对象设置主网口和可选的冗余网口(延迟配置,Build 时生效)。

参数:

  • primary (NetworkInfo) — 主网口信息
  • redundant (NetworkInfo?) — 冗余网口信息(可选)

SetNetwork(string primaryName, string? redundantName = null)​

public DarraEtherCAT SetNetwork(string primaryName, string? redundantName = null)

通过网口名称设置主网口和可选的冗余网口(延迟配置,Build 时解析)。

参数:

  • primaryName (string) — 主网口名称或描述
  • redundantName (string?) — 冗余网口名称或描述(可选)

示例:

var master = new DarraEtherCAT()
.SetNetwork("以太网 1");

SetEsiFile(string filePath)​

public DarraEtherCAT SetEsiFile(string filePath)

加载单个 ESI 文件(延迟配置,Build 时生效)。可多次调用。

备注

如果使用 DENI 文件,通常不需要单独加载 ESI。

SetEsiFiles(string path)​

public DarraEtherCAT SetEsiFiles(string path)

加载 ESI 文件目录(延迟配置,Build 时生效)。可多次调用。

EnableAutoStartup()​

public DarraEtherCAT EnableAutoStartup()

标记需要自动配置从站(Build 时执行)。

备注

如果使用 ENI/DENI 文件,通常不需要调用此方法。已有启动参数的从站会被完整跳过,不会被覆盖或补充。此方法仅对未配置的从站生效(自动从 ESI/MDP/SM 生成 PDO/DC/启动参数)。

链式调用覆盖规则

SetENI 和 SetNetwork 都可以设置网口。当两者同时使用时,后调用的方法覆盖前面的冲突配置:

// SetENI 在后 → 使用 ENI 文件中的网口配置
var master = new DarraEtherCAT()
.SetNetwork("以太网 1")
.SetENI(@"C:\config.xml") // ← ENI 中的网口覆盖 SetNetwork
.Build()
.Master;
// SetNetwork 在后 → SetNetwork 的网口覆盖 ENI
var master = new DarraEtherCAT()
.SetENI(@"C:\config.xml")
.SetNetwork("以太网 1") // ← 覆盖 ENI 中的网口
.Build()
.Master;

ENI/DENI 文件中的从站配置、PDO 映射、DC 设置等始终保留,仅网口配置受覆盖规则影响。EnableAutoStartup() 仅对未配置的从站生效,ENI/DENI 已配置的从站会被完整跳过。

构建​

Build()​

public BuildResult Build()

构建并初始化主站(终结方法)。BuildResult 支持隐式转换为 DarraEtherCAT?(成功为实例,失败为 null),也支持元组解构 (success, message)。失败时实例已自动释放。

建议用法 — 直接链式调用,用 null 判断是否成功:

var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\config.xml")
.Build()
.Master;

if (master == null) return; // 初始化失败
master.SetState(EcState.OP);

扩展用法 — 需要获取详细错误消息时,使用元组解构:

var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\config.xml");

var (success, message) = master.Build();
if (!success)
{
Console.WriteLine($"初始化失败: {message}");
return;
}
Console.WriteLine(message); // "主站初始化成功,扫描到 N 个从站"

Validate()​

public ValidationResult Validate()

Build 前配置预检查,不执行实际构建。检查 ENI 文件存在性、主站实例上限等。

返回值:

  • ValidationResult — 包含 IsValid (bool) 和 Errors (IReadOnlyList<string>) 属性

示例:

var master = new DarraEtherCAT().SetENI("config.xml");
var result = master.Validate();
if (!result.IsValid)
{
foreach (var error in result.Errors)
Console.WriteLine($"配置错误: {error}");
return;
}
master.Build();

资源释放​

Close()​

public void Close()

关闭并清空内存,停止主站和数据采集线程。邮箱 worker 退出等待每站限时 800 ms 且整轮共享预算,Close 总时长受控(详见 Close())。

Dispose()​

public void Dispose()

释放主站资源,关闭网络连接。实现 IDisposable 接口。

DarraEtherCAT.EmergencyCleanup()​

public static void EmergencyCleanup()

紧急清理所有主站资源。在进程异常退出(如 AppDomain.UnhandledException)时调用,释放网卡句柄和共享内存,避免资源泄漏。

示例:

AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
DarraEtherCAT.EmergencyCleanup();
};

多实例管理​

DarraEtherCAT.ActiveInstanceCount​

public static int ActiveInstanceCount { get; }

当前活跃的主站实例数量。

DarraEtherCAT.MaxInstanceCount​

public static int MaxInstanceCount { get; }

允许的最大主站实例数量。

网络扫描​

网卡枚举范围

GetNetworkInfo() 默认只返回已绑定 DarraRT_Eth 的物理有线口。虚拟网卡、WiFi、TAP 不进列表。ASIX 等 USB 有线网卡按物理有线口保留。非 Windows 无协议绑定概念,恒返回物理有线口。

DarraEtherCAT.GetNetworkInfo(bool needSlavesNum = true, bool includeUnbound = false)​

public static List<NetworkInfo> GetNetworkInfo(bool needSlavesNum = true, bool includeUnbound = false)

获取网卡列表。默认只列已绑定的物理有线口,并扫描每张口的从站数。

参数:

  • needSlavesNum (bool, 默认 true) —
    • true: 已绑定口一次 QFB 填 SlaveNum。空口不做 7s BRD。
    • false: 仅列举网卡, SlaveNum/RedundantSlaveNum 均为 0.
  • includeUnbound (bool, 默认 false) —
    • false(默认): 只返回已绑定 DarraRT_Eth 的物理有线口;未绑定口不扫描、不返回。绑定由 DLL 自动完成,SDK 只读。
    • true: 返回全部物理有线口(含未绑定)。非 Windows 忽略此参数。

返回值:

  • List<NetworkInfo> — 网络适配器信息列表

相关结构:

public class NetworkInfo
{
public string Name { get; } // 适配器系统名称(用于 SetNetwork)
public string Desc { get; } // 适配器描述(用户可读)
public string Mac { get; } // MAC 地址(用于网卡绑定)
public int SlaveNum { get; } // 该 NIC 上从站总数 (环+分支), 0=未扫/无从站
public int RedundantSlaveNum { get; } // 受冗余保护的从站数 (环从站数), 0=单端口布线
public bool BoundProtocol { get; } // 是否已绑定 DarraRT_Eth
public OperationalStatus LinkStatus { get; } // 链路状态(System.Net.NetworkInformation,Up=在线 / Down=离线,查询不到为 Unknown)
// 分支从站数 = SlaveNum - RedundantSlaveNum
}

示例 — 扫描:

// 默认: 已绑定物理有线口 + 从站数, GUI 直接显示
var adapters = DarraEtherCAT.GetNetworkInfo();
foreach (var a in adapters)
{
if (a.SlaveNum == 0) continue;
int branch = a.SlaveNum - a.RedundantSlaveNum;
string s = a.RedundantSlaveNum > 0
? $"{a.RedundantSlaveNum}(冗余) + {branch}(分支) = {a.SlaveNum}"
: $"{a.SlaveNum}(单端口)";
Console.WriteLine($"{a.Desc}: {s}");
}

示例 — 仅列举已绑定网卡 (跳过从站扫描):

var adapters = DarraEtherCAT.GetNetworkInfo(needSlavesNum: false);

示例 — 含未绑定的物理有线口:

var adapters = DarraEtherCAT.GetNetworkInfo(needSlavesNum: false, includeUnbound: true);

DarraEtherCAT.GetBoundAdapters()​

public static List<NetworkInfo> GetBoundAdapters()

只读查询已绑定 DarraRT_Eth 的物理有线口(不含任何绑定写操作)。虚拟网卡、WiFi、TAP 不进列表;ASIX USB 有线保留。非 Windows 返回空列表。SlaveNum 为 0。

返回值:

  • List<NetworkInfo> — 已绑定且启用的物理有线口(BoundProtocol 均为 true)

示例:

var bound = DarraEtherCAT.GetBoundAdapters();
foreach (var a in bound)
Console.WriteLine($"{a.Desc} {a.Name}");

DarraEtherCAT.QuickSlaveCount(string adapterName)​

public static int QuickSlaveCount(string adapterName)

快速从站计数(不读取 EEPROM,仅 BRD)。DENI / 下拉数量走这条。要选用从站请用 ScanSlaves()。数量为探测到的从站个数,不得当 0 丢弃。

参数:

  • adapterName (string) — 网络适配器名称

返回值:

  • int — 检测到的从站数量
冗余从站计数

冗余模式下的从站计数已并入 GetNetworkInfo() — 返回的每个适配器同时给出环上从站数 (RedundantSlaveNum) 和总数 (SlaveNum),无需单独的 QuickSlaveCountRedundant。详见上方 GetNetworkInfo()。

DarraEtherCAT.GetRingSlaveCount()​

public static int GetRingSlaveCount()

获取上一次冗余扫描中环形路径上的从站数。

返回值:

  • int — 环形路径上的从站数量

DarraEtherCAT.ScanSlaves(string adapterName, string? secondaryAdapter = null)​

public static List<ScannedSlaveInfo> ScanSlaves(string adapterName, string? secondaryAdapter = null)

完整扫描:读 EEPROM / 拓扑,返回可选用的从站列表。DENI 快速计数请用 QuickSlaveCount()。count > 0 时列表不得为空(详情未就绪则占位「从站 N」)。

参数:

  • adapterName (string) — 主网络适配器名称
  • secondaryAdapter (string?) — 冗余网络适配器名称(可选,用于环形拓扑)

返回值:

  • List<ScannedSlaveInfo> — 从站详细信息列表

相关结构:

public class ScannedSlaveInfo
{
// 基本标识
public int Index { get; } // 从站索引(1-based)
public string Name { get; } // 设备名称
public uint VendorID { get; } // 厂商 ID
public uint ProductCode { get; } // 产品代码
public uint RevisionNumber { get; } // 修订版本号
public uint SerialNumber { get; } // 序列号

// 地址
public ushort ConfigAddr { get; } // 配置地址
public ushort AliasAddr { get; } // 别名地址

// 拓扑信息
public ushort ParentIndex { get; } // 父从站索引(0=主站是父节点)
public byte Topology { get; } // 拓扑类型 (0~4)
public byte ActivePorts { get; } // 活动端口位图
public byte EntryPort { get; } // 入口端口
public byte ParentPort { get; } // 父从站上的端口号
public byte PhysicalType { get; } // 物理端口类型

// 计算属性
public bool IsCoupler { get; } // 是否为耦合器
public string DeviceType { get; } // "Coupler"/"Terminal"/"Box"/"Extension"/"Device"
public int Depth { get; } // 拓扑深度

// 格式化显示
public string VendorIDHex { get; } // "0x..."
public string ProductCodeHex { get; } // "0x..."
}

示例:

var slaves = DarraEtherCAT.ScanSlaves(@"\Device\NPF_{...}");
foreach (var s in slaves)
{
var indent = new string(' ', s.Depth * 2);
Console.WriteLine($"{indent}[{s.Index}] {s.Name} ({s.DeviceType})");
}

DarraEtherCAT.AbortScan()​

public static void AbortScan()

中止所有正在进行的扫描操作。用于关闭窗口或取消操作时快速中断阻塞的网络扫描。

示例:

// 在后台线程中扫描
var scanTask = Task.Run(() => DarraEtherCAT.ScanSlaves(adapterName));

// 用户点击取消
DarraEtherCAT.AbortScan();

版本与授权​

DarraEtherCAT.GetSerialNumber()​

public static string GetSerialNumber()

获取当前设备的序列号(用于授权验证)。

返回值:

  • string — 设备序列号,获取失败时返回空字符串

完整示例​

使用 DENI 文件(推荐)​

var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\MyProject.xml")
.Build()
.Master;

if (master == null) return;

bool ok = master.SetState(EcState.OP, out string stateMsg);
Console.WriteLine(ok ? "运行中" : $"失败: {stateMsg}");

动态扫描网口​

var adapters = DarraEtherCAT.GetNetworkInfo();
var target = adapters.FirstOrDefault(a => a.SlaveNum > 0);
if (target == null) return;

var master = new DarraEtherCAT()
.SetNetwork(target)
.SetEsiFiles(@"C:\ESI")
.EnableAutoStartup()
.Build()
.Master;

if (master == null) return;

冗余模式 (自动识别冗余对)​

// GetNetworkInfo() 默认会对 ≥2 张有从站的 NIC 自动跑 QFB 跨 NIC 探测,
// 找到的冗余对两端 RedundantSlaveNum > 0 (= 环上从站数).
var adapters = DarraEtherCAT.GetNetworkInfo();
var pair = adapters.Where(a => a.RedundantSlaveNum > 0).ToList();
if (pair.Count < 2) return; // 没找到冗余对 (单端口布线)

var master = new DarraEtherCAT()
.SetNetwork(pair[0], pair[1])
.SetENI(@"C:\config.xml")
.Build()
.Master;

if (master == null) return;

多实例(多网卡独立总线)​

每个主站实例管理一条独立的 EtherCAT 总线,使用不同的网卡。创建新实例无需重启,即时生效。

多实例要求
  • 每个实例必须使用不同的网卡 — 同一网卡不能被多个实例同时使用,SDK 会自动检测并阻止重复绑定
  • CPU 核心自动分配 — 每个实例自动占用不同的 CPU 核心,无需手动设置
  • Mailbox Gateway 端口自动偏移 — 每个实例自动分配不同的端口号
  • 授权与日志共享 — 所有实例共享同一授权状态和日志系统,无冲突
// 获取所有有从站的网卡
var adapters = DarraEtherCAT.GetNetworkInfo()
.Where(a => a.SlaveNum > 0).ToList();

// 实例 1 — 绑定第一张网卡
var master1 = new DarraEtherCAT()
.SetNetwork(adapters[0])
.EnableAutoStartup()
.Build()
.Master;

// 实例 2 — 绑定第二张网卡(即时生效,无需重启)
var master2 = new DarraEtherCAT()
.SetNetwork(adapters[1])
.EnableAutoStartup()
.Build()
.Master;

if (master1 != null) master1.SetState(EcState.OP);
if (master2 != null) master2.SetState(EcState.OP);

// 各实例独立运行,互不干扰
master1.Events.ProcessDataCyclicSync += (idx) => { /* 总线 1 的 PDO 处理 */ };
master2.Events.ProcessDataCyclicSync += (idx) => { /* 总线 2 的 PDO 处理 */ };

// 查询活跃实例数
Console.WriteLine($"活跃主站数: {DarraEtherCAT.ActiveInstanceCount}");

// 释放时自动归还网卡和端口
master2.Close(); // 释放后 adapters[1] 可被新实例使用

多实例资源分配:

  • 网卡 — 每个实例使用不同的网卡
  • CPU 核心 — 每个实例自动分配不同的专用核心
  • Mailbox 端口 — 每个实例自动分配递增的端口号
  • 工作线程 — 各实例独立