初始化
强烈建议使用 Darra 配置工具导出的 DENI 文件进行初始化。
DENI 文件包含完整的网络配置(网口、从站、PDO 映射、DC 设置等),可确保配置的一致性和正确性。
使用 DENI 文件管理设备硬件设施将获得更好的兼容性与通用性。
创建主站
C SDK 通过函数指针表 dll_t 加载 Darra.Core.dll,主站索引 master_index 作为所有后续 API 的第一个参数。
Initialize()
uint16_t Initialize(void);
uint16_t InitializeSpecificMaster(uint16_t master_index);
int GetMaxMasterInstances(void);
Initialize() 创建一个新的主站实例并返回其索引(>0 成功,0 失败)。InitializeSpecificMaster() 初始化指定索引的主站(不允许索引 0)。GetMaxMasterInstances() 返回允许的最大主站实例数。
SetNetwork()
uint16_t SetNetwork(uint16_t master_index, const char* if_primary, const char* if_secondary);
绑定网卡。if_secondary 传空字符串 "" 表示单网卡模式。
参数:
master_index(uint16_t) — 主站索引if_primary(const char*) — 主网卡名称(Windows:\\Device\\NPF_{GUID},Linux:eth0)if_secondary(const char*) — 冗余网卡名称(""表示无冗余)
返回值:
uint16_t— 发现的从站数量,0 或 0xFFFF 表示失败
示例:
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
uint16_t slave_count = dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
printf("发现 %d 个从站\n", slave_count);
资源释放
Dispose()
void Dispose(uint16_t master_index);
void FreeMemory(void* ptr);
void EmergencyCloseNics(void);
Dispose()释放主站实例。调用后master_index不可再使用。FreeMemory()释放 SDK 内部分配的内存(如GetSerialNumber()返回的字符串)。EmergencyCloseNics()紧急关闭所有网卡句柄,在进程异常退出(信号处理 /atexit)时调用。
多实例管理
每个主站实例管理一条独立的 EtherCAT 总线,使用不同的网卡。Initialize() 多次调用即可创建多个实例。
- 每个实例必须使用不同的网卡(同一网卡不能被多个实例同时使用)
- CPU 核心由 SDK 自动分配,不同实例占用不同核心
- Mailbox Gateway 端口自动按实例索引偏移
- 授权与日志在所有实例间共享
uint16_t master1 = dll.Initialize();
dll.SetNetwork(master1, "\\Device\\NPF_{GUID_A}", "");
uint16_t master2 = dll.Initialize();
dll.SetNetwork(master2, "\\Device\\NPF_{GUID_B}", "");
dll.SetStateSequence(master1, EC_STATE_OPERATIONAL, 10000);
dll.SetStateSequence(master2, EC_STATE_OPERATIONAL, 10000);
dll.Dispose(master1);
dll.Dispose(master2);
网络扫描
GetNetworkInfo()
int GetNetworkInfo(BOOL is_redundant, BOOL need_slaves_num);
void* GetNetworksPointer(void);
GetNetworkInfo() 扫描系统中可用的网卡并返回数量。need_slaves_num=TRUE 时同时为每张 NIC 做 BRD 探测填充从站数 + 自动识别冗余对(耗时约 500ms – 1.5s);FALSE 时仅列举 NIC(耗时约 100ms)。GetNetworksPointer() 返回内部网卡列表指针,无需释放。
QuickSlaveCount()
int QuickSlaveCount(const char* adapter_name);
int QuickSlaveCountRedundant(const char* primary, const char* secondary);
int ScanSlaveCount(const char* adapter_name);
int ScanSlaveCountRedundant(const char* primary, const char* secondary);
int GetRingSlaveCount(void);
int QuickFindRedundantPairBatch(const char** adapter_names, int adapter_count,
int* primary_idx, int* secondary_idx);
QuickSlaveCount*— 不读 EEPROM,仅广播查询,返回从站数量。ScanSlaveCount*— 完整扫描(读取 EEPROM)。GetRingSlaveCount()— 上一次冗余扫描中环上的从站数。QuickFindRedundantPairBatch()— 在多张 NIC 中批量探测冗余对,输出主/副适配器索引。
ReadSlaveInfo()
int ReadSlaveInfo(const char* adapter_name);
int GetScannedSlaveCount(void);
BOOL GetScannedSlaveInfo(int index,
uint32_t* vendor_id, uint32_t* product_code,
uint32_t* revision, uint32_t* serial,
char* name, int name_size,
uint16_t* config_addr, uint16_t* alias_addr,
uint16_t* parent, uint8_t* topology, uint8_t* activeports,
uint8_t* entryport, uint8_t* parentport, uint8_t* ptype);
ReadSlaveInfo() 扫描指定网卡上所有从站并缓存详情,返回从站数量。配合 GetScannedSlaveCount() 与 GetScannedSlaveInfo() 按索引读取每个从站的厂商 ID / 产品代码 / 拓扑信息等(index 0-based)。
示例:
int count = dll.ReadSlaveInfo("\\Device\\NPF_{GUID}");
for (int i = 0; i < count; i++) {
uint32_t vid, pid, rev, serial;
char name[128];
uint16_t cfg_addr, alias, parent;
uint8_t topo, ports, entry, pport, ptype;
if (dll.GetScannedSlaveInfo(i, &vid, &pid, &rev, &serial,
name, sizeof(name), &cfg_addr, &alias,
&parent, &topo, &ports, &entry, &pport, &ptype)) {
printf("[%d] %s (VID=0x%08X, PID=0x%08X)\n", i + 1, name, vid, pid);
}
}
AbortScan()
void AbortScan(void);
void ResetScanAbort(void);
void AbortNetwork(void);
void ResetAbortNetwork(void);
中止/重置正在进行的网络扫描操作。AbortScan() 用于关闭 UI 窗口或取消操作时快速中断阻塞的扫描;AbortNetwork() 作用于网络层(绑定/通讯)的取消。
一步初始化 (EcInit)
EcInit()
uint16_t EcInit(const char* json_config);
uint16_t EcInitFromFile(const char* json_file_path);
void EcClose(uint16_t master_index);
EcInit() 通过 JSON 字符串一步完成 Initialize + SetNetwork + LoadConfig + SetStateSequence + Start。EcInitFromFile() 从文件读取 JSON。EcClose() 一步关闭并释放,配对使用。
JSON 配置:
{
"adapter": "\\Device\\NPF_{GUID}",
"redundant_adapter": "",
"cycle_time_us": 1000,
"dc_sync0_ns": 1000000,
"target_state": "OP",
"slaves": []
}
字段说明:
adapter(必选) — 主网卡名称redundant_adapter(可选) — 冗余网卡名称(空字符串=单网卡)cycle_time_us(可选) — PDO 周期,默认 1000dc_sync0_ns(可选) — DC SYNC0 周期,0=不配置 DCtarget_state(可选) — 目标状态("OP"/"SAFEOP"/"PREOP")slaves(可选) — 从站启动参数配置数组
版本与授权
GetSerialNumber()
/* 返回值需要调用 FreeMemory() 释放 */
char* GetSerialNumber(void);
char* GetDeviceName(void);
char* GetUserEmail(void);
int VerifyLicense(void);
BOOL IsLicenseValid(void);
获取设备序列号 / 设备名称 / 用户邮箱(用于授权验证)。VerifyLicense() 返回 0 表示授权有效,IsLicenseValid() 返回当前授权状态。返回字符串指针的 API 必须调用 FreeMemory() 释放。
示例:
char* serial = dll.GetSerialNumber();
if (serial) {
printf("设备序列号: %s\n", serial);
dll.FreeMemory(serial);
}
完整示例
使用 ENI/DENI 文件(推荐)
#define DYNAMIC_LOAD
#include "ethercat.h"
#include <stdio.h>
int main(void)
{
dll_t dll;
if (!LOAD_DLL(&dll, "Darra.Core.dll")) return 1;
uint16_t master = dll.Initialize();
if (master == 0) { UNLOAD_DLL(&dll); return 1; }
uint16_t ret = dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
if (ret == 0 || ret == 0xFFFF) {
dll.Dispose(master); UNLOAD_DLL(&dll); return 1;
}
printf("发现 %d 个从站\n", ret);
if (!dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000)) {
dll.Dispose(master); UNLOAD_DLL(&dll); return 1;
}
dll.Start(master);
getchar();
dll.Stop(master);
dll.ClearStartupParameters(master, 0);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}
动态扫描网口
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
int adapter_count = dll.GetNetworkInfo(FALSE, TRUE);
printf("发现 %d 个网络适配器\n", adapter_count);
int slave_count = dll.ReadSlaveInfo("\\Device\\NPF_{GUID}");
for (int i = 0; i < slave_count; i++) {
/* 通过 GetScannedSlaveInfo() 读取详情 */
}
uint16_t master = dll.Initialize();
dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
dll.Start(master);
一步初始化
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.EcInitFromFile("config.json");
if (master == 0) { UNLOAD_DLL(&dll); return 1; }
/* ... 业务逻辑 ... */
dll.EcClose(master);
UNLOAD_DLL(&dll);