主站诊断
通过 master.Diagnostics() 访问所有诊断、监控、统计功能。
建议通过 事件 驱动异常处理,而非自行轮询。 直接读取诊断属性适用于 UI 显示等场景。
单个从站的状态诊断、链路质量请参考 从站诊断。
功能概览
| 功能 | 说明 |
|---|---|
| 通信与性能统计 | 帧计数、丢包、抖动、PDO 丢帧、网口状态、拓扑 |
| DC 同步 | 同步窗口阈值、DCSyncLost 事件 |
| 冗余状态 | 冗余激活、故障点检测(断线 + CRC 故障定位) |
| 诊断快照 | 一次取所有诊断指标的一致快照 |
| 诊断控制 | 启停数据采集、重置统计 |
通信与性能统计
| 类别 | 方法 | 类型 | 需启用 | 说明 |
|---|---|---|---|---|
| 帧计数 | getRTcnt() | int | 是 | 每秒帧数(Hz),5 秒平均 |
| getErrorCnt() | long | 是 | 每秒错误数,5 秒平均 | |
| getPacketLossRate() | float | 是 | 最近 5 秒丢包率(TX vs RX 滑窗, 0.0~1.0) | |
| getLateFrameRate() | float | 是 | 过慢帧率(idx 出 8 帧窗 stale, 不计丢, 0.0~1.0) | |
| 周期与抖动 | getCycleTimeSpan() | int | 是 | 实际周期时间(微秒) |
| getAvgJitterUs() | double | 是 | 最近 5 秒平均抖动(微秒) | |
| 邮箱延迟 | getMailboxLatencyUs() | double | 不可停 | 邮箱收发延迟 - 最大(微秒),见下方说明 |
| getMailboxLatencyAvgUs() | double | 不可停 | 邮箱收发延迟 - 平均(微秒) | |
| 总线实时性能 | getBusCycleHz() | long | 不可停 | 总线频率(Hz) |
| getBusMaxJitterUs() | double | 不可停 | 总线最大抖动(微秒) | |
| getBusAvgJitterUs() | double | 不可停 | 总线平均抖动(微秒) | |
| getBusRoundtripUs() | double | 不可停 | 报文往返延迟(微秒) | |
| getBusLoadPercent() | double | 不可停 | 通讯负载(%)-- RTT/周期×100 | |
| getSmiCount() | long | 不可停 | SMI 累计次数 | |
| getSmiPeakUs() | double | 不可停 | SMI 峰值抖动(微秒) | |
| PDO 丢帧 | getPDO().getTotalLost() | int | 不可停 | 累计丢帧数(所有组合计) |
| getPDO().getConsecutiveLost() | int | 不可停 | 当前连续丢帧数(所有组中最大值) | |
| getPDO().getFrameLossStats(int) | int[] | 不可停 | 指定组丢帧统计 [累计, 连续, 最大连续] | |
| 从站异常 | getWorstSlaveIndex() | int | 不可停 | 异常率最高的从站索引 |
| getWorstLinkQuality() | short | 不可停 | 最差从站健康度(%) | |
| 网口状态 | isPrimaryPortOk() | boolean | 不可停 | 主端口是否正常 |
| isSecondaryPortOk() | boolean | 不可停 | 副端口是否正常 | |
| getPrimaryPortErrors() | long | 不可停 | 主端口最近 5 秒错误数 | |
| getSecondaryPortErrors() | long | 不可停 | 副端口最近 5 秒错误数 | |
| 拓扑 | getTopologyDescription() | String | 不可停 | 拓扑描述(线性/环形) |
| getTimingMode() | String | 不可停 | 定时模式描述("硬件定时器" / "RT就绪" / "降级" / "RT错误"),WDK 驱动必须就绪 |
| 指标 | 公式 | 说明 |
|---|---|---|
| RTcnt | 采样周期帧数 / 窗口秒数 | 滑动窗口平均帧频 |
| ErrorCnt | 采样周期错误数 / 窗口秒数 | 滑动窗口平均错误率 |
| PacketLossRate | (TX-RX-pipeline) / TX | 5 秒滑窗, pipeline 在途不算丢 |
| LateFrameRate | LateDrop / TX | idx 出 8 帧窗 stale, 不计入丢包 |
需 setEnabled(true)。
旧 getMaxJitterUs()("应用抖动")测的是异步通知线程被 PDO 每周期唤醒的间隔抖动。
PDO 过程数据已纯内核化(RT 内核收发 + 内核共享内存指针零拷贝),不存在"用户态
每周期通知"环节,该指标失去意义,已替换为 getMailboxLatencyUs() / getMailboxLatencyAvgUs()。
新语义: 邮箱(CoE/SoE/FoE/EoE/AoE/VoE 等非周期、请求-响应)事务的收发往返时延 ——
从请求发出到响应返回的耗时(最近 1 秒结算)。getMailboxLatencyUs() 为最大值,
getMailboxLatencyAvgUs() 为平均值。
示例:
MasterDiagnosticsInfo diag = master.Diagnostics();
diag.setEnabled(true); // 启用诊断数据采集
// 帧计数
System.out.println("帧频: " + diag.getRTcnt() + " Hz");
System.out.printf("丢包率: %.2f%%%n", diag.getPacketLossRate() * 100);
// 周期与抖动
System.out.println("周期时间: " + diag.getCycleTimeSpan() + " us");
System.out.printf("应用抖动: 平均 %.2f us%n", diag.getAvgJitterUs());
// 邮箱收发延迟
System.out.printf("邮箱延迟: 平均 %.2f us, 最大 %.2f us%n",
diag.getMailboxLatencyAvgUs(), diag.getMailboxLatencyUs());
// PDO 丢帧
MasterDiagnosticsInfo.MasterPDODiagnostics pdo = diag.getPDO();
System.out.printf("PDO 丢帧: 累计=%d, 连续=%d%n",
pdo.getTotalLost(), pdo.getConsecutiveLost());
// 从站异常
System.out.printf("最差从站: #%d (%d%%)%n",
diag.getWorstSlaveIndex(), diag.getWorstLinkQuality());
// 网口状态
System.out.println("主端口: " + (diag.isPrimaryPortOk() ? "正常" : "异常"));
System.out.println("副端口: " + (diag.isSecondaryPortOk() ? "正常" : "未连接"));
// 拓扑信息
System.out.println("拓扑: " + diag.getTopologyDescription());
System.out.println("定时: " + diag.getTimingMode());
每个从站的 ESC 端口错误通过 readSlaveErrorCounters() 获取。详见 从站错误计数器。
WKC(工作计数器)观测
整体 WKC 实测值 / 期望值通过主站属性 master.WKC() / master.ExpectedWKC() 读取,并在诊断快照中以 WkcActual / WkcExpected 字段一次性返回。逐个从站对 WKC 的贡献状态用 slave.WcContributed()(EcWcContributed 枚举),可精确定位到具体掉了哪个从站。
R1 可观测性约定(不可违反):
master.ExpectedWKC()= 配置期 / 进 OP 时确定的固定真值,拓扑固定它就固定,概念上不可变,永不动态下调迁就劣化总线。master.WKC()= 总线实测值,如实反映此刻哪些从站真的在响应,永不篡改。WKC < ExpectedWKC不是 master 故障,而是有映射从站此刻没贡献 WKC(疑似掉站 / 热插拔恢复中);从站恢复后实测值自动回升匹配期望,无需任何重置调用。
主站级 WKC 镜像(薄读零帧,实时)
master.Diagnostics() 上另有一组主站级聚合 WKC 镜像 getter,反映内核 per-slave WcState 诊断缓存的主站级聚合镜像,全部薄读零帧(不缓存、不需刷新——内核每周期、WKC 异常时立即维护,DLL 保证读到即此刻真实总线现实)。配合逐从站的 slave.WcContributed() 可定位到具体掉了哪个从站。
| 类别 | 方法 | 类型 | 读写 | 需启用 | 说明 |
|---|---|---|---|---|---|
| WKC 镜像 | getWkcActualMirror() | int | 只读 | 不可停 | 总线实测工作计数器镜像(actual WKC)— 反映此刻哪些从站真的在响应。R1:如实值永不篡改。0 = 未知(内核共享内存未就绪) |
| getWkcExpectedMirror() | int | 只读 | 不可停 | 期望工作计数器镜像(expected WKC)— 配置期/进 OP 确定的固定真值。R1:永不动态下调迁就劣化总线。0 = 未知 | |
| getWcDeficit() | int | 只读 | 不可停 | WKC 缺额 = expected − actual(>0 表示有映射从站此刻没贡献 WKC,疑似掉站/热插拔,报警依据)。从站恢复后自动归 0。0 = 全在线或未知 | |
| getMappedSlaveCount() | int | 只读 | 不可停 | 已映射(参与 WKC 计算)的从站数。0 = 契约未就绪/未进 OP/内核共享内存未就绪,此时上层应显"未知"而非"掉站" | |
| getWcStateSeq() | long | 只读 | 不可停 | 内核 WcState 缓存序列号(每次刷新自增,uint64)。判断 per-slave 镜像是否在两次读取间更新过;停增=内核未跑 |
早期文档曾标注"Java 未暴露 per-master 聚合 WKC 镜像"。该说明已陈旧——上述 5 个 getter 现已在 master.Diagnostics() 上公开,与 C# / Python / Rust / C / C++ SDK 同口径。旧 DLL 未导出这些 native getter 时,Java 侧诚实降级返回 0(不臆造)。
R1 可观测性约定(不可违反):
getWkcExpectedMirror()= 配置期 / 进 OP 时确定的固定真值,拓扑固定它就固定,概念上不可变,永不动态下调迁就劣化总线。getWkcActualMirror()= 总线实测值,如实反映此刻哪些从站真的在响应,永不篡改。getWcDeficit() = getWkcExpectedMirror() − getWkcActualMirror()。> 0不是 master 故障,而是有映射从站此刻没贡献 WKC(疑似掉站 / 热插拔恢复中);从站恢复后自动归 0,无需任何重置调用。
示例:
MasterDiagnosticsInfo diag = master.Diagnostics();
// 主站级 WKC 镜像(薄读零帧,实时)
System.out.printf("WKC: %d / %d (映射从站 %d 个)%n",
diag.getWkcActualMirror(), diag.getWkcExpectedMirror(), diag.getMappedSlaveCount());
if (diag.getWcDeficit() > 0) {
System.out.printf("⚠️ WKC 缺额 %d — 有从站没在响应,逐个从站定位:%n", diag.getWcDeficit());
for (Slave slave : master.Slaves()) {
if (slave.WcContributed() == EcatSlaveDiag.EcWcContributed.NOT_CONTRIBUTED) {
System.out.printf(" 从站 %d 未贡献 WKC%n", slave.SlaveNum);
}
}
}
long seq = diag.getWcStateSeq(); // 序列号判断镜像是否更新过(无需轮询帧)
// 也可仍用整体 WKC(实测 / 期望)做粗判
System.out.printf("整体 WKC: %d / %d%n", master.WKC(), master.ExpectedWKC());
单个从站对 WKC 的贡献状态请用 slave.WcContributed()(EcWcContributed 枚举)。getWcDeficit() > 0 时遍历从站即可定位掉站点。
热插拔重建
hotSwapRebuild()
public int hotSwapRebuild()
运行中一次性热插拔重建拓扑:任意状态下调用一次,在线重扫总线 + 重建拓扑图 + 重配 PDO + 恢复到运行态(OP)。适用于现场拔掉 / 换上一个模块后,不停总线地把网络重新带回运行态。它不是 Build() 那种"释放全网从头扫"的重建——是面向热插拔的在线增量恢复。
返回值(错误码语义):
| 返回码 | 名称 | 含义与处理 |
|---|---|---|
0 | OK | 成功:从站全部回到 OP |
-20 | BUSY | 另一操作正在进行——稍后重试 |
-21 | RESCAN_0 | 重扫到 0 个从站——已停在 PreOp 安全态,检查物理链路 / 供电 |
-22 | SDO_ABORT | 重配被从站 SDO abort 拒绝——检查 PDO 映射 / Startup 参数 |
-23 | NO_OP | 部分从站未达 OP——已回滚停安全态,查各从站 AL Status Code |
-24 | TIMEOUT | 重建超时 |
-25 | IDX_FULL | 帧索引池饱和 |
-1 | (通用失败) | 调用层异常(Throwable 捕获),不抛异常直接返通用失败码 |
行为 / 约束(R1 如实可观测):
- 在 OP 状态调用会短暂中断 PDO(内部降 PreOp 重配再升回 OP),调用方需容忍这一周期的过程数据空窗。
- 失败时返错误码不掩盖——绝不靠篡改内部计数让结果"看起来对"。
- 即使最终 0 个从站在 OP,PDO 循环也照常运行(周期不掉、帧不停),期望 WKC 不动态下调迁就劣化总线。
示例:
int rc = master.Diagnostics().hotSwapRebuild();
switch (rc) {
case 0:
System.out.println("热插拔重建成功,从站已回到 OP");
break;
case -20:
System.out.println("有操作进行中,稍后重试");
break;
case -21:
System.out.println("重扫到 0 站,已停 PreOp 安全态——检查链路/供电");
break;
case -23:
System.out.println("部分从站未达 OP,已回滚安全态——逐个查 AL Status Code:");
for (Slave slave : master.Slaves()) {
if (slave.State() != EcState.OP) {
System.out.printf(" 从站 %d: AL=0x%04X%n",
slave.SlaveNum, slave.ErrorCode().value);
}
}
break;
default:
System.out.println("热插拔重建失败: " + rc);
break;
}
DC 同步
自动监控(ETG.1500 5.13.3),每秒检查各从站时间偏差。超出同步窗口阈值时触发 DCSyncLost 事件。
通过 getSyncWindowThreshold() / setSyncWindowThreshold(int thresholdNs) 读写同步窗口阈值(纳秒),默认 1000ns。
示例:
MasterDiagnosticsInfo diag = master.Diagnostics();
diag.setSyncWindowThreshold(500); // 500ns 阈值
单个从站的同步状态请使用从站级 DC 诊断方法。
冗余状态
getRingMode() 反映环拓扑冗余的运行状态("无冗余" / "双向冗余" / "降级(仅primary)")。
getBreakPoint() / getAllBreakPoints() 统一检测两类物理故障:
| 类型 | faultType | 检测方式 | 典型场景 |
|---|---|---|---|
| 断线 | 0 | DL Status 端口物理链路丢失 | 拔线、线缆断裂 |
| CRC 故障 | 1 | 端口级 RxError + InvalidFrame 持续增长 | 接触不良、线缆老化、连接器氧化 |
故障线缆段定位: 当相邻从站的对向端口(如从站 N 的 P1 和从站 N+1 的 P0)同时报故障,说明连接线缆有问题。仅单侧报故障则定位到该端口连接器。
BreakPointInfo 结构:
public static class BreakPointInfo {
public final int slaveIndex; // 故障从站索引 (1-based)
public final int port; // 故障端口号 (0-3, 对应 P0-P3)
public final int faultType; // 故障类型: 0=断线, 1=CRC故障
public boolean isLinkDown(); // 是否为断线故障
public boolean isCrcFault(); // 是否为 CRC 故障
}
单个从站的冗余状态请参考 从站诊断 - 冗余诊断。
示例:
MasterDiagnosticsInfo diag = master.Diagnostics();
// 环拓扑冗余模式
System.out.println("冗余模式: " + diag.getRingMode());
if (diag.isRedundancyActive()) {
System.out.println("冗余已激活");
}
MasterDiagnosticsInfo.BreakPointInfo bp = diag.getBreakPoint();
if (bp != null) {
System.out.println("故障: " + bp);
if (bp.isCrcFault()) {
System.out.println("建议检查线缆/连接器");
}
}
// 获取所有故障点
for (MasterDiagnosticsInfo.BreakPointInfo b : diag.getAllBreakPoints()) {
System.out.println("故障点: " + b);
}
诊断快照
GetSnapshot()
public DiagnosticsSnapshot GetSnapshot()
一次调用获取所有诊断数据的一致快照,避免多次属性访问导致的数据不一致和性能开销。适合 UI 刷新和日志记录场景。
返回值:
DiagnosticsSnapshot— 诊断数据快照对象
相关结构:
public static class DiagnosticsSnapshot {
public final int Frequency; // 应用频率 (Hz)
public final long ErrorCount; // 每秒错误数
public final float PacketLossRate; // 丢包率 (0.0~1.0) — TX vs RX 5s 滑窗
public final float LateFrameRate; // 过慢帧率 (0.0~1.0)
public final double AvgJitterUs; // 平均抖动 (微秒)
public final double MailboxLatencyUs; // 邮箱收发延迟 - 最大 (微秒)
public final double MailboxLatencyAvgUs; // 邮箱收发延迟 - 平均 (微秒)
public final int CycleTimeUs; // 实际周期时间 (微秒)
public final short WkcActual; // WKC 实际值
public final short WkcExpected; // WKC 期望值
public final long BusCycleHz; // 总线频率 (Hz)
public final double BusMaxJitterUs; // 总线最大抖动 (微秒)
public final double BusAvgJitterUs; // 总线平均抖动 (微秒)
public final double BusRoundtripUs; // 报文往返延迟 (微秒)
public final double BusLoadPercent; // 通讯负载 (%) — RTT/周期×100
public final long SmiCount; // SMI 累计次数
public final double SmiPeakUs; // SMI 峰值抖动 (微秒)
public final boolean PrimaryPortOk; // 主端口是否正常
public final boolean SecondaryPortOk; // 副端口是否正常
public final boolean RedundancyActive; // 冗余是否激活
}
示例:
MasterDiagnosticsInfo diag = master.Diagnostics();
diag.setEnabled(true);
MasterDiagnosticsInfo.DiagnosticsSnapshot snap = diag.GetSnapshot();
System.out.printf("频率: %d Hz, 丢包: %.2f%%, 抖动: %.2f us%n",
snap.Frequency, snap.PacketLossRate * 100, snap.AvgJitterUs);
System.out.printf("WKC: %d/%d, 总线: %d Hz%n",
snap.WkcActual, snap.WkcExpected, snap.BusCycleHz);
if (snap.RedundancyActive) {
System.out.println("冗余已激活");
}
诊断控制
标记为"是"的属性需先设置 setEnabled(true) 启动周期性采样,标记为"不可停"的功能始终活跃。
reset (诊断计数器)
public void reset()
一次性重置所有诊断统计,包括:
- 基础诊断统计(帧错误等)
- PDO 丢帧统计(所有组)
- DC 同步窗口统计
- 所有从站的端口错误计数器
只清空主站层面的统计数据, 不影响 FSoE 安全连接状态机(slave.FSoE.Reset)。
示例:
MasterDiagnosticsInfo diag = master.Diagnostics();
diag.setEnabled(true); // 启用采集
// ... 运行一段时间 ...
diag.reset(); // 重置统计
AL 错误分类
对 AL Status Code 进行分类,帮助快速判断错误性质和处理策略。
EtherCATTypes.classifyALError
public static EtherCATTypes.ALErrorCategory classifyALError(int errorCode)
对 AL Status Code(从站返回的错误码)进行分类,帮助快速判断错误性质和处理策略。
参数:
errorCode(int) — AL Status Code,从slave.ErrorCode()或状态转换失败时获取
返回值:
ALErrorCategory— 错误分类枚举
相关结构:
public enum ALErrorCategory {
NONE, // 无错误
TRANSIENT, // 瞬态错误,可重试状态转换,通常自动恢复
CONFIGURATION, // 配置错误,检查 PDO 映射、SM 配置、Startup 参数等
HARDWARE, // 硬件错误,检查从站硬件、线缆、电源
UNKNOWN // 未知错误,查阅 ETG.1000 或从站手册
}
示例:
Slave slave = master.getSlave(1);
EtherCATTypes.EcALState errorCode = slave.ErrorCode();
if (errorCode != EtherCATTypes.EcALState.NO_ERROR) {
EtherCATTypes.ALErrorCategory category =
EtherCATTypes.classifyALError(errorCode.value);
System.out.printf("从站 1 错误 %s (0x%04X): %s%n",
errorCode, errorCode.value, category);
switch (category) {
case TRANSIENT:
System.out.println("瞬态错误,尝试重新切换状态...");
break;
case CONFIGURATION:
System.out.println("配置错误,请检查 PDO/SM 配置");
break;
case HARDWARE:
System.out.println("硬件错误,请检查从站设备");
break;
default:
break;
}
}
0x001E无效输入映射 — Configuration0x001D无效输出映射 — Configuration0x0011无效邮箱配置 — Configuration0x002D同步错误 — Transient0x0032DC 同步超时 — Transient0x0050EEPROM 错误 — Hardware
从站错误计数器
readSlaveErrorCounters(int slaveIndex)
public EtherCATTypes.SlaveErrorCounters readSlaveErrorCounters(int slaveIndex)
读取指定从站的错误计数器汇总(CRC 错误、帧错误、丢帧统计)。
参数:
slaveIndex(int) — 从站索引(1-based)
返回值:
SlaveErrorCounters— 错误计数器汇总
相关结构:
public static class SlaveErrorCounters {
public int slaveIndex; // 从站编号
public long port0CrcErrors; // 端口 0 CRC 错误
public long port1CrcErrors; // 端口 1 CRC 错误
public long port2CrcErrors; // 端口 2 CRC 错误
public long port3CrcErrors; // 端口 3 CRC 错误
public long frameErrors; // 帧错误计数
public long lostFrames; // 丢帧计数
}
示例:
EtherCATTypes.SlaveErrorCounters counters = master.Diagnostics().readSlaveErrorCounters(1);
if (counters.port0CrcErrors + counters.frameErrors + counters.lostFrames > 0) {
System.out.println("从站 1 错误计数:");
System.out.printf(" CRC 错误: P0=%d, P1=%d, P2=%d, P3=%d%n",
counters.port0CrcErrors, counters.port1CrcErrors,
counters.port2CrcErrors, counters.port3CrcErrors);
System.out.println(" 帧错误: " + counters.frameErrors);
System.out.println(" 丢帧: " + counters.lostFrames);
}
诊断消息
slave.CoE().readDiagnosticMessages()
public List<DiagnosticMessage> readDiagnosticMessages()
通过 CoE 读取从站对象 0x10F3(诊断历史对象,ETG.1020)中的诊断消息。返回从站记录的诊断事件列表,包含时间戳、错误码和描述信息。
访问路径: slave.CoE().readDiagnosticMessages()
返回值:
List<DiagnosticMessage>— 诊断消息列表,无消息时返回空列表
相关结构:
public static class DiagnosticMessage {
public final byte SubIndex; // 子索引
public final int DiagCode; // 诊断码
public final short Flags; // 标志位
public final short TextIndex; // 文本索引
public final byte[] RawData; // 原始数据
}
示例:
for (Slave slave : master.Slaves()) {
// slave.CoE() 子对象延迟创建, 始终返回实例 (不会为 null)
CoE coe = slave.CoE();
List<CoE.DiagnosticMessage> messages = coe.readDiagnosticMessages();
if (messages.isEmpty()) continue;
for (CoE.DiagnosticMessage msg : messages) {
System.out.printf("[从站 %d] 代码=0x%08X, %s%n",
slave.SlaveNum, msg.DiagCode, msg);
}
}
并非所有从站都支持 0x10F3 诊断历史对象。不支持的从站调用时返回空列表。此方法通过 SDO 读取,不建议在实时路径中高频调用。
按问题分块的诊断字段指南
诊断接口统一从 master.Diagnostics()(返回 MasterDiagnosticsInfo)访问。下表把现场症状映射到对应的真实 getter:
| 现场症状 | 字段 (Java getter / 访问路径) |
|---|---|
| 是不是丢包? | getPacketLossRate() + getLateFrameRate() |
| PDO 是否连续丢帧? | getPDO().getConsecutiveLost() (≥5 = 持续) + getPDO().getTotalLost() |
| 周期抖动大不大? | getAvgJitterUs() / getBusMaxJitterUs() / getBusAvgJitterUs() |
| 邮箱事务慢不慢? | getMailboxLatencyUs() (最大) / getMailboxLatencyAvgUs() (平均) |
| 哪个从站不稳定? | getWorstSlaveIndex() + getWorstLinkQuality() |
| 某从站 CRC / 帧错误? | readSlaveErrorCounters(i) → port0CrcErrors / frameErrors / lostFrames |
| 哪根网线断了 / CRC 劣化? | getBreakPoint() / getAllBreakPoints() |
| 主副口是否正常? | isPrimaryPortOk() / isSecondaryPortOk() + getPrimaryPortErrors() / getSecondaryPortErrors() |
| 冗余真在工作? | isRedundancyActive() + getRingMode() |
| RT 内核稳定? | getTimingMode()("硬件定时器" / "RT就绪" / "降级" / "RT错误")+ getSmiCount() / getSmiPeakUs() |
1. 帧 / 丢包
- getPacketLossRate(): 最近 5 秒丢包率(TX vs RX 滑窗)
- getLateFrameRate(): 过慢帧率(idx 出 8 帧窗 stale,不计入丢包)
- getPDO().getConsecutiveLost(): 当前连续丢帧数(区分偶发 / 持续,核心指标)
- getPDO().getTotalLost(): 累计丢帧数(所有组合计)
- getErrorCnt(): 每秒错误数(5 秒平均)
2. 端口收发
- isPrimaryPortOk() / isSecondaryPortOk(): 主 / 副端口是否正常
- getPrimaryPortErrors() / getSecondaryPortErrors(): 主 / 副端口最近 5 秒错误数
3. per-slave 物理层 (ESC 寄存器)
通过 master.Diagnostics().readSlaveErrorCounters(slaveIndex) 返回 SlaveErrorCounters(见上文「从站错误计数器」),含:
- port0CrcErrors ~ port3CrcErrors: 各端口 CRC 错误数(ESC 0x0300/0x0304)
- frameErrors: 帧错误计数
- lostFrames: 丢帧计数
每个从站的端口错误 / 链路质量也可通过 slave.Diagnostics().ReadPortErrors() / slave.Diagnostics().LinkQuality() 读取,详见 从站诊断。
4. 总线实时性能
- getBusCycleHz(): 总线频率(Hz)
- getBusMaxJitterUs() / getBusAvgJitterUs(): 总线最大 / 平均抖动(微秒)
- getBusRoundtripUs(): 报文往返延迟(微秒)
- getBusLoadPercent(): 通讯负载(%)
5. 内核稳定性
- getTimingMode(): 定时模式描述(WDK 驱动必须就绪)
- getSmiCount() / getSmiPeakUs(): SMI 累计次数与峰值抖动(微秒)
完整诊断流程示例
MasterDiagnosticsInfo diag = master.Diagnostics();
diag.setEnabled(true);
// 1. PDO 持续 vs 偶发
if (diag.getPDO().getConsecutiveLost() >= 5) {
System.out.println("持续 PDO 丢帧");
}
// 2. 丢包率
if (diag.getPacketLossRate() > 0.01f) {
System.out.printf("丢包率 %.2f%% 严重%n", diag.getPacketLossRate() * 100);
}
// 3. 故障定位
MasterDiagnosticsInfo.BreakPointInfo bp = diag.getBreakPoint();
if (bp != null) {
System.out.printf("故障点: 从站 %d P%d (%s)%n",
bp.slaveIndex, bp.port, bp.isCrcFault() ? "CRC劣化" : "断线");
}
// 4. 最不稳定从站
int worst = diag.getWorstSlaveIndex();
if (worst > 0) {
System.out.printf("最不稳定从站: #%d (健康度 %d%%)%n",
worst, diag.getWorstLinkQuality());
}
// 5. RT 内核定时模式
System.out.println("定时模式: " + diag.getTimingMode());