属性与状态机
属性
所有 C API 函数第一个参数为 master_index。
下表中函数签名省略该参数,完整调用形式如: dll.GetLinkStatus(master)。
| 类别 | 函数 | 返回类型 | 读写 | 说明 |
|---|---|---|---|---|
| 网络与链路 | GetLinkStatus(mi) | uint8_t | 只读 | 网络链路状态 (ec_link_state_t) |
| 网络与链路 | GetNetworksPointer() | void* | 只读 | 网络适配器列表内部指针 |
| 错误码 | GetSlaveALStatusCode(mi, si) | uint16_t | 只读 | 从站 AL Status Code 错误码 |
| 从站分组 | GetGroupSlaveCount(mi, group) | uint16_t | 只读 | 指定组的从站数量 |
| 从站分组 | GetActiveGroupCount(mi) | uint8_t | 只读 | 活跃组数量(映射后有效) |
| 运行时 | GetSlaveState(mi, si) | uint8_t | 只读 | 从站当前状态 |
| 运行时 | GetExpectedWKC(mi) / SetExpectedWKC(mi, wkc) | uint16_t | 读写 | 期望工作计数器 |
| 主时钟 | GetMasterDCTime(mi) | int64_t | 只读 | DC 主时钟时间(纳秒) |
| 主时钟 | GetReferenceClockSlave(mi) | uint16_t | 只读 | 参考时钟从站索引 |
EtherCAT 状态枚举:
typedef enum {
EC_STATE_NONE = 0x00, /* 无状态 / 未知 */
EC_STATE_INIT = 0x01, /* Init 状态 */
EC_STATE_PRE_OP = 0x02, /* Pre-Operational */
EC_STATE_BOOT = 0x03, /* Boot 状态 (固件更新) */
EC_STATE_SAFE_OP = 0x04, /* Safe-Operational */
EC_STATE_OPERATIONAL = 0x08, /* Operational */
EC_STATE_ACK = 0x10, /* 错误确认位 */
EC_STATE_ERROR = 0x10 /* 错误状态标志 */
} ec_state_t;
typedef enum {
EC_LINK_DOWN = 0,
EC_LINK_UP = 1,
EC_LINK_REDUNDANCY = 2
} ec_link_state_t;
状态机管理
SetState()
BOOL SetState(uint16_t master_index, int state);
BOOL SetStateWithTimeout(uint16_t master_index, int state, uint32_t timeout_ms);
BOOL SetSlaveStateWithTimeout(uint16_t master_index, uint16_t slave_index,
int state, uint32_t timeout_ms);
切换主站和所有从站到目标状态(按 ETG 状态机链跨多级一次到位)。SetSlaveStateWithTimeout 仅切换单个从站。SDK 内部对硬件偶发抖动自动执行 3 次重试(每次失败后等待 1.5s),用户不需要自行包装重试逻辑。
切到 EC_STATE_SAFE_OP 时,SDK 按 ETG 标准自动为非 DC 从站配置同步循环计数阈值与 SyncManager 同步类型(FreeRun 兜底),避免部分驱动器缺省值导致 SafeOp 卡死。SetStateSequence / SetStateWithStartup 同样自动执行。从站不支持对应配置时静默跳过;自定义同步策略应在切到 SafeOp 之后覆盖。
SetStateSequence()
BOOL SetStateSequence(uint16_t master_index, int target_state, uint32_t timeout_ms);
BOOL SetStateWithStartup(uint16_t master_index, int target_state, uint32_t timeout_ms);
SetStateSequence 链式自动状态转换 (Init → PreOp → SafeOp → OP),自动执行启动参数。推荐使用此函数。SetStateWithStartup 单步状态转换,自动执行 Before/After 启动参数。
示例:
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, adapter, "");
if (!dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000)) {
printf("状态转换失败\n");
}
Stop()
void Stop(uint16_t master_index);
void Start(uint16_t master_index);
void AbortNetwork(void);
void ResetAbortNetwork(void);
Stop() 停止 PDO 循环线程,主站切回 PRE_OP 状态。Start() 启动 PDO 循环(在 SafeOp/OP 后调用)。AbortNetwork() 中断协议层所有阻塞操作(如 SetState),可从任意线程调用。
GetMasterState() / GetSlaveState()
void* GetMasterState(uint16_t master_index);
uint8_t GetSlaveState(uint16_t master_index, uint16_t slave_index);
uint8_t GetLinkStatus(uint16_t master_index);
uint16_t GetSlaveALStatusCode(uint16_t master_index, uint16_t slave_index);
GetMasterState() 返回内部 ec_master_state_t*(无需释放),转 uint8_t* 取首字节即聚合状态(EC_STATE_*)。GetSlaveState() 返回单个从站当前状态。GetSlaveALStatusCode() 返回从站 AL Status Code 错误码。
首选订阅 RegisterSlaveStateChangeCallbackAsync, 状态变化时由 SDK 主动推送。仅在 UI 刷新 / 简单 demo 场景才用 GetMasterState 自己轮询(建议 ≥ 100ms 间隔, 不要塞进 PDO 回调)。
WaitForState()
BOOL WaitForState(uint16_t master_index, int state,
uint32_t timeout_ms, uint32_t poll_interval_ms);
BOOL WaitForSlaveState(uint16_t master_index, uint16_t slave_index, int state,
uint32_t timeout_ms, uint32_t poll_interval_ms);
int GetTransitionTimeoutMs(uint16_t master_index, int from, int to);
轮询等待主站/单从站到达指定状态。不发起状态切换, 仅等待已请求的转换完成 / PDO 流稳定确认。GetTransitionTimeoutMs() 查询 SDK 内部按 ESI / 配置 / ETG.1020 兜底的某条状态转换推荐最大等待时长(毫秒)。
过程数据看门狗
int SetAllSlaveWatchdog(uint16_t master_index, uint32_t timeout_ms);
int SetAllSlavePdiWatchdog(uint16_t master_index, uint32_t timeout_ms);
批量设置所有从站的过程数据看门狗 / PDI 看门狗超时(毫秒,0 = 禁用,最大 6553ms)。返回成功设置的从站数量。
单个从站的看门狗配置请使用 SetSlaveWatchdog()。
示例:
int count = dll.SetAllSlaveWatchdog(master, 100);
printf("已设置 %d 个从站看门狗超时为 100ms\n", count);
诊断控制方法
void ResetDiagnostics(uint16_t master_index);
BOOL ResetSlavePortErrorCounters(uint16_t master_index, uint16_t slave_index);
ResetDiagnostics() 一次性重置主站诊断统计、所有从站端口错误计数器和 PDO 丢帧统计。ResetSlavePortErrorCounters() 重置指定从站的端口错误计数器。
详细诊断 API 请参考 主站诊断。
热插拔自修复
在断电重插/更换从站的场景下,SDK 自动识别身份不符并进入保护状态,避免错误设备被误纳入控制循环。事件流程见 SlaveIdentityMismatch 事件。
AcknowledgeSlaveReplacement()
BOOL AcknowledgeSlaveReplacement(uint16_t master_index, uint16_t slave_index);
用户确认从站替换完毕,触发 EtherCAT 识别状态机重新探测该从站。
调用时机: 订阅 RegisterSlaveIdentityMismatchCallback 接收到身份不符报警后,操作员检查/更换设备完毕,调用本方法让 SDK 重新检测。
行为:
- 若身份已纠正(换回正确设备 / 同型号升级 Revision)→ 自动恢复并触发 从站发现事件
- 若身份仍不匹配 → 再次触发
SlaveIdentityMismatch,回到IDENT_REJECTED状态
参数:
master_index(uint16_t) — 主站索引slave_index(uint16_t) — 从站编号(1-based,与配置一致)
返回值:
BOOL—TRUE=已接受并复位 FSM;FALSE=参数无效,或从站当前不在IDENT_REJECTED/FAILED状态
在 SlaveIdentityMismatch 事件触发之前调用本方法无效。从站未进入保护状态时 SDK 会持续正常探测,无需手动确认。
示例:
static void on_identity_mismatch(uint16_t mi, uint16_t si,
uint32_t ev, uint32_t ep, uint32_t er,
uint32_t av, uint32_t ap, uint32_t ar)
{
printf("从站 %d 身份不符 (期望 Vendor=0x%08X, 实际 Vendor=0x%08X)\n",
si, ev, av);
if (show_replacement_dialog(si)) {
BOOL ok = dll.AcknowledgeSlaveReplacement(mi, si);
if (!ok)
printf("确认失败: 从站不在 IDENT_REJECTED 状态\n");
}
}
dll.RegisterSlaveIdentityMismatch(on_identity_mismatch);
- Discovery
is_found=FALSE/TRUE— 从站断电/断线(身份仍匹配)→ 自动恢复,触发is_found=TRUE SlaveIdentityMismatch— 从站身份变了(换错设备 / 换同型号旧版本固件)→ 手动调AcknowledgeSlaveReplacement
状态机工具函数 (ESM)
ETG.1000.6 §6.4 + ETG.1020 状态合法性 / 默认超时 / AL Status Code 分类 / 主站等级查询。这组函数是纯查询, 不发起任何 EtherCAT 帧, 用于在请求 SetSlaveStateWithTimeout 之前预校验, 或在事件回调里把 AL Status Code 翻译成中文/分类。
/* 转换合法性 (ETG.1000.6 §6.4 状态机表) */
int EsmIsLegalTransition(int from, int to);
int EsmGetLegalTransitions(int from, int* out_states, int max_count);
uint32_t EsmGetDefaultTimeoutMs(int from, int to);
/* AL Status Code 语义 (ETG.1000.6 §6.4.4 Table 58) */
int EsmIsKnownAlStatusCode(uint16_t al_status_code);
int EsmClassifyAlStatusCode(uint16_t al_status_code);
/* 主站等级 (ETG.1500 §5.3) */
uint8_t EsmGetMasterClass(uint16_t master_index); /* 'A'=0x41, 'B'=0x42 */
EsmIsLegalTransition / EsmGetLegalTransitions — 合法转换包括 IP/PS/SO/PI/SP/OS/SI/OI/IB/BI 与同状态自跳; 跳级升级 (Init→SafeOp / PreOp→Op) 非法。返回 1 合法 / 0 非法; EsmGetLegalTransitions 把 from 的所有合法目标状态写入 out_states, 返回写入数量, -1 表示 from 非法或缓冲不足。
EsmGetDefaultTimeoutMs — 返回 ETG.1020 Table 55 推荐的状态转换超时 (毫秒, 0=非法转换)。SetStateWithTimeout 内部已用此值做兜底, 用户层通常无需手动传超时。
EsmClassifyAlStatusCode — 错误分类: 0=NoError, 1=Transient (可立即重试), 2=Configuration (需重配置/SDO 修改), 3=Hardware (需用户介入), 4=Unknown (厂商特定/0x8000 段)。
EsmGetMasterClass — Darra Master 当前定位 Class A Full (0x41), 支持 Cable Redundancy + Hot Connect + SDO Info + FoE + Mailbox。
示例:
/* 在请求状态前预校验 */
int from = dll.GetSlaveState(master, 1);
if (!dll.EsmIsLegalTransition(from, EC_STATE_OPERATIONAL)) {
printf("非法转换 %d -> OP, 应该走 SetStateSequence\n", from);
} else {
uint32_t to = dll.EsmGetDefaultTimeoutMs(from, EC_STATE_OPERATIONAL);
dll.SetSlaveStateWithTimeout(master, 1, EC_STATE_OPERATIONAL, to);
}
/* AL Status Code 翻译 */
uint16_t code = dll.GetSlaveALStatusCode(master, 1);
const char* desc = AL_StatusCode_GetDescription(code);
int cls = dll.EsmClassifyAlStatusCode(code);
printf("AL=0x%04X (%s, class=%d)\n", code, desc, cls);
if (cls == 1) {
/* Transient → 直接重试切到当前请求的目标态 */
}
AL_StatusCode_GetDescription / AL_StatusCode_GetSeverity / AL_StatusCode_GetRecoveryHint 在 protocol/protocol_codes.h 中, 返回静态中文/英文描述, 与本节 ESM API 配合使用。
系统级生命周期与紧急控制
int EnsureDriversRunning(void);
void EmergencyCloseNics(void);
void AbortNetwork(void);
void ResetAbortNetwork(void);
void AbortScan(void);
void ResetScanAbort(void);
EnsureDriversRunning — 静态函数, 不带 master 参数。检查 DarraRT.sys + DarraRT_Eth.sys 两个内核驱动是否 RUNNING, 缺失时尝试 StartService 启动。返回 1 = 全部就绪, 0 = 启动失败 (上层应弹错误对话框, 禁止主界面继续)。Initialize() 入口已自动调一次, 用户在带 GUI 的程序里也可以单独显式调用以提前提示。
EmergencyCloseNics — 进程紧急退出钩子: 不论 master 是否 OP, 立即关闭所有已绑 NIC (PCAP / WDK), 释放线程, 不走正常 Dispose 链。仅在 signal(SIGINT) / SetConsoleCtrlHandler 等紧急路径调用。
AbortNetwork / AbortScan — 设置中断标志, 让阻塞的 SetState* / GetNetworkInfo / ScanSlaveInfo 立即返回失败。可从任意线程调用 (UI 取消按钮)。ResetAbortNetwork / ResetScanAbort 清除标志, 准备下一轮调用。
示例:
/* 启动前驱动检查 */
if (!dll.EnsureDriversRunning()) {
fprintf(stderr, "DarraRT 驱动未就绪, 程序无法继续\n");
return -1;
}
/* GUI 取消按钮 */
void on_cancel_clicked(void) {
dll.AbortNetwork(); /* 让阻塞的 SetState 立即返回 */
dll.AbortScan(); /* 让 ScanSlaveInfo 立即返回 */
}
/* SIGINT 紧急退出 */
void on_ctrl_c(int sig) {
dll.EmergencyCloseNics();
_exit(1);
}
启动配置校验
int VerifyAllSlaveIdentities(uint16_t master_index,
ec_slave_identity_t* expected, uint32_t slave_count,
BOOL check_revision, BOOL check_serial,
uint64_t* mismatch_mask);
int ec_validate_config(uint16_t master_index);
int ValidateConfig(uint16_t master_index, char* error_buf, int buf_size);
VerifyAllSlaveIdentities — 一次性校验整网身份 (Vendor / ProductCode / 可选 Revision / 可选 Serial), mismatch_mask 按 bit 位置标记不匹配从站 (slave_index 1..64 对应 bit0..63)。配合 SetSlaveOptional 让某些位置允许换设备。
ec_validate_config / ValidateConfig — Build 前预检查: 网络配置 / 从站到组映射 / PDO 大小 / 看门狗参数等。ValidateConfig 把人类可读错误写入 error_buf (UTF-8 中文), 返回错误条数 (0 = 配置 OK)。
诊断快照与组级 WKC
typedef struct {
int frequency;
uint32_t error_count;
float packet_loss_rate;
float late_frame_rate;
double avg_jitter_us;
double max_jitter_us;
int cycle_time_us;
uint16_t wkc_actual;
uint16_t wkc_expected;
uint32_t bus_cycle_hz;
double bus_max_jitter_us;
double bus_avg_jitter_us;
double bus_roundtrip_us;
double bus_load_percent;
uint32_t smi_count;
double smi_peak_us;
int primary_port_ok;
int secondary_port_ok;
int redundancy_active;
} ec_diagnostics_snapshot_t;
BOOL GetDiagnosticsSnapshot(uint16_t master_index, ec_diagnostics_snapshot_t* snapshot);
GetDiagnosticsSnapshot 一次调用拿到所有诊断指标 (线程安全, 内部加锁拷贝)。HMI / Web 仪表盘场景推荐用此 API, 不要逐个调 GetPacketLossRate / GetMasterDCTime / GetCommunicationStats (多次跨锁、值之间不一致)。详见 主站诊断。