主站诊断
建议通过 事件 驱动异常处理,而非自行轮询。 直接读取诊断属性适用于 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 |
| WKC | GetExpectedWKC(mi) / SetExpectedWKC(mi, wkc) | uint16_t | 读写 | 期望工作计数器 |
| WKC | GetPrimaryWKC() | uint16_t | 只读 | 主网口 WKC(冗余模式独立跟踪) |
| WKC | GetSecondaryWKC() | uint16_t | 只读 | 副网口 WKC(冗余模式独立跟踪) |
| 从站异常 | 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 OutOfOrderFrames;
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;
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 丢帧统计:累计丢帧、当前连续丢帧、历史最大连续丢帧。
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 | 类型 | 检测方式 | 典型场景 |
|---|---|---|---|
| 0 | 断线 | DL Status 端口物理链路丢失 | 拔线、线缆断裂 |
| 1 | CRC 故障 | 端口级 RxError + InvalidFrame 持续增长 | 接触不良、线缆老化 |
GetRingMode() 返回值: 0=Inactive(未激活), 1=Dual(双向冗余), 2=Degraded(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 avg_jitter_us; /* 平均抖动 (微秒) */
double max_jitter_us; /* 最大抖动 (微秒) */
int cycle_time_us; /* 实际周期时间 (微秒) */
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; /* 总线往返延迟 (微秒) */
int primary_port_ok; /* 主端口正常 (BOOL) */
int secondary_port_ok; /* 副端口正常 (BOOL) */
int redundancy_active; /* 冗余激活 (BOOL) */
} ec_diagnostics_snapshot_t;
示例:
ec_diagnostics_snapshot_t snap;
if (dll.GetDiagnosticsSnapshot(master, &snap)) {
printf("频率: %d Hz, 抖动: %.2f us, WKC: %d/%d, 丢包: %.4f\n",
snap.frequency, snap.max_jitter_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 错误分类
typedef enum {
AL_ERROR_NONE = 0, /* 无错误 */
AL_ERROR_TRANSIENT = 1, /* 瞬态: 同步丢失 / DC 超时 / 看门狗 */
AL_ERROR_CONFIGURATION = 2, /* 配置错误: PDO 映射 / SM / 邮箱 */
AL_ERROR_HARDWARE = 3, /* 硬件错误: EEPROM / 端口 / 物理层 */
AL_ERROR_UNKNOWN = 4 /* 未在已知列表中 */
} DarraAlErrorClass;
DarraAlErrorClass diagnostics_classify_al_error(uint16_t al_status_code);
对从站 AL Status Code 进行分类,帮助快速判断错误性质和处理策略。
| 分类 | 处理建议 |
|---|---|
| Transient | 瞬态错误,可重试状态转换,通常自动恢复 |
| Configuration | 检查 PDO 映射、SM 配置、Startup 参数 |
| Hardware | 检查从站硬件、线缆、电源 |
| Unknown | 查阅 ETG.1000 或从站手册 |
0x001E无效输入映射 — Configuration0x001D无效输出映射 — Configuration0x0011无效邮箱配置 — Configuration0x002D同步错误 — Transient0x0032DC 同步超时 — Transient0x0050EEPROM 错误 — Hardware
从站错误计数器
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 端口。
示例:
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]);
}
}
配置预检查
int ValidateConfig(uint16_t master_index, char* error_buf, int buf_size);
Build 前预检查主站配置是否完整(网络适配器、从站发现、PDO 映射等)。返回 0 表示配置有效,非 0 表示配置问题数量;error_buf 接收错误描述(可为 NULL)。
主站身份与诊断对象
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。
诊断消息
int coe_read_diagnostic_messages(uint16_t master_index, uint16_t slave_index,
ec_diag_message_t* out_messages, int max_count);
通过 CoE 读取从站对象 0x10F3(诊断历史对象,ETG.1020)中的诊断消息。返回从站记录的诊断事件数量。
并非所有从站都支持 0x10F3 诊断历史对象。不支持的从站调用时返回 0。此接口通过 SDO 读取,不建议在实时路径中高频调用。
紧急控制与中止
void AbortScan(void);
void ResetScanAbort(void);
void AbortNetwork(void);
void ResetAbortNetwork(void);
void EmergencyCloseNics(void);
终止正在进行的网络扫描或紧急关闭网卡,避免长操作卡死调用方。
EmergencyCloseNics 不走标准 Stop / Dispose 流程, 仅在死锁兜底场景使用. 正常退出请用 Stop + Dispose + EcClose.
完整示例
#define DYNAMIC_LOAD
#include "ethercat.h"
#include <stdio.h>
int main(void) {
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
dll.Start(master);
dll.SetDiagnosticsEnabled(master, TRUE);
/* 一致快照 */
ec_diagnostics_snapshot_t snap;
if (dll.GetDiagnosticsSnapshot(master, &snap)) {
printf("频率: %d Hz, 抖动: %.2f us, 丢包: %.4f\n",
snap.frequency, snap.max_jitter_us, snap.packet_loss_rate);
}
/* PDO 丢帧 */
uint32_t total, cur, max;
dll.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 = dll.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] == 0 ? "断线" : "CRC故障");
}
/* AL 错误分类 */
uint16_t al = dll.GetSlaveALStatusCode(master, 1);
if (al) {
DarraAlErrorClass c = diagnostics_classify_al_error(al);
printf("从站1 错误 0x%04X: 分类=%d\n", al, c);
}
dll.ResetDiagnostics(master);
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}