主站诊断
建议通过 事件 驱动异常处理,而非自行轮询。 直接读取诊断属性适用于 UI 显示等场景。
单个从站的状态诊断、链路质量请参考 从站诊断。
功能概览
| 功能 | 说明 |
|---|---|
| 通信与性能统计 | 帧计数、丢包、抖动、PDO 丢帧、网口状态、拓扑 |
| DC 同步 | 同步窗口阈值、DCSyncLost 事件 |
| 冗余状态 | 冗余激活、故障点检测(断线 + CRC 故障定位) |
| 诊断控制 | 启停数据采集、重置统计 |
通信与性能统计
| 类别 | 函数 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 帧计数 | GetPacketLossRate(mi) | float | 只读 | 丢包率(0.0~1.0),TX vs RX 5 秒滑窗 |
| 帧计数 | GetLateFrameRate(mi) | float | 只读 | 过慢帧率(0.0~1.0),idx 出 8 帧窗 stale |
| 周期与抖动 | DarraEcat_GetCycleCount(mi) | uint64_t | 只读 | PDO 周期计数器,单调递增 |
| 周期与抖动 | GetDiagnosticsSnapshot → cycle_time_us | int | 只读 | 实际周期时间(微秒),对应诊断结构 CycleTimeSpan。字段为 0 时回退 1e6/Hz(1000000/frequency),不要写死 1ms。 |
| 周期与抖动 | GetDiagnosticsSnapshot → bus_avg_jitter_us / bus_max_jitter_us | double | 只读 | 最近5秒平均抖动 / 最大抖动(微秒)。隔离未生效时抖动变大;周期仍应钉在设定值。最短周期 31.25µs(Windows 推荐不低于 125µs)。 |
| 周期与抖动 | GetDiagnosticsSnapshot → frequency | int | 只读 | 每秒帧数(Hz)= 1e9/LoopCycleNs。相对设定 ±20%,或 max jitter 超过周期的 25%,连续偏离必须记日志并报警。看 PDOFrameLoss / DCSyncLost。不要按 1000Hz 写死。 |
| WKC | GetExpectedWKC(mi) / SetExpectedWKC(mi, wkc) | uint16_t | 读写 | 期望工作计数器 |
| WKC | GetPrimaryWKC() | uint16_t | 只读 | 主网口 WKC(冗余模式独立跟踪) |
| WKC | GetSecondaryWKC() | uint16_t | 只读 | 副网口 WKC(冗余模式独立跟踪) |
| WKC 镜像 | GetWkcActualMirror(mi) | uint16_t | 只读 | 总线实测工作计数器镜像(actual WKC)— 反映此刻哪些从站真的在响应。R1:如实值永不篡改。薄读零帧,实时 |
| WKC 镜像 | GetWkcExpectedMirror(mi) | uint16_t | 只读 | 期望工作计数器镜像(expected WKC)— 配置期/进 OP 确定的固定真值。R1:永不动态下调迁就劣化总线。薄读零帧,实时 |
| WKC 镜像 | GetWcDeficit(mi) | uint16_t | 只读 | WKC 缺额 = expected − actual(>0 表示有映射从站此刻没贡献 WKC,疑似掉站/热插拔)。从站恢复后自动归 0。薄读零帧,实时 |
| WKC 镜像 | GetMappedSlaveCount(mi) | uint16_t | 只读 | 已映射(参与 WKC 计算)的从站数。薄读零帧,实时 |
| WKC 镜像 | GetWcStateSeq(mi) | uint64_t | 只读 | 内核 WcState 缓存序列号(每次刷新自增)。判断 per-slave 镜像是否在两次读取间更新过。薄读零帧,实时 |
| 从站异常 | GetSlaveLinkQuality(mi, si) | int16_t | 只读 | 从站链路质量 0-100% |
| 网口帧计数 | GetWdkPrimaryFrameTx(mi) / GetWdkPrimaryFrameRx(mi) | uint32_t | 只读 | 主网卡发送/接收累计帧数 |
| 网口帧计数 | GetWdkSecondaryFrameTx(mi) / GetWdkSecondaryFrameRx(mi) | uint32_t | 只读 | 副网卡发送/接收累计帧数(冗余有效) |
| 网口帧计数 | GetWdkPdoIdxDropCount(mi) | uint32_t | 只读 | PDO idx 槽位被覆盖丢弃次数 |
| 详细诊断 | GetDetailedDiagnostics(mi) | void* | 只读 | 详细诊断数据指针(无需释放) |
| 通信统计 | GetCommunicationStats(mi) / ResetCommunicationStats(mi) | void* | 读写 | 通信统计指针 + 重置 |
详细诊断数据结构:
typedef struct {
uint32_t FrameErrors;
uint32_t LostFrames;
uint32_t ChecksumErrors;
uint32_t TimeoutFrames;
uint32_t RxErrorCount[EC_MAXSLAVE];
uint32_t TxErrorCount[EC_MAXSLAVE];
uint16_t LostLinkCount[EC_MAXSLAVE];
uint16_t InvalidFrameCount[EC_MAXSLAVE];
uint16_t WorkingCounterErrors;
uint16_t ConsecutiveWkcErrors;
uint32_t TotalWkcMismatches;
uint32_t PdoLostFrames[EC_MAXGROUP];
uint32_t PdoConsecutiveLost[EC_MAXGROUP];
uint32_t PdoMaxConsecutiveLost[EC_MAXGROUP];
int16_t LinkQualityPercent[EC_MAXSLAVE];
uint32_t PrimaryPortTxCount;
uint32_t PrimaryPortRxCount;
uint32_t SecondaryPortTxCount;
uint32_t SecondaryPortRxCount;
uint32_t PrimaryPortErrors;
uint32_t SecondaryPortErrors;
} ec_diagnostics_data_t;
DarraEcat_GetCycleCount()
uint64_t DarraEcat_GetCycleCount(uint16_t master_index);
PDO 周期计数器。普通 C 函数,直接调用,单调递增。
返回值:
uint64_t— 已完成的 PDO 周期数
实际周期(微秒)读诊断结构 CycleTimeSpan 或快照 cycle_time_us。字段为 0 时回退 1e6 / Hz(1000000 / frequency),不要写死 1000µs。
示例:
uint64_t cycles = DarraEcat_GetCycleCount(master);
ec_diagnostics_snapshot_t snap;
if (GetDiagnosticsSnapshot(master, &snap)) {
int cycle_us = snap.cycle_time_us;
if (cycle_us <= 0 && snap.frequency > 0)
cycle_us = (int)(1000000 / snap.frequency);
printf("周期计数=%llu, 周期=%d us, 频率=%d Hz\n",
(unsigned long long)cycles, cycle_us, snap.frequency);
}
PDO 丢帧统计
void GetPDOFrameLossStats(uint16_t master_index, uint8_t group,
uint32_t* total_lost, uint32_t* consecutive_lost,
uint32_t* max_consecutive_lost);
void ResetPDOFrameLossStats(uint16_t master_index, uint8_t group);
按组(0-7)查询 PDO 丢帧统计:累计丢帧、当前连续丢帧、历史最大连续丢帧。
WKC 镜像(掉站可观测)
uint16_t GetWkcActualMirror(uint16_t master_index); /* 实测 WKC 镜像 */
uint16_t GetWkcExpectedMirror(uint16_t master_index); /* 期望 WKC 镜像 (固定真值) */
uint16_t GetWcDeficit(uint16_t master_index); /* 缺额 = expected - actual */
uint16_t GetMappedSlaveCount(uint16_t master_index); /* 已映射从站数 */
uint64_t GetWcStateSeq(uint16_t master_index); /* WcState 缓存序列号 (自增) */
WKC 镜像是内核 per-slave WcState 诊断缓存的主站级聚合镜像,全部薄读零帧(不缓存、不需刷新——内核每周期、WKC 异常时立即维护,DLL 保证读到即此刻真实总线现实)。配合每个从站的 GetSlaveWcState(mi, si) 可定位到具体掉了哪个从站。
R1 可观测性约定(不可违反):
GetWkcExpectedMirror(mi)= 配置期 / 进 OP 时确定的固定真值,拓扑固定它就固定,概念上不可变,永不动态下调迁就劣化总线。GetWkcActualMirror(mi)= 总线实测值,如实反映此刻哪些从站真的在响应,永不篡改。GetWcDeficit(mi) = expected − actual。> 0不是 master 故障,而是有映射从站此刻没贡献 WKC(疑似掉站 / 热插拔恢复中);从站恢复后自动归 0,无需任何重置调用。
示例:
uint16_t act = GetWkcActualMirror(master);
uint16_t exp = GetWkcExpectedMirror(master);
printf("WKC: %u / %u (映射从站 %u 个)\n", act, exp, GetMappedSlaveCount(master));
uint16_t deficit = GetWcDeficit(master);
if (deficit > 0) {
printf("WKC 缺额 %u — 有从站没在响应, 逐个从站定位:\n", deficit);
for (int si = 1; si <= ec_slave_count(master); si++) {
if (GetSlaveWcState(master, si) == 0) /* 0 = 未贡献 */
printf(" 从站 %d 未贡献 WKC\n", si);
}
}
uint64_t seq = GetWcStateSeq(master); /* 序列号判断镜像是否更新过 (无需轮询帧) */
热插拔重建
/* 运行中一次性热插拔重建拓扑 (2.11.0 为 no-op, 见下) */
int HotSwapRebuild(uint16_t master_index);
设计意图:任意状态下调用一次,做在线增量恢复(重扫从站 + 重建拓扑图 + 恢复运行态),适用于现场拔掉 / 换上一个模块后不停总线地把网络带回运行态;只对异常 / 拔插站做单站恢复,不给健康 OP 站发状态命令。它不是"把整网释放后从零重建"的做法。
热插拔重建功能已屏蔽(发布版稳定优先)。本入口只记一条 WARNING 日志后恒返回 0(RESULT_OK),不重扫、不重建拓扑、不改任何状态——调用成功 ≠ 拓扑已重建,需要"确实重建"的调用方不要依赖返回值判成功。主站未初始化 / 索引非法时返回 -6(RESULT_ERR_NOT_INITIALIZED)。
返回值(功能启用后的错误码契约,当前不会出现):
| 返回码 | 名称 | 含义与处理 |
|---|---|---|
0 | OK | 成功:从站全部回到 OP |
-20 | BUSY | 另一操作正在进行——稍后重试 |
-21 | RESCAN_0 | 重扫到 0 个从站——已停在 PreOp 安全态,检查物理链路 / 供电 |
-22 | SDO_ABORT | 重配被从站 SDO abort 拒绝——检查 PDO 映射 / Startup 参数 |
-23 | NO_OP | 部分从站未达 OP——该站标记 lost 并带 AL code,其余站继续 OP,逐站查 AL Status Code |
-24 | TIMEOUT | 重建超时 |
-25 | IDX_FULL | 帧索引池饱和 |
约束(R1 如实可观测,启用后仍适用): 失败返错误码不掩盖——绝不靠篡改内部计数让结果"看起来对";即使 0 个从站在 OP,PDO 循环也照常运行(周期不掉、帧不停),期望 WKC 不动态下调迁就劣化总线。
示例:
int rc = HotSwapRebuild(master);
if (rc == 0) {
/* 2.11.0: 恒返回 0, 不代表拓扑已重建 */
printf("HotSwapRebuild 已调用(2.11.0 为 no-op)\n");
} else {
printf("HotSwapRebuild 失败: %d\n", rc);
}
DC 同步
自动监控(ETG.1500 5.13.3),每秒检查各从站时间偏差。超出 SyncWindowThreshold 阈值时触发 DCSyncLost 回调。
void SetSyncWindowThreshold(uint16_t master_index, int threshold_ns);
int GetSyncWindowThreshold(uint16_t master_index);
threshold_ns 默认 1000ns。
单个从站的同步状态请使用 GetSlaveSyncWindowStatus(),详见 从站 DC 同步。
冗余状态
int GetBreakPoints(uint16_t master_index,
uint16_t* out_slaves, uint8_t* out_ports,
uint8_t* out_types, uint16_t max_results);
int GetRingMode(uint16_t master_index);
BOOL GetSecondaryLinkStatus(uint16_t master_index);
GetBreakPoints() 检测当前所有故障点,统一覆盖两类物理故障:
| FaultType | 类型 | 检测方式 | 典型场景 |
|---|---|---|---|
EC_DIAG_BP_LINK_LOST(0) | 断线 | DL Status 端口物理链路丢失 | 拔线、线缆断裂 |
EC_DIAG_BP_CRC_FAULT(1) | CRC 故障 | 端口级 RxError + InvalidFrame 持续增长 | 接触不良、线缆老化 |
GetRingMode() 返回 ec_ring_mode_t: EC_RING_INACTIVE(0)=未激活、EC_RING_DUAL(1)=双向冗余、EC_RING_DEGRADED(2)=secondary 链路不可用,仅 primary 工作。
故障线缆段定位: 当相邻从站对向端口(如从站 N 的 P1 与从站 N+1 的 P0)同时报故障,说明线缆段有问题;仅单侧报故障则定位到该端口连接器。
单个从站的冗余状态请参考 从站诊断 - 冗余诊断。
诊断快照
BOOL GetDiagnosticsSnapshot(uint16_t master_index, ec_diagnostics_snapshot_t* snapshot);
一次调用获取所有诊断数据的一致快照(内部加锁,线程安全),避免多次属性访问导致的数据不一致和性能开销。适合 UI 刷新和日志记录场景。
typedef struct {
int frequency; /* 每秒帧数 (Hz) */
uint32_t error_count; /* 每秒错误数 */
float packet_loss_rate; /* 丢包率 (0.0-1.0) */
float late_frame_rate; /* 过慢帧率 (0.0-1.0) */
double mailbox_latency_avg_us; /* 邮箱收发延迟 - 平均 (微秒) */
double mailbox_latency_us; /* 邮箱收发延迟 - 最大 (微秒) */
int cycle_time_us; /* 实际周期时间 (微秒);0 时回退 1e6/Hz */
uint16_t wkc_actual; /* 当前 WKC */
uint16_t wkc_expected; /* 期望 WKC */
uint32_t bus_cycle_hz; /* 总线频率 (Hz) */
double bus_max_jitter_us; /* 总线最大抖动 (微秒) */
double bus_avg_jitter_us; /* 总线平均抖动 (微秒) */
double bus_roundtrip_us; /* 总线往返延迟 (微秒) */
double bus_load_percent; /* 通讯负载 (%) */
uint32_t smi_count; /* SMI 次数 */
double smi_peak_us; /* SMI 峰值 (微秒) */
int primary_port_ok; /* 主端口正常 (BOOL) */
int secondary_port_ok; /* 副端口正常 (BOOL) */
int redundancy_active; /* 冗余激活 (BOOL) */
} ec_diagnostics_snapshot_t;
mailbox_latency_us / mailbox_latency_avg_us 反映邮箱(CoE/SoE/FoE/EoE/AoE/VoE 等非周期、请求-响应)事务的收发往返时延 —— 从请求发出到响应返回的耗时(最近 1 秒结算的最大值 / 平均值)。
PDO 过程数据已是纯内核 RT 收发 + 内核共享内存指针零拷贝,不存在"用户态每周期通知"环节,旧的"应用抖动"指标失去意义,故改为统计邮箱事务时延。总线时序仍由 bus_max_jitter_us / bus_avg_jitter_us 反映。
示例:
ec_diagnostics_snapshot_t snap;
if (GetDiagnosticsSnapshot(master, &snap)) {
printf("频率: %d Hz, 邮箱收发延迟: %.2f us, WKC: %d/%d, 丢包: %.4f\n",
snap.frequency, snap.mailbox_latency_us,
snap.wkc_actual, snap.wkc_expected, snap.packet_loss_rate);
}
诊断控制
void SetDiagnosticsEnabled(uint16_t master_index, BOOL enable);
BOOL GetDiagnosticsEnabled(uint16_t master_index);
void ResetDiagnostics(uint16_t master_index);
SetDiagnosticsEnabled() 启用/禁用诊断数据采集(默认关闭,启用后周期性采样)。ResetDiagnostics() 一次性重置所有诊断统计:基础诊断统计、PDO 丢帧统计、DC 同步窗口统计、所有从站的端口错误计数器。只清空主站层面的统计数据, 不影响 FSoE 安全连接状态机。
AL 错误分类
/* 定义于 master/diagnostics_ext.h, 按 AL Status Code 高位段分类 */
typedef enum {
AL_ERR_NONE = 0,
AL_ERR_GENERAL = 1, /* 0x0001-0x000F 通用错误 */
AL_ERR_STARTUP = 2, /* 0x0010-0x001F 启动 (PDO 映射 / SM 配置) */
AL_ERR_MAILBOX = 3, /* 0x0020-0x002F 邮箱 */
AL_ERR_WATCHDOG = 4, /* 0x0030-0x003F 看门狗 */
AL_ERR_SYNC = 5, /* 0x0040-0x004F 同步 */
AL_ERR_DC = 6, /* 0x0050-0x005F DC */
AL_ERR_MBX_EOE = 7, /* 0x0060-0x006F EoE 邮箱 */
AL_ERR_UNKNOWN = 99
} DarraAlErrorClass;
DarraAlErrorClass diagnostics_classify_al_error(uint16_t alStatusCode);
对从站 AL Status Code 进行分类,帮助快速判断错误性质和处理策略。分类依据 AL Status Code 的高位段。
| 分类 | 处理建议 |
|---|---|
| AL_ERR_GENERAL | 通用错误,查阅 ETG.1000 或从站手册 |
| AL_ERR_STARTUP | 检查 PDO 映射、SM 配置、Startup 参数 |
| AL_ERR_MAILBOX | 检查邮箱协议配置 (CoE/FoE 等) |
| AL_ERR_WATCHDOG | PDO 周期过慢或通信中断,检查丢帧 |
| AL_ERR_SYNC | 同步错误,检查 SYNC0 周期配置 |
| AL_ERR_DC | DC 同步配置问题,检查 DC 启动时间 |
| AL_ERR_MBX_EOE | EoE 邮箱错误 |
| AL_ERR_UNKNOWN | 未在已知段内,查阅 ETG.1000 或从站手册 |
0x001E无效输入映射 — AL_ERR_STARTUP0x001D无效输出映射 — AL_ERR_STARTUP0x0011无效邮箱配置 — AL_ERR_STARTUP(按高位段归入 STARTUP,与语义标签不同段)0x002D同步错误 — AL_ERR_MAILBOX0x0032DC 同步超时 — AL_ERR_WATCHDOG0x0052DC PLL 错误 — AL_ERR_DC
打印从站状态时, C 可用 ec_state_name(state) inline 把 EC_STATE_INIT/PRE_OP/SAFE_OP/OP/BOOT 直接转字符串, 屏蔽错误位 (& 0x0F);同样 ec_link_name(ec_link(mi)) 输出 "UP" / "DOWN" / "REDUNDANCY"。 详见 inline 包装。
从站错误计数器
BOOL ResetSlavePortErrorCounters(uint16_t master_index, uint16_t slave_index);
BOOL ReadSlavePortErrorCounters(uint16_t master_index, uint16_t slave_index,
uint8_t* rx_error, uint8_t* invalid_frame, uint8_t* lost_link);
int ReadAllSlavePortErrorCounters(uint16_t master_index);
void* GetSlavePortErrorStats(uint16_t master_index, uint16_t slave_index);
读取/重置从站 ESC 端口错误计数器(ETG.1000.4)。rx_error/invalid_frame/lost_link 各为 4 字节数组,对应 P0-P3 端口(GetSlavePortErrorStats() 返回的 ec_esc_port_error_stats_t 另含 FwdRxErrorCount[4])。
示例:
uint8_t rx[4], inv[4], lost[4];
if (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]);
}
}
配置预检查
int ec_validate_config(uint16_t master_index);
Build 前预检查主站配置是否完整(网络适配器、从站发现、PDO 映射等)。返回 0 表示配置有效,非 0 表示配置问题数量。
主站身份与诊断对象
BOOL GetMasterIdentity(uint16_t master_index, ec_master_identity_t* identity);
BOOL GetMasterDiagData(uint16_t master_index, ec_master_diag_data_t* diag);
读取主站 ETG.1510 标准对象:身份对象 0x1018(VendorID/ProductCode/RevisionNumber/SerialNumber)和诊断数据对象 0xF120。
诊断消息 (CoE 0x10F3 诊断历史)
从站对象 0x10F3(诊断历史对象,ETG.1020)是标准对象字典对象,应用层用通用 SDO 读写原语 dx_sdo_read / dx_sdo_write 直接读取其各子索引即可(0x10F3:04 是否有新消息、0x10F3:02 最新编号、0x10F3:05 标志、0x10F3:06..FF 各条消息、0x10F3:03 写编号确认已读)。完整子索引布局与读取流程示例见 CoE 诊断历史。
并非所有从站都支持 0x10F3 诊断历史对象。不支持的从站读取时 DarraEcat_dx_sdo_read_ex 回填的 out_abort_code 为对象不存在错误。此对象通过 SDO 读取,不建议在实时路径中高频调用。
紧急控制与中止
void AbortScan(void);
void ResetScanAbort(void);
void AbortNetwork(void);
void ResetAbortNetwork(void);
void EmergencyCloseNics(void);
终止正在进行的网络扫描或紧急关闭网卡,避免长操作卡死调用方。
EmergencyCloseNics 不走标准 Stop / Dispose 流程, 仅在死锁兜底场景使用。 正常退出请用 Stop + Dispose + EcClose。
完整示例
#include "ethercat.h"
#include <stdio.h>
int main(void) {
uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);
SetDiagnosticsEnabled(master, TRUE);
/* 一致快照 */
ec_diagnostics_snapshot_t snap;
if (GetDiagnosticsSnapshot(master, &snap)) {
printf("频率: %d Hz, 邮箱收发延迟: %.2f us, 丢包: %.4f\n",
snap.frequency, snap.mailbox_latency_us, snap.packet_loss_rate);
}
/* PDO 丢帧 */
uint32_t total, cur, max;
GetPDOFrameLossStats(master, 0, &total, &cur, &max);
if (cur > 10) printf("警告: PDO 连续丢帧 %u\n", cur);
/* 故障点 */
uint16_t bp_s[8]; uint8_t bp_p[8], bp_t[8];
int n = GetBreakPoints(master, bp_s, bp_p, bp_t, 8);
for (int i = 0; i < n; i++) {
printf("故障: 从站%d P%d %s\n", bp_s[i], bp_p[i],
bp_t[i] == EC_DIAG_BP_LINK_LOST ? "断线" : "CRC故障");
}
/* AL 错误分类 */
uint16_t al = GetSlaveALStatusCode(master, 1);
if (al) {
DarraAlErrorClass c = diagnostics_classify_al_error(al);
printf("从站1 错误 0x%04X: 分类=%d\n", al, c);
}
ResetDiagnostics(master);
Stop(master);
Dispose(master);
return 0;
}
按问题分块的诊断字段指南 (C 风格)
GetDetailedDiagnostics(mi) 返回 ec_diagnostics_data_t*(字段名见上文 详细诊断数据结构,全部 PascalCase)。总线 RT 性能指标走 GetDiagnosticsSnapshot() 输出的 ec_diagnostics_snapshot_t。
| 现场症状 | C 接口 |
|---|---|
| 是不是丢包? | diag->LostFrames + GetPacketLossRate(mi) |
| 是偶发还是持续? | diag->ConsecutiveWkcErrors (≥5 = 持续) |
| WKC 累计不匹配? | diag->TotalWkcMismatches / diag->WorkingCounterErrors |
| CRC 问题? | diag->InvalidFrameCount[i] (ESC 0x0304) |
| 哪根网线断了? | GetBreakPoints(mi, ...) |
| 总线抖动 / 周期? | snap.bus_max_jitter_us / snap.bus_cycle_hz (GetDiagnosticsSnapshot) |
帧错误 (TX vs RX 对比)
ec_diagnostics_data_t 字段:
- LostFrames:帧根本没回来
- TimeoutFrames:PDO 周期未响应
- WorkingCounterErrors:WKC 不匹配
- ConsecutiveWkcErrors:连续 WKC 错误 (核心区分偶发/持续)
- TotalWkcMismatches:累计 WKC 不匹配总数
- FrameErrors:派生总和
- ChecksumErrors:物理层 CRC (从 ESC 派生)
端口收发
PrimaryPortTxCount / PrimaryPortRxCount, SecondaryPortTxCount / SecondaryPortRxCount, PrimaryPortErrors / SecondaryPortErrors (5 秒滑窗)
per-slave 物理层 (ESC 寄存器, 数组 1-based)
RxErrorCount[i] (ESC 0x0300) | InvalidFrameCount[i] (ESC 0x0304 CRC) | LostLinkCount[i] (ESC 0x0310) | LinkQualityPercent[i]
PDO 丢帧 (每组)
PdoLostFrames[g] (累计丢帧) | PdoConsecutiveLost[g] (当前连续) | PdoMaxConsecutiveLost[g] (历史最大连续)
总线 RT 性能
通过 GetDiagnosticsSnapshot(mi, &snap) 一致读 ec_diagnostics_snapshot_t:
- bus_cycle_hz / bus_max_jitter_us / bus_avg_jitter_us / bus_roundtrip_us
- frequency / mailbox_latency_us / mailbox_latency_avg_us / cycle_time_us / packet_loss_rate
完整诊断流程示例 (C)
ec_diagnostics_data_t* diag = (ec_diagnostics_data_t*)GetDetailedDiagnostics(mi);
// 1. 持续 vs 偶发
if (diag->ConsecutiveWkcErrors >= 5)
printf("持续 WKC 错误\n");
// 2. 丢包率
float loss = GetPacketLossRate(mi);
if (loss > 0.01f) printf("丢包率 %.2f%% 严重\n", loss * 100.0f);
// 3. 故障定位
uint16_t bp_slaves[16]; uint8_t bp_ports[16]; uint8_t bp_types[16];
int bpn = GetBreakPoints(mi, bp_slaves, bp_ports, bp_types, 16);
for (int i = 0; i < bpn; i++)
printf("故障点: 从站 %d P%d (类型=%d)\n", bp_slaves[i], bp_ports[i], bp_types[i]);
// 4. 最不稳定从站 (按 ESC CRC 错误计数)
int worst = 0; uint16_t worstCnt = 0;
for (int i = 1; i < EC_MAXSLAVE; i++)
if (diag->InvalidFrameCount[i] > worstCnt)
{ worstCnt = diag->InvalidFrameCount[i]; worst = i; }
if (worstCnt > 0) printf("CRC 错误最多的从站: #%d (%u 次)\n", worst, worstCnt);
// 5. 总线 RT 性能
ec_diagnostics_snapshot_t snap;
if (GetDiagnosticsSnapshot(mi, &snap))
printf("总线 %u Hz, 最大抖动 %.2f us\n",
snap.bus_cycle_hz, snap.bus_max_jitter_us);