从站诊断
每个从站提供独立的诊断函数,可查询端口错误、冗余、DC 同步、链路质量等信息。
从站状态(GetSlaveState、GetSlaveALStatusCode)等基础属性请参考 属性与状态机。
建议通过 事件 驱动异常处理(如 SlaveStateChanged、SlaveOffline、DCSyncLost),而非轮询。
直接读取从站诊断函数适用于 UI 显示等场景。
全局通信统计、丢包率、PDO 丢帧汇总请参考 主站诊断。
功能概览
| 功能 | 函数 | 说明 |
|---|---|---|
| WKC 与健康镜像 | GetSlaveWcState() / GetSlaveAlStatusMirror() / GetSlaveMailboxHealth() 等 | per-slave WcState/AL/邮箱健康内核镜像 (薄读零帧, 实时) |
| 通信诊断 | ReadSlavePortErrorCounters() | ESC 端口错误计数器 (RX/帧/链路) |
| 冗余诊断 | GetSlaveRedundancyActivated() | 冗余激活、主/冗余线路断路检测 |
| DC 同步 | GetSlaveSyncWindowStatus() | 同步状态、时间差 |
| 链路质量 | GetSlaveLinkQuality() | 链路质量百分比 (0-100) |
| 从站详细信息 | GetSlaveDetailedInfo() | 单从站完整快照指针 |
| 端口断线事件 | RegisterSlavePortLinkChanged | P0-P3 link 翻转事件 |
WKC 与健康镜像
/* per-slave WKC 贡献: 1=本周期贡献 / 0=没贡献(疑似掉站) / 0xFF=未知(契约未就绪) */
uint8_t GetSlaveWcState(uint16_t master_index, uint16_t slave_index);
/* per-slave AL 镜像 (16bit): 低字节 = AL State (bit4=Error 位),
* 有 Error 位时高字节 = AL Status Code 低字节 */
uint16_t GetSlaveAlStatusMirror(uint16_t master_index, uint16_t slave_index);
/* 邮箱健康度原始字节: 0=未知 / 1=健康 / 2=降级 (在 OP 但邮箱半失效) */
uint8_t GetSlaveMailboxHealth(uint16_t master_index, uint16_t slave_index);
/* 是否被迫降级到 FreeRun: 1=已降级 / 0=正常 */
uint8_t GetSlaveFreeRunDemoted(uint16_t master_index, uint16_t slave_index);
/* 邮箱半失效累计降级次数 (从站修复后不清零, 反映历史劣化) */
uint32_t GetSlaveHealthDegradedCount(uint16_t master_index, uint16_t slave_index);
/* 专项修复邮箱半失效: TRUE=修复触发成功 / FALSE=无需修复或失败.
* 仅重建邮箱通道 handler, 不改 WKC 不脱 OP */
BOOL RecoverSlaveMailboxHealth(uint16_t master_index, uint16_t slave_index);
内核每周期 (WKC 异常时立即) 维护 per-slave 诊断缓存, C SDK 薄读零帧 —— 调一次拿一次, 不缓存, 读到即此刻真实总线现实, 无需任何刷新。配合主站级 GetWcDeficit(mi) 可在掉站时定位到具体从站。
R1 可观测性约定(不可违反): GetSlaveWcState() 返回 0 (没贡献) 不是 master 故障, 而是该从站此刻没在响应 (疑似掉站 / 热插拔恢复中); 修复后自动回 1。契约未就绪 / 越界时返回 0xFF (未知), 诚实暴露不臆造。GetSlaveMailboxHealth() 返回 2 (降级) 表示从站仍在 OP 但邮箱半失效 (CoE/SDO 可能阻塞), 如实反映不参与 WKC 篡改。
GetSlaveWcState 返回值:
| 值 | 含义 |
|---|---|
| 0 | 该从站此刻没在贡献 WKC(疑似掉站 / 热插拔恢复中) |
| 1 | 该从站正常贡献 WKC(在总线上响应) |
| 0xFF | 内核镜像未填充(契约未就绪 / 越界)— 诚实未知,不臆造 |
GetSlaveMailboxHealth 返回值:
| 值 | 含义 |
|---|---|
| 0 | 未知 / 无邮箱 / 不在 OP — 邮箱健康度此时无意义 |
| 1 | 健康 — 在 OP 且邮箱可用 |
| 2 | 降级 — 在 OP 但邮箱半失效(handler 丢失 / DC 降级伴 0x001F) |
示例:
for (uint16_t s = 1; s <= slave_count; s++) {
/* WKC 贡献:掉站定位 */
if (dll.GetSlaveWcState(master, s) == 0) {
uint16_t al = dll.GetSlaveAlStatusMirror(master, s);
printf("从站 %d 未贡献 WKC, AL 镜像=0x%04X\n", s, al);
}
/* 邮箱半失效(在 OP 但 CoE 可能阻塞)*/
if (dll.GetSlaveMailboxHealth(master, s) == 2) {
printf("从站 %d 邮箱半失效 %u 次%s\n", s,
dll.GetSlaveHealthDegradedCount(master, s),
dll.GetSlaveFreeRunDemoted(master, s) ? " (被迫 FreeRun)" : "");
dll.RecoverSlaveMailboxHealth(master, s); /* 通常内核已自动修复, 此为手动介入 */
}
}
ESC 端口错误结构:
typedef struct {
uint8_t RxErrorCount[4]; /* 各端口 RX 错误计数 [Port0-3], 物理层 CRC 或编码错误 */
uint8_t InvalidFrameCount[4]; /* 各端口无效帧计数 [Port0-3], 帧格式不正确 */
uint8_t LostLinkCount[4]; /* 各端口链路丢失计数 [Port0-3], 物理连接断开次数 */
} ec_esc_port_error_stats_t;
通信诊断
BOOL ReadSlavePortErrorCounters(uint16_t master_index, uint16_t slave_index,
uint8_t* rx_error, uint8_t* invalid_frame, uint8_t* lost_link);
BOOL ResetSlavePortErrorCounters(uint16_t master_index, uint16_t slave_index);
int ReadAllSlavePortErrorCounters(uint16_t master_index);
void* GetSlavePortErrorStats(uint16_t master_index, uint16_t slave_index);
void UpdateDiagnosticsWithESCErrors(uint16_t master_index);
ReadSlavePortErrorCounters() 读取从站各端口(4 字节数组对应 P0-P3)的 RX 错误、无效帧、链路丢失、转发 RX 错误计数。ResetSlavePortErrorCounters() 重置单从站,ReadAllSlavePortErrorCounters() 一次读取所有从站并返回成功数量。GetSlavePortErrorStats() 返回内部统计指针(无需释放)。
ReadSlavePortErrorCounters() 需实时读取从站,不建议高频调用(建议 ≥ 100ms 间隔)。
冗余和 DC 状态查询函数为"始终活跃",无需启用诊断模块。
示例:
uint8_t rx[4], inv[4], lost[4];
if (dll.ReadSlavePortErrorCounters(master, 1, rx, inv, lost)) {
for (int p = 0; p < 4; p++) {
if (rx[p] || inv[p] || lost[p])
printf("从站1 P%d: RX=%u Inv=%u Lost=%u\n", p, rx[p], inv[p], lost[p]);
}
}
冗余诊断
BOOL GetSlaveRedundancyActivated(uint16_t master_index, uint16_t slave_index);
BOOL GetSlavePrimaryLinkBroken(uint16_t master_index, uint16_t slave_index);
BOOL GetSlaveSecondaryLinkBroken(uint16_t master_index, uint16_t slave_index);
仅在主站启用冗余时有意义,仅检测物理断线,不含 CRC 故障。
| 函数 | 说明 |
|---|---|
| GetSlaveRedundancyActivated | 冗余被激活(从站未丢失,但网络中存在断线,冗余机制正在工作) |
| GetSlavePrimaryLinkBroken | 主线路断路(从主端口 Port0 到该从站的路径上存在断线) |
| GetSlaveSecondaryLinkBroken | 冗余线路断路(从副端口 Port1 反向到该从站的路径上存在断线) |
全局冗余状态和断线点请参考 主站诊断 - 冗余状态。
示例:
for (uint16_t s = 1; s <= slave_count; s++) {
if (!dll.GetSlaveRedundancyActivated(master, s)) continue;
printf("从站 %d: 冗余激活", s);
if (dll.GetSlavePrimaryLinkBroken(master, s)) printf(", 主线路断路");
if (dll.GetSlaveSecondaryLinkBroken(master, s)) printf(", 冗余线路断路");
printf("\n");
}
DC 同步
BOOL GetSlaveSyncWindowStatus(uint16_t master_index, uint16_t slave_index,
int* diff_ns, int* max_diff_ns, int* min_diff_ns,
BOOL* in_sync, uint32_t* out_of_sync_count);
void ResetSlaveSyncWindowStats(uint16_t master_index, uint16_t slave_index);
int16_t GetSlaveLinkQuality(uint16_t master_index, uint16_t slave_index);
GetSlaveSyncWindowStatus() 返回当前/最大/最小偏差、是否在同步窗口内(阈值由主站 SetSyncWindowThreshold() 控制)、累计失同步次数。GetSlaveLinkQuality() 返回链路质量百分比(0-100,综合 RxError + InvalidFrame + WKC 异常计算)。详见 DC 配置 与 主站诊断 - DC 同步。
完整示例
#define DYNAMIC_LOAD
#include "ethercat.h"
#include <stdio.h>
int main(void) {
dll_t dll;
LOAD_DLL(&dll, "DarraEtherCAT.dll");
uint16_t master = dll.Initialize();
uint16_t slave_count = dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
dll.Start(master);
for (uint16_t s = 1; s <= slave_count; s++) {
printf("--- 从站 %d ---\n", s);
/* 端口错误 */
uint8_t rx[4], inv[4], lost[4];
if (dll.ReadSlavePortErrorCounters(master, s, rx, inv, lost)) {
for (int p = 0; p < 4; p++)
if (rx[p] || inv[p] || lost[p])
printf(" P%d: RX=%u Inv=%u Lost=%u\n", p, rx[p], inv[p], lost[p]);
}
/* 链路质量 */
printf(" 链路质量: %d%%\n", dll.GetSlaveLinkQuality(master, s));
/* 冗余 */
if (dll.GetSlaveRedundancyActivated(master, s))
printf(" 冗余激活: 主断=%d, 副断=%d\n",
dll.GetSlavePrimaryLinkBroken(master, s),
dll.GetSlaveSecondaryLinkBroken(master, s));
/* DC 同步 */
int diff, max_d, min_d; BOOL sync; uint32_t oos;
if (dll.GetSlaveSyncWindowStatus(master, s, &diff, &max_d, &min_d, &sync, &oos))
printf(" DC: 偏差=%dns, 同步=%s, 失步=%u\n",
diff, sync ? "是" : "否", oos);
}
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}