跳到主要内容

从站诊断

每个从站提供独立的诊断属性和子对象,可查询端口错误、冗余、DC 同步等信息。

从站基础属性

从站状态(State()、ErrorCode()、IsLost())等基础属性请参考 属性与方法。

配合事件使用

建议通过 事件 驱动异常处理(如 addSlaveStateChangedListener、addSlaveOfflineListener、addDCSyncLostListener),而非轮询。 直接读取从站诊断属性适用于 UI 显示等场景。

主站级诊断

全局通信统计、丢包率、PDO 丢帧汇总请参考 主站诊断。

功能概览​

类别功能读写访问路径说明
实时状态镜像实时状态镜像只读slave.WcContributed() / MailboxHealth() 等per-slave WcState/AL/邮箱健康内核镜像(薄读零帧,实时)
通信诊断通信诊断只读slave.Diagnostics().ReadPortErrors()ESC 端口错误计数器
冗余诊断冗余诊断只读slave.RedundancyActivated() 等冗余激活、主/冗余线路断路检测
DC 同步DC 同步只读slave.DC().GetSyncWindowStatus()同步状态、时间差

属性​

类别方法类型读写说明
通信诊断ReadPortErrors()EscPortErrors只读读取 ESC 端口错误计数器
ResetPortErrors()boolean只写重置端口错误计数器
LinkQuality()short只读获取链路质量(0-100%)
冗余诊断RedundancyActivated()boolean只读冗余被激活。从站未丢失但网络中存在断线,冗余机制正在工作
PrimaryLinkBroken()boolean只读主线路断路。从主端口(Port0)到该从站的路径上存在断线
SecondaryLinkBroken()boolean只读冗余线路断路。从副端口(Port1)反向到该从站的路径上存在断线
DC 同步DC().GetSyncWindowStatus()SyncWindowStatus只读同步窗口状态(时间差、是否同步等)

EscPortErrors 结构:

字段类型读写说明
RxErrorbyte[4]只读各端口 RX 错误计数 [Port0-3]
InvalidFramebyte[4]只读各端口无效帧计数 [Port0-3]
LostLinkbyte[4]只读各端口链路丢失计数 [Port0-3]
FwdRxErrorbyte[4]只读各端口转发 RX 错误计数 [Port0-3]
HasErrors()boolean只读是否存在任何错误
备注

冗余和 DC 属性始终活跃,无需启用 master.Diagnostics().setEnabled(true)。 ReadPortErrors() 需实时读取从站,不建议高频调用。

实时状态镜像​

2.5.x 起,从站提供零帧实时状态镜像访问 — 内核每 PDO 周期(WKC 异常立即)维护 per-slave 诊断缓存,SDK 经 native getter 薄读,不发额外帧、属性本身实时。适合 UI 高频刷新和掉站检测。

类别方法类型读写说明
WKC 贡献WcContributed()EcWcContributed只读本周期该从站是否贡献了应有 WKC(内核 per-slave 位)
AL 状态镜像AlStatusMirror()EcAlStatusMirror只读从站 AL 状态镜像(内核 round-robin 填的 16-bit raw,含 AL State + Error + AL Status Code)
邮箱健康MailboxHealth()EcMailboxHealth只读该从站邮箱健康度(内核每秒更新的镜像薄读)。DEGRADED 表示在 OP 但邮箱半失效(CoE/SDO 可能阻塞),如实反映不参与 WKC 篡改
邮箱健康IsFreeRunDemoted()boolean只读该从站是否被迫降级到 FreeRun 同步模式(薄读零帧)。true 仅当"配置期要 DC 但运行中被迫退到 FreeRun"——半失效根源观测点
邮箱健康HealthDegradedCount()long只读邮箱半失效连续累计计数(≈秒数,unsigned uint32 已 & 0xFFFFFFFFL 还原)。0 = 健康或未评估;持续累加表示半失效未恢复,内核在 >=3 时触发自动修复
邮箱健康RecoverMailboxHealth()boolean只读手动触发邮箱半失效修复。通常无需手动调用——内核检测到半失效持续若干秒后会自动修复。通讯异常时返回 false 不抛异常
掉站标志IsLost()boolean只读从站是否丢失

EcWcContributed 枚举​

public enum EcWcContributed {
CONTRIBUTED(1), // 本周期该从站贡献了应有 WKC(在线响应正常)
NOT_CONTRIBUTED(0), // 本周期没贡献 WKC(疑似掉站 / 异常)
UNKNOWN(0xFF); // 未知 — 契约未就绪 / 内核共享内存未就绪(诚实哨兵,不臆造在线或掉站)
}
  • isKnown() — 数据是否可信(非 UNKNOWN)。UI 据此决定显示真值还是"未知"。

EcAlStatusMirror 结构​

AlStatusMirror() 返回的解析对象,把内核 16-bit raw 镜像拆为枚举:

  • getRaw() (int) — 原始 16-bit 镜像值(unsigned,0..65535)
  • getAlState() (EcState) — AL State 基础枚举(不含 Error 标志)
  • hasError() (boolean) — 是否处于错误状态(bit4 置位)
  • getAlStatusCode() (EcALState) — AL Status Code 枚举(仅 Error 置位时有效,否则 NO_ERROR)
  • isKnown() (boolean) — 数据是否可信(raw == 0 表示内核未提供,UI 应显"未知")

MailboxHealth() / IsFreeRunDemoted() / HealthDegradedCount() / RecoverMailboxHealth()​

让"在 OP 但邮箱半失效"(CoE/SDO 阻塞)这种隐性故障对上层可见。MailboxHealth() == DEGRADED 表示从站仍在 OP 但邮箱通道半失效。HealthDegradedCount() 持续累加表示半失效未恢复(内核 >=3 秒自动修复),也可 RecoverMailboxHealth() 手动介入。R1:全部如实反映,不参与任何 WKC 篡改。

EcMailboxHealth 枚举​

public enum EcMailboxHealth {
UNKNOWN(0), // 未知 — 无邮箱 / 不在 OP / 契约未就绪(诚实哨兵)
HEALTHY(1), // 健康 — 在 OP 且邮箱可用
DEGRADED(2); // 降级 — 在 OP 但邮箱半失效(handler 丢失 / DC 降级伴 0x001F)
}
  • isKnown() — 数据是否可信(非 UNKNOWN)。UI 据此决定显示真值还是"未知"。

示例:

for (Slave slave : master.Slaves()) {
// 邮箱半失效(在 OP 但 CoE 可能阻塞)
if (slave.MailboxHealth() == EcatSlaveDiag.EcMailboxHealth.DEGRADED) {
System.out.printf("从站 %d 邮箱半失效 %ds%s%n",
slave.SlaveNum, slave.HealthDegradedCount(),
slave.IsFreeRunDemoted() ? "(被迫 FreeRun)" : "");
if (slave.HealthDegradedCount() >= 5) {
slave.RecoverMailboxHealth(); // 通常内核已自动修复,此为手动介入
}
}
}
诚实哨兵

WcContributed()、AlStatusMirror() 与 MailboxHealth() 在内核契约未就绪 / 无共享内存时诚实返回 UNKNOWN / 未知,绝不臆造在线或掉站状态。

示例:

for (Slave slave : master.Slaves()) {
EcatSlaveDiag.EcWcContributed wc = slave.WcContributed();
if (wc == EcatSlaveDiag.EcWcContributed.NOT_CONTRIBUTED) {
System.out.printf("从站 %d 本周期未贡献 WKC,疑似掉站%n", slave.SlaveNum);
}

EcatSlaveDiag.EcAlStatusMirror al = slave.AlStatusMirror();
if (al.isKnown() && al.hasError()) {
System.out.printf("从站 %d AL 错误: %s, Code=%s%n",
slave.SlaveNum, al.getAlState(), al.getAlStatusCode());
}
}

通信诊断​

ReadPortErrors()​

public EscPortErrors ReadPortErrors()

读取从站 ESC 端口错误计数器,返回各端口的物理层错误统计。

返回值:

  • EscPortErrors -- 端口错误计数器,读取失败返回 null

示例:

for (Slave slave : master.Slaves()) {
EscPortErrors errors = slave.Diagnostics().ReadPortErrors();
if (errors != null && errors.HasErrors()) {
System.out.printf("从站 %d: RX错误=%s, 无效帧=%s, 链路丢失=%s%n",
slave.SlaveNum,
Arrays.toString(errors.RxError),
Arrays.toString(errors.InvalidFrame),
Arrays.toString(errors.LostLink));
}
}

拓扑信息​

通过 slave.Topology() 访问从站级 SlaveTopology;通过 master.SlaveTopology() 访问主站级拓扑。

类别方法类型读写说明
拓扑信息Count()int只读主站级根节点数(入口端口类型为 MII,或 ParentStation==0 直连主站)。EBUS 模块挂在耦合器下不算根。从站级拓扑无主站列表,返回 0。不是 master.SlaveCount()(全网从站数)
EntryPort()byte只读入口端口
ParentPort()byte只读父端口号
ParentStation()short只读父站地址(注: 实际是从站索引 1-based,不是 station address;0 表示父节点是主站)
PhysicalType()byte只读物理类型
TopologyByte()byte只读拓扑字节
ActivePorts()byte只读活动端口
ConsumedPorts()byte只读已使用端口数

冗余诊断​

直接通过 Slave 属性访问从站的冗余状态。仅在主站启用冗余时有意义。

主站级监控

全局冗余状态和断线点请参考 主站诊断 - 冗余状态。

示例:

for (Slave slave : master.Slaves()) {
if (!slave.RedundancyActivated()) continue;

System.out.print("从站 " + slave.SlaveNum + ": 冗余激活");
if (slave.PrimaryLinkBroken()) System.out.print(", 主线路断路");
if (slave.SecondaryLinkBroken()) System.out.print(", 冗余线路断路");
System.out.println();
}

DC 同步​

通过 slave.DC().GetSyncWindowStatus() 访问从站的 DC 同步状态。

主站级监控

全局阈值和事件请参考 主站诊断 - DC 同步。

SyncWindowStatus 结构:

字段类型读写说明
DiffNsint只读当前时间差(纳秒)
MaxDiffNsint只读最大时间差(纳秒)
MinDiffNsint只读最小时间差(纳秒)
InSyncboolean只读是否在同步窗口内
OutOfSyncCountint只读超出同步窗口次数

示例:

for (Slave slave : master.Slaves()) {
if (!slave.HasDC()) continue;

DC.SyncWindowStatus status = slave.DC().GetSyncWindowStatus();
if (status != null) {
System.out.printf("从站 %d: 时间差=%dns, 同步=%b%n",
slave.SlaveNum, status.DiffNs, status.InSync);
}
}

完整示例​

for (Slave slave : master.Slaves()) {
System.out.println("--- 从站 " + slave.SlaveNum + ": " + slave.Name() + " ---");

// 端口错误
EscPortErrors errors = slave.Diagnostics().ReadPortErrors();
if (errors != null && errors.HasErrors()) {
System.out.printf(" 端口错误: RX=%s, 无效帧=%s, 链路丢失=%s%n",
Arrays.toString(errors.RxError),
Arrays.toString(errors.InvalidFrame),
Arrays.toString(errors.LostLink));
}

// 冗余(仅冗余模式下有意义)
if (slave.RedundancyActivated()) {
System.out.printf(" 冗余激活: 主线路断=%b, 冗余线路断=%b%n",
slave.PrimaryLinkBroken(), slave.SecondaryLinkBroken());
}

// DC 同步
if (slave.HasDC()) {
DC.SyncWindowStatus status = slave.DC().GetSyncWindowStatus();
if (status != null) {
System.out.printf(" DC: 时间差=%dns, 同步=%b%n",
status.DiffNs, status.InSync);
}
}
}