从站诊断
每个从站提供独立的诊断属性和方法, 可查询端口错误、冗余、DC 同步等信息。
从站基础属性
从站状态 (State()、ErrorCode()、IsLost()) 等基础属性请参考 属性与方法。
配合事件使用
建议通过 事件 驱动异常处理 (如 SlaveStateChanged、SlaveOffline、DCSyncLost), 而非轮询。直接读取从站诊断属性适用于 UI 显示等场景。
主站级诊断
全局通信统计、丢包率、PDO 丢帧汇总请参考 主站诊断。
功能概览
| 功能 | 访问方式 | 说明 |
|---|---|---|
| 通信诊断 | slave.ReadPortErrors() | ESC 端口错误计数器 |
| 冗余诊断 | slave.RedundancyActivated() / PrimaryLinkBroken() / SecondaryLinkBroken() | 冗余激活、主/冗余线路断路检测 |
| DC 同步 | slave.IsInSync() / SyncTimeDifference() / GetSyncWindowStatus() | 同步状态、时间差、失步次数 |
| 拓扑信息 | slave.ActivePorts() / ParentStation() | 端口连接和父节点 |
| 链路质量 | diag.SlaveLinkQuality(slave) | 从站链路质量 |
备注
冗余和 DC 属性为"不可停" (始终活跃), 无需启用 master.GetDiagnostics().Enabled。
ReadPortErrors() 需实时读取从站, 不建议高频调用。
通信诊断
ReadPortErrors()
std::optional<EscPortErrors> ReadPortErrors() const;
读取从站 ESC 端口错误计数器, 返回各端口的物理层错误统计。
返回值:
std::optional<EscPortErrors>— 端口错误计数器, 读取失败返回std::nullopt
相关结构:
struct EscPortErrors {
uint8_t RxError[4]; // 各端口 RX 错误计数 [Port0-3]
uint8_t InvalidFrame[4]; // 各端口无效帧计数 [Port0-3]
uint8_t LostLink[4]; // 各端口链路丢失计数 [Port0-3]
bool HasErrors; // 是否存在任何错误
};
struct SyncWindowStatus {
int DiffNs; // 当前与参考时钟的时间差 (纳秒)
int MaxDiffNs; // 最大时间差 (纳秒)
int MinDiffNs; // 最小时间差 (纳秒)
bool InSync; // 是否在同步窗口内
uint32_t OutOfSyncCount; // 失步次数
};
示例:
for (int i = 1; i <= master.SlaveCount(); ++i) {
auto& s = master.GetSlave(i);
auto errors = s.ReadPortErrors();
if (errors && errors->HasErrors) {
printf("从站 %d: RX=[%d,%d,%d,%d] 无效帧=[%d,%d,%d,%d] 链路丢失=[%d,%d,%d,%d]\n", i,
errors->RxError[0], errors->RxError[1], errors->RxError[2], errors->RxError[3],
errors->InvalidFrame[0], errors->InvalidFrame[1], errors->InvalidFrame[2], errors->InvalidFrame[3],
errors->LostLink[0], errors->LostLink[1], errors->LostLink[2], errors->LostLink[3]);
}
}
冗余诊断
通过从站对象访问冗余状态。仅在主站启用冗余时有意义。RedundancyActivated() 表示从站未丢失但网络中存在物理断线; PrimaryLinkBroken() / SecondaryLinkBroken() 表示主/副端口路径上存在断线。CRC 故障不触发这些标志。
主站级监控
全局冗余状态和断线点请参考 主站诊断 - 冗余状态。
示例:
for (int i = 1; i <= master.SlaveCount(); ++i) {
auto& s = master.GetSlave(i);
if (!s.RedundancyActivated()) continue;
printf("从站 %d: 冗余激活", i);
if (s.PrimaryLinkBroken()) printf(", 主线路断路");
if (s.SecondaryLinkBroken()) printf(", 冗余线路断路");
printf("\n");
}
DC 同步
通过从站对象访问 DC 同步状态。IsInSync() 返回是否在同步窗口内 (阈值由 master.GetDiagnostics().SyncWindowThreshold() 控制); SyncTimeDifference() 返回与参考时钟的纳秒级时间差; GetSyncWindowStatus() 返回完整 SyncWindowStatus 结构。
主站级监控
全局阈值和事件请参考 主站诊断 - DC 同步。
示例:
for (int i = 1; i <= master.SlaveCount(); ++i) {
auto& s = master.GetSlave(i);
if (!s.HasDC()) continue;
auto status = s.GetSyncWindowStatus();
if (status) {
printf("从站 %d: 时间差=%dns 最大=%dns 同步=%s 失步=%u\n", i,
status->DiffNs, status->MaxDiffNs,
status->InSync ? "是" : "否", status->OutOfSyncCount);
}
}
完整示例
#include "ethercat.hpp"
using namespace darra::ethercat;
void slave_diagnostics_report(EtherCATMaster& master) {
auto& diag = master.GetDiagnostics();
for (int i = 1; i <= master.SlaveCount(); ++i) {
auto& s = master.GetSlave(i);
printf("--- 从站 %d: %s ---\n", i, s.Name().c_str());
auto errors = s.ReadPortErrors();
if (errors && errors->HasErrors) {
printf(" 端口错误: RX=[%d,%d,%d,%d]\n",
errors->RxError[0], errors->RxError[1],
errors->RxError[2], errors->RxError[3]);
}
if (s.RedundancyActivated()) {
printf(" 冗余激活: 主线路断=%s 冗余线路断=%s\n",
s.PrimaryLinkBroken() ? "是" : "否",
s.SecondaryLinkBroken() ? "是" : "否");
}
if (s.HasDC()) {
auto sync = s.GetSyncWindowStatus();
if (sync) {
printf(" DC: 时间差=%dns 同步=%s 失步=%u\n",
sync->DiffNs, sync->InSync ? "是" : "否",
sync->OutOfSyncCount);
}
}
printf(" 活动端口=0x%02X 父节点=%u 链路质量=%d\n",
s.ActivePorts(), s.ParentStation(), diag.SlaveLinkQuality(i));
}
}