从站诊断
每个从站提供独立的诊断属性和子对象,可查询端口错误、冗余、DC 同步等信息。
从站状态(State、ErrorCode、IsLost)等基础属性请参考 属性与方法。
建议通过 事件 驱动异常处理(如 SlaveStateChanged、SlaveOffline、DCSyncLost),而非轮询。
直接读取从站诊断属性适用于 UI 显示等场景。
全局通信统计、丢包率、PDO 丢帧汇总请参考 主站诊断。
功能概览
| 功能 | 访问路径 | 说明 |
|---|---|---|
| WKC 与健康镜像 | slave.* | per-slave WcState/AL/邮箱健康内核镜像(薄读零帧,实时) |
| 通信诊断 | slave.Diagnostics.ReadPortErrors() | ESC 端口错误计数器 |
| 冗余诊断 | slave.Diagnostics.* | 冗余激活、主/冗余线路断路检测 |
| DC 同步 | slave.Diagnostics.DC.* | 同步状态、时间差 |
| 类别 | 属性 | 类型 | 访问 | 使能控制 | 说明 |
|---|---|---|---|---|---|
| WKC 与健康镜像 | WcContributed | WcContribution | 只读 | 不可停 | 该从站对工作计数器(WKC)的贡献状态——内核 per-slave WcState 诊断缓存镜像的薄读(零帧实时)。NotContributed ≠ master 故障,是该从站此刻没在响应(疑似掉站/热插拔恢复中) |
AlStatusMirror | EcSlaveStatus | 只读 | 不可停 | 该从站 AL 状态镜像(内核诊断缓存薄读,零帧实时)。低字节 = AL State,含 Error 位时可 .HasFault() | |
AlStatusCode | EcALState | 只读 | 不可停 | 该从站当前 AL Status Code(内核 AL 镜像高字节解码)。仅当 AlStatusMirror 含 Error 位时有意义,无 Error 返回 NoError。薄读零帧,实时 | |
MailboxHealth | EcMailboxHealth | 只读 | 不可停 | 该从站邮箱健康度(内核每秒更新的镜像薄读)。Degraded 表示在 OP 但邮箱半失效(CoE/SDO 可能阻塞),如实反映不参与 WKC 篡改 | |
IsFreeRunDemoted | bool | 只读 | 不可停 | 该从站是否被迫降级到 FreeRun 同步模式(薄读零帧)。true 仅当“配置期要 DC 但运行中被迫退到 FreeRun”——半失效根源观测点 | |
HealthDegradedCount | uint | 只读 | 不可停 | 邮箱半失效连续累计计数(≈秒数,内核每秒评估)。0 = 健康或未评估;持续累加表示半失效未恢复,内核在 >=3 时触发自动修复 | |
RecoverMailboxHealth() | bool | 只读 | 不可停 | 手动触发邮箱半失效修复(内核自愈流程)。通常无需手动调用——内核 >=3 秒后自动修复。通讯异常时返回 false 不抛异常 | |
| 通信诊断 | ReadPortErrors() | EscPortErrors? | 只读 | 读取 ESC 端口错误计数器。实时读取从站寄存器,失败返回 null | |
| 冗余诊断 | RedundancyActivated | bool | 只读 | 不可停 | 冗余被激活。从站未丢失,但网络中存在断线(物理链路丢失),冗余机制正在工作。CRC 故障不触发此标志 |
PrimaryLinkBroken | bool | 只读 | 不可停 | 主线路断路。从主端口 (Port0) 到该从站的路径上存在断线。仅检测物理断线,不含 CRC 故障 | |
SecondaryLinkBroken | bool | 只读 | 不可停 | 冗余线路断路。从副端口 (Port1) 反向到该从站的路径上存在断线。仅检测物理断线,不含 CRC 故障 | |
| DC 同步 | DC.IsInSync | bool | 只读 | 不可停 | 是否在同步窗口内(阈值由 master.Diagnostics.SyncWindowThreshold 控制) |
DC.SyncTimeDifference | int | 只读 | 不可停 | 当前与参考时钟的时间差(纳秒) |
EscPortErrors 结构体
| 字段 | 类型 | 说明 |
|---|---|---|
| RxError | byte[] | 各端口 RX 错误计数 [Port0-3] |
| InvalidFrame | byte[] | 各端口无效帧计数 [Port0-3] |
| LostLink | byte[] | 各端口链路丢失计数 [Port0-3] |
| HasErrors | bool | 是否存在任何错误(只读派生) |
WKC/健康镜像、冗余和 DC 属性为"不可停"(始终活跃),无需启用 master.Diagnostics.Enabled。
ReadPortErrors() 需实时读取从站,不建议高频调用。
WKC 与健康镜像
这些属性直接在 slave 上访问(不在 slave.Diagnostics 子对象下),全部为内核 per-slave 诊断缓存的薄读零帧——内核每周期(WKC 异常时立即)维护缓存,读到即此刻真实总线现实,无需调任何刷新。配合主站级 master.Diagnostics.WcDeficit 可在掉站时定位到具体从站。
WcContributed(从站 WKC 贡献)
public WcContribution WcContributed { get; }
该从站对工作计数器(WKC)的贡献状态。
R1 可观测性约定: NotContributed 不是 master 故障,而是该从站此刻没在响应(疑似掉站 / 热插拔恢复中);修复后自动回 Contributed。内核未填充时诚实返回 Unknown(0xFF),不臆造。
WcContribution 枚举
| 值 | 名称 | 说明 |
|---|---|---|
| 0x00 | NotContributed | 该从站此刻没在贡献 WKC(疑似掉站 / 热插拔恢复中) |
| 0x01 | Contributed | 该从站正常贡献 WKC(在总线上响应) |
| 0xFF | Unknown | 内核镜像未填充(尚未运行 PDO 循环 / 索引越界)— 诚实暴露,不臆造 |
AlStatusMirror / AlStatusCode
public EcSlaveStatus AlStatusMirror { get; }
public EcALState AlStatusCode { get; }
AlStatusMirror 为该从站 AL 状态镜像(低字节 = AL State,bit4 = Error 位,可 .HasFault())。当 Error 位置位时,AlStatusCode 给出 EcALState 错误码;无 Error 时返回 EcALState.NoError。
MailboxHealth / IsFreeRunDemoted / HealthDegradedCount / RecoverMailboxHealth()
让"在 OP 但邮箱半失效"(CoE/SDO 阻塞)这种隐性故障对上层可见。MailboxHealth == Degraded 表示从站仍在 OP 但邮箱通道半失效。HealthDegradedCount 持续累加表示半失效未恢复(内核 >=3 秒自动修复),也可 RecoverMailboxHealth() 手动介入。R1:全部如实反映,不参与任何 WKC 篡改。
EcMailboxHealth 枚举
| 值 | 名称 | 说明 |
|---|---|---|
| 0 | Unknown | 未知 / 无邮箱 / 不在 OP — 邮箱健康度此时无意义 |
| 1 | Healthy | 健康 — 在 OP 且邮箱可用 |
| 2 | Degraded | 降级 — 在 OP 但邮箱半失效(handler 丢失 / DC 降级伴 0x001F) |
示例:
foreach (var slave in master.Slaves)
{
// WKC 贡献:掉站定位
if (slave.WcContributed == WcContribution.NotContributed)
Console.WriteLine($"从站 {slave.SlaveNum} 未贡献 WKC, AL={slave.AlStatusCode}");
// 邮箱半失效(在 OP 但 CoE 可能阻塞)
if (slave.MailboxHealth == EcMailboxHealth.Degraded)
{
Console.WriteLine($"从站 {slave.SlaveNum} 邮箱半失效 {slave.HealthDegradedCount}s" +
(slave.IsFreeRunDemoted ? " (被迫 FreeRun)" : ""));
if (slave.HealthDegradedCount >= 5)
slave.RecoverMailboxHealth(); // 通常内核已自动修复,此为手动介入
}
}
通信诊断
ReadPortErrors()
public EscPortErrors? ReadPortErrors()
读取从站 ESC 端口错误计数器,返回各端口的物理层错误统计。
返回值:
EscPortErrors?— 端口错误计数器,读取失败返回null
示例:
foreach (var slave in master.Slaves)
{
var errors = slave.Diagnostics.ReadPortErrors();
if (errors?.HasErrors == true)
{
var e = errors.Value;
Console.WriteLine($"从站 {slave.SlaveNum}: " +
$"RX错误=[{string.Join(",", e.RxError)}], " +
$"无效帧=[{string.Join(",", e.InvalidFrame)}], " +
$"链路丢失=[{string.Join(",", e.LostLink)}]");
}
}
冗余诊断
通过 slave.Diagnostics 访问从站的冗余状态。仅在主站启用冗余时有意义。
全局冗余状态和断线点请参考 主站诊断 - 冗余状态。
示例:
foreach (var slave in master.Slaves)
{
if (!slave.Diagnostics.RedundancyActivated) continue;
Console.Write($"从站 {slave.SlaveNum}: 冗余激活");
if (slave.Diagnostics.PrimaryLinkBroken)
Console.Write(", 主线路断路");
if (slave.Diagnostics.SecondaryLinkBroken)
Console.Write(", 冗余线路断路");
Console.WriteLine();
}
DC 同步
通过 slave.Diagnostics.DC 访问从站的 DC 同步状态。
全局阈值和事件请参考 主站诊断 - DC 同步。
示例:
foreach (var slave in master.Slaves)
{
if (!slave.HasDC) continue;
var dc = slave.Diagnostics.DC;
Console.WriteLine($"从站 {slave.SlaveNum}: " +
$"时间差={dc.SyncTimeDifference}ns, " +
$"同步={dc.IsInSync}");
}
完整示例
foreach (var slave in master.Slaves)
{
Console.WriteLine($"--- 从站 {slave.SlaveNum}: {slave.Name} ---");
// 端口错误
var errors = slave.Diagnostics.ReadPortErrors();
if (errors?.HasErrors == true)
{
var e = errors.Value;
Console.WriteLine($" 端口错误: RX=[{string.Join(",", e.RxError)}], " +
$"无效帧=[{string.Join(",", e.InvalidFrame)}], " +
$"链路丢失=[{string.Join(",", e.LostLink)}]");
}
// 冗余(仅冗余模式下有意义)
if (slave.Diagnostics.RedundancyActivated)
{
Console.WriteLine($" 冗余激活: 主线路断={slave.Diagnostics.PrimaryLinkBroken}, " +
$"冗余线路断={slave.Diagnostics.SecondaryLinkBroken}");
}
// DC 同步
if (slave.HasDC)
{
Console.WriteLine($" DC: 时间差={slave.Diagnostics.DC.SyncTimeDifference}ns, " +
$"同步={slave.Diagnostics.DC.IsInSync}");
}
}