主站诊断
通过 master.Diagnostics 访问所有诊断、监控、统计功能。
建议通过 事件 驱动异常处理,而非自行轮询。
直接读取诊断属性适用于 UI 显示等场景。
单个从站的状态诊断、链路质量请参考 从站诊断。
功能概览
| 功能 | 说明 |
|---|---|
| 通信与性能统计 | 帧计数、丢包、抖动、PDO 丢帧、网口状态、拓扑 |
| DC 同步 | 同步窗口阈值、DCSyncLost 事件 |
| 冗余状态 | 冗余激活、故障点检测(断线 + CRC 故障定位) |
| 诊断控制 | 启停数据采集、重置统计 |
通信与性能统计
| 类别 | 属性 | 类型 | 访问 | 使能控制 | 说明 |
|---|---|---|---|---|---|
| 帧计数 | RTcnt | int | 只读 | 是 | 每秒帧数(Hz),5秒平均 |
ErrorCnt | uint | 只读 | 是 | 每秒错误数,5秒平均 | |
PacketLossRate | float | 只读 | 是 | 丢包率(0.0~1.0)— TX vs RX 5 秒滑窗,pipeline 在途不算丢 | |
LateFrameRate | float | 只读 | 是 | 过慢帧率(0.0~1.0)— idx 出 8 帧窗 stale, 不计入丢包 | |
| 周期与抖动 | CycleTimeSpan | int | 只读 | 是 | 实际周期时间(微秒),实时值 |
AvgJitterUs | double | 只读 | 是 | 最近5秒平均抖动(微秒),总线抖动 | |
| 邮箱收发延迟 | MailboxLatencyUs | double | 只读 | 是 | 邮箱收发延迟 - 最大(微秒)。邮箱(CoE/SoE/FoE/EoE/AoE/VoE 等非周期、请求-响应)事务从请求发出到响应返回的最大往返耗时(最近1秒结算) |
MailboxLatencyAvgUs | double | 只读 | 是 | 邮箱收发延迟 - 平均(微秒)。同上事务的平均往返耗时(最近1秒结算) | |
| PDO 丢帧 | PDO.TotalLost | uint | 只读 | 不可停 | 累计丢帧数(所有组合计) |
PDO.ConsecutiveLost | uint | 只读 | 不可停 | 当前连续丢帧数(所有组中最大值) | |
PDO.GetFrameLossStats(byte group) | PDOFrameLossStats | 只读 | 不可停 | 获取指定组的 PDO 丢帧统计。0-7 返回对应组,不传参数返回所有组汇总。返回值含 TotalLost / ConsecutiveLost / MaxConsecutiveLost | |
| 从站异常 | WorstSlaveIndex | ushort | 只读 | 不可停 | 异常率最高的从站索引 |
WorstLinkQuality | short | 只读 | 不可停 | 最差从站的通信健康度(%),越低越差 | |
| WKC | WKC | ushort | 只读 | 不可停 | 当前工作计数器(actual WKC)。WKC ≠ ExpectedWKC 表示有从站未正确响应 |
ExpectedWKC | ushort | 只读 | 不可停 | 期望工作计数器(expected WKC)。配置期/进 OP 时确定的固定真值,永不动态下调迁就劣化总线 | |
PrimaryWKC | ushort | 只读 | 不可停 | 主网口工作计数器(冗余模式下独立跟踪) | |
SecondaryWKC | ushort | 只读 | 不可停 | 副网口工作计数器(冗余模式下独立跟踪) | |
PrimaryExpectedWKC | ushort | 只读 | 不可停 | 主发帧期望 WKC = ring + branch(看到全部从站)。冗余下与 SecondaryExpectedWKC 不同,单口/拓扑未识别时返回 0,回退到 ExpectedWKC | |
SecondaryExpectedWKC | ushort | 只读 | 不可停 | 副发帧期望 WKC = ring 部分(跳过分支,仅环上从站)。单口/拓扑未识别时返回 0,回退到 ExpectedWKC | |
| WKC 镜像(per-slave WcState 主站级聚合) | WkcActualMirror | int | 只读 | 不可停 | 总线实测工作计数器镜像(actual WKC)— 反映此刻哪些从站真的在响应。R1:如实值,永不篡改。薄读零帧,实时 |
WkcExpectedMirror | int | 只读 | 不可停 | 期望工作计数器镜像(expected WKC)— 配置期/进 OP 时确定的固定真值。R1:拓扑固定它就固定,概念上不可变,永不动态下调迁就劣化总线。薄读零帧,实时 | |
WcDeficit | int | 只读 | 不可停 | WKC 缺额 = expected − actual(>0 表示有映射从站此刻没贡献 WKC,疑似掉站/热插拔)。R1:不是 master 故障,是总线现实的可观测量;从站恢复后自动归 0。薄读零帧,实时 | |
MappedSlaveCount | int | 只读 | 不可停 | 已映射(参与 WKC 计算)的从站数。薄读零帧,实时 | |
WcStateSeq | ulong | 只读 | 不可停 | 内核 WcState 缓存序列号(每次刷新自增)。用于判断 per-slave 镜像是否在两次读取之间更新过(无需轮询帧)。薄读零帧,实时 | |
| 网口状态 | PrimaryPortOk | bool | 只读 | 不可停 | 主端口是否正常(有流量且5秒内无错误) |
SecondaryPortOk | bool | 只读 | 不可停 | 副端口是否正常(有流量且5秒内无错误,无冗余时始终 false) | |
PrimaryPortErrors | uint | 只读 | 不可停 | 主端口最近5秒错误数 | |
SecondaryPortErrors | uint | 只读 | 不可停 | 副端口最近5秒错误数 | |
| 拓扑 | TopologyDescription | string | 只读 | 不可停 | 拓扑模式描述("线性" / "环形" / "环+分支") |
TimingMode | string | 只读 | 不可停 | 定时模式("硬件定时器" / "RT就绪" / "降级" / "RT错误")。WDK 驱动必须就绪,不再存在软件定时器路径 | |
| DC 同步 | SyncWindowThreshold | int | 读写 | 不可停 | 同步窗口阈值(纳秒),默认 1000ns。超出阈值触发 DCSyncLost 事件 |
| 冗余状态 | RedundancyActive | bool | 只读 | 不可停 | 冗余是否激活(存在断线但网络仍正常运行) |
RingMode | RingMode | 只读 | 不可停 | 环拓扑冗余运行模式(Inactive/Dual/Degraded) | |
BreakPoint | BreakPointInfo? | 只读 | 不可停 | 当前故障点(多个故障只返回第一个)。支持断线和 CRC 故障两种类型,恢复后自动清除,无故障为 null | |
| 诊断控制 | Enabled | bool | 读写 | 诊断数据采集开关(默认关闭)。启用后周期性采样,记录标记为[是]的统计数据 |
计算公式
| 指标 | 公式 | 说明 |
|---|---|---|
| RTcnt | 采样周期帧数 / 窗口秒数 | 滑动窗口平均帧频 |
| ErrorCnt | 采样周期错误数 / 窗口秒数 | 滑动窗口平均错误率 |
| PacketLossRate | (TX-RX-pipeline) / TX | 5 秒滑窗丢包率, 全双工 pipeline 在途不算丢 |
| LateFrameRate | LateDrop / TX | 过慢帧率 (idx 出 8 帧窗 stale, 不计入丢包) |
需 Enabled = true。
PDOFrameLossStats 结构
| 属性 | 类型 | 说明 |
|---|---|---|
| TotalLost | uint | 累计丢帧数 |
| ConsecutiveLost | uint | 当前连续丢帧数 |
| MaxConsecutiveLost | uint | 历史最大连续丢帧数 |
示例:
var diag = master.Diagnostics;
diag.Enabled = true; // 启用诊断数据采集
// 帧计数
Console.WriteLine($"帧频: {diag.RTcnt} Hz");
Console.WriteLine($"丢包率: {diag.PacketLossRate:P2}");
Console.WriteLine($"错误数: {diag.ErrorCnt}");
// 周期与抖动
Console.WriteLine($"周期时间: {diag.CycleTimeSpan} µs");
Console.WriteLine($"总线抖动: 平均 {diag.AvgJitterUs:F2} µs");
// 邮箱收发延迟(CoE/SoE/FoE/EoE/AoE/VoE 非周期事务往返时延)
Console.WriteLine($"邮箱延迟: 平均 {diag.MailboxLatencyAvgUs:F2} µs, 最大 {diag.MailboxLatencyUs:F2} µs");
// PDO 丢帧(所有组汇总)
var pdo = diag.PDO;
Console.WriteLine($"PDO 丢帧: 累计={pdo.TotalLost}, 连续={pdo.ConsecutiveLost}");
if (pdo.ConsecutiveLost > 10)
Console.WriteLine("警告: PDO 连续丢帧!");
// 按组查询丢帧(0-7)
var group0Stats = pdo.GetFrameLossStats(0); // 组0(默认组)
var group1Stats = pdo.GetFrameLossStats(1); // 组1
Console.WriteLine($"组0丢帧: {group0Stats.TotalLost}, 组1丢帧: {group1Stats.TotalLost}");
// 从站异常
Console.WriteLine($"最差从站: #{diag.WorstSlaveIndex} ({diag.WorstLinkQuality}%)");
// 网口状态
Console.WriteLine($"主端口: {(diag.PrimaryPortOk ? "正常" : "异常")}");
Console.WriteLine($"副端口: {(diag.SecondaryPortOk ? "正常" : "未连接")}");
// 拓扑信息
Console.WriteLine($"拓扑: {diag.TopologyDescription}");
Console.WriteLine($"定时: {diag.TimingMode}");
每个从站的 ESC 端口错误通过 slave.Diagnostics.ReadPortErrors() 获取。详见 从站诊断 - 通信诊断。
WKC 镜像(掉站可观测)
WkcActualMirror / WkcExpectedMirror / WcDeficit 是内核 per-slave WcState 诊断缓存的主站级聚合镜像,全部为薄读零帧(不缓存、不需刷新——内核每周期、WKC 异常时立即维护,DLL 保证读到即此刻真实总线现实)。配合每个从站的 slave.WcContributed 可定位到具体掉了哪个从站。
R1 可观测性约定(不可违反):
WkcExpectedMirror= 配置期 / 进 OP 时确定的固定真值,拓扑固定它就固定,概念上不可变,永不动态下调迁就劣化总线。WkcActualMirror= 总线实测值,如实反映此刻哪些从站真的在响应,永不篡改。WcDeficit = WkcExpectedMirror − WkcActualMirror。WcDeficit > 0不是 master 故障,而是有映射从站此刻没贡献 WKC(疑似掉站 / 热插拔恢复中);从站恢复后WcDeficit自动归 0,无需任何重置调用。
示例:
var diag = master.Diagnostics;
// 主站级 WKC 镜像(薄读零帧,实时)
Console.WriteLine($"WKC: {diag.WkcActualMirror} / {diag.WkcExpectedMirror} (映射从站 {diag.MappedSlaveCount} 个)");
if (diag.WcDeficit > 0)
{
Console.WriteLine($"⚠️ WKC 缺额 {diag.WcDeficit} — 有从站没在响应,逐个从站定位:");
foreach (var slave in master.Slaves)
if (slave.WcContributed == WcContribution.NotContributed)
Console.WriteLine($" 从站 {slave.SlaveNum} 未贡献 WKC (AL={slave.AlStatusCode})");
}
// 序列号判断镜像是否更新过(无需轮询帧)
ulong seq = diag.WcStateSeq;
单个从站对 WKC 的贡献状态请用 slave.WcContributed(WcContribution 枚举)。WcDeficit > 0 时遍历从站即可定位掉站点。
拓扑重建与热插拔
运行中现场插拔从站(换一个 IO 模块 / 加一个伺服)后,可用以下两个入口让 SDK 与新物理拓扑对齐。两者均通过 master.Diagnostics 访问。
RefreshTopology()
public void RefreshTopology()
重扫各从站端口 + 重建拓扑图 + 按端口 active 走图,重算每路径期望 WKC(PrimaryExpectedWKC / SecondaryExpectedWKC),不重启总线。插 / 拔从站后调用,让拓扑显示与 WKC 期望与当前物理端口图对齐。
失败时不抛异常,沿用上一轮拓扑表(C 端已有"防期望恒 0"守护,保证回退后期望非零)。仅刷新拓扑视图与期望 WKC,不改变从站状态、不重配 PDO。
示例:
master.Diagnostics.RefreshTopology();
Console.WriteLine($"刷新后期望 WKC: 主 {master.Diagnostics.PrimaryExpectedWKC}, 副 {master.Diagnostics.SecondaryExpectedWKC}");
RefreshTopology() 只刷新拓扑视图与期望 WKC(轻量、不动从站状态);HotSwapRebuild() 是一次性热插拔重建——重扫从站 + 重配 PDO + 恢复 OP。换模块后想真正把新从站带回运行态用 HotSwapRebuild();只想刷新拓扑显示用 RefreshTopology()。
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 | (通用失败) | 调用层异常(DLL 未导出等),不抛异常直接返通用失败码 |
行为 / 约束(R1 如实可观测):
- 在 OP 状态调用会短暂中断 PDO(内部降 PreOp 重配再升回 OP),调用方需容忍这一周期的过程数据空窗。
- 失败时返错误码不掩盖——绝不靠篡改内部计数让结果"看起来对"。
- 即使最终 0 个从站在 OP,PDO 循环也照常运行(周期不掉、帧不停),
ExpectedWKC不动态下调迁就劣化总线。
示例:
int rc = master.Diagnostics.HotSwapRebuild();
switch (rc)
{
case 0:
Console.WriteLine("热插拔重建成功,从站已回到 OP");
break;
case -20:
Console.WriteLine("有操作进行中,稍后重试");
break;
case -21:
Console.WriteLine("重扫到 0 站,已停 PreOp 安全态——检查链路/供电");
break;
case -23:
Console.WriteLine("部分从站未达 OP,已回滚安全态——逐个查 AL Status Code:");
foreach (var slave in master.Slaves)
if (slave.State != EcState.OP)
Console.WriteLine($" 从站 {slave.SlaveNum}: AL=0x{(ushort)slave.ErrorCode:X4}");
break;
default:
Console.WriteLine($"热插拔重建失败: {rc}");
break;
}
DC 同步
自动监控(ETG.1500 5.13.3),每秒检查各从站时间偏差。超出 SyncWindowThreshold 阈值时触发 DCSyncLost 事件。
单个从站的同步状态请使用 slave.Diagnostics.DC.IsInSync 和 slave.Diagnostics.DC.SyncTimeDifference。
冗余状态
RingMode 反映环拓扑冗余的运行状态。Inactive 表示冗余未初始化,Dual 表示双向冗余正常工作,Degraded 表示 secondary 链路不可用仅 primary 工作。
BreakPoint 统一检测两类物理故障:
| 类型 | FaultType | 检测方式 | 典型场景 |
|---|---|---|---|
| 断线 | 0 | DL Status 端口物理链路丢失 | 拔线、线缆断裂 |
| CRC 故障 | 1 | 端口级 RxError + InvalidFrame 持续增长 | 接触不良、线缆老化、连接器氧化 |
故障线缆段定位: 当相邻从站的对向端口(如从站 N 的 P1 和从站 N+1 的 P0)同时报故障,说明连接线缆有问题。仅单侧报故障则定位到该端口连接器。
RingMode 枚举
| 值 | 名称 | 说明 |
|---|---|---|
| 0 | Inactive | 未激活:冗余监控未初始化 |
| 1 | Dual | 双向冗余:双端口发送,正常工作 |
| 2 | Degraded | 降级模式:secondary链路不可用,仅primary工作 |
BreakPointInfo 结构
| 属性 | 类型 | 说明 |
|---|---|---|
| SlaveIndex | ushort | 故障从站索引 (1-based) |
| Port | byte | 故障端口号 (0-3, 对应 P0-P3) |
| FaultType | byte | 故障类型:0=断线,1=CRC 故障 |
| IsLinkDown | bool | 是否为断线故障 |
| IsCrcFault | bool | 是否为 CRC 故障(线缆/连接器劣化) |
单个从站的冗余状态请参考 从站诊断 - 冗余诊断。
示例:
var diag = master.Diagnostics;
// 环拓扑冗余模式
Console.WriteLine($"冗余模式: {diag.RingMode}");
if (diag.RingMode == RingMode.Degraded)
Console.WriteLine("警告: secondary链路不可用,仅primary工作");
if (diag.RedundancyActive)
Console.WriteLine("冗余已激活");
var bp = diag.BreakPoint;
if (bp != null)
{
Console.WriteLine($"故障: {bp}"); // 输出: "从站5 P3 断线" 或 "从站3 P1 CRC故障"
if (bp.Value.IsCrcFault)
Console.WriteLine("建议检查线缆/连接器");
}
诊断快照
GetSnapshot()
public DiagnosticsSnapshot GetSnapshot()
一次调用获取所有诊断数据的一致快照,避免多次属性访问导致的数据不一致和性能开销。适合 UI 刷新和日志记录场景。
返回值:
DiagnosticsSnapshot— 诊断数据快照对象
相关结构:
public class DiagnosticsSnapshot
{
public int Frequency { get; } // 每秒帧数 (Hz)
public uint ErrorCount { get; } // 每秒错误数
public float PacketLossRate { get; } // 丢包率 (0.0-1.0) — TX vs RX, 5 秒滑窗
public float LateFrameRate { get; } // 过慢帧率 (0.0-1.0) — idx 出 8 帧窗 stale, 不计丢
public double AvgJitterUs { get; } // 总线平均抖动 (µs)
public double MailboxLatencyUs { get; } // 邮箱收发延迟 - 最大 (µs)
public double MailboxLatencyAvgUs { get; } // 邮箱收发延迟 - 平均 (µs)
public int CycleTimeUs { get; } // 实际周期时间 (µs)
public ushort WkcActual { get; } // 当前 WKC
public ushort WkcExpected { get; } // 期望 WKC
public int WcDeficit { get; } // WKC 缺额 = expected − actual
public ulong WcStateSeq { get; } // 内核 WcState 缓存序列号
public int MappedSlaveCount { get; } // 已映射(参与 WKC)从站数
public int WkcActualMirror { get; } // 实测 WKC 镜像
public int WkcExpectedMirror { get; } // 期望 WKC 镜像
public ushort PrimaryWKC { get; } // 主网口 WKC
public ushort SecondaryWKC { get; } // 副网口 WKC
public ushort PrimaryExpectedWKC { get; } // 主发帧期望 WKC
public ushort SecondaryExpectedWKC { get; } // 副发帧期望 WKC
public uint BusCycleHz { get; } // 总线频率 (Hz)
public double BusLoadPercent { get; } // 通讯负载 (%)
public bool PrimaryPortOk { get; } // 主端口正常
public bool SecondaryPortOk { get; } // 副端口正常
public bool RedundancyActive { get; } // 冗余激活
public bool HasWkcMirrorData { get; } // WKC 镜像数据是否就绪(MappedSlaveCount>0 且 WcStateSeq>0 且 WkcExpectedMirror>0)
}
示例:
var snap = master.Diagnostics.GetSnapshot();
Console.WriteLine($"频率: {snap.Frequency} Hz, 抖动: {snap.AvgJitterUs:F2} µs");
Console.WriteLine($"WKC: {snap.WkcActual}/{snap.WkcExpected}");
if (snap.RedundancyActive)
Console.WriteLine("冗余已激活");
诊断控制
标记为"是"的属性需先设置 Enabled = true 启动周期性采样,标记为"不可停"的功能始终活跃。
Reset (诊断计数器)
public void Reset()
一次性重置所有诊断统计,包括:
- 基础诊断统计(帧错误等)
- PDO 丢帧统计
- DC 同步窗口统计
- 所有从站的端口错误计数器
只清空主站层面的统计数据, 不影响 FSoE 安全连接状态机 (slave.FSoE.Reset)。
AL 错误分类
对 AL Status Code 进行分类,帮助快速判断错误性质和处理策略。
ALErrorClassifier.Classify
public static ALErrorCategory Classify(ushort alStatusCode)
public static ALErrorCategory Classify(EcALState alState)
对 AL Status Code(从站返回的错误码)进行分类,帮助快速判断错误性质和处理策略。支持 ushort 和 EcALState 两种参数类型。
参数:
alStatusCode(ushort) 或alState(EcALState) — AL Status Code,从slave.ErrorCode或状态转换失败时获取
返回值:
ALErrorCategory— 错误分类枚举
相关结构:
None(0) — 无错误Transient(1) — 瞬态错误,可重试状态转换,通常自动恢复Configuration(2) — 配置错误,检查 PDO 映射、SM 配置、Startup 参数等Hardware(3) — 硬件错误,检查从站硬件、线缆、电源Unknown(4) — 未知错误,查阅 ETG.1000 或从站手册
示例:
var slave = master.Slaves[1];
if (slave.ErrorCode != EcALState.NoError)
{
var category = ALErrorClassifier.Classify(slave.ErrorCode);
Console.WriteLine($"从站 {slave.SlaveNum} 错误 0x{(ushort)slave.ErrorCode:X4}: {category}");
switch (category)
{
case ALErrorCategory.Transient:
Console.WriteLine("瞬态错误,尝试重新切换状态...");
break;
case ALErrorCategory.Configuration:
Console.WriteLine("配置错误,请检查 PDO/SM 配置");
break;
case ALErrorCategory.Hardware:
Console.WriteLine("硬件错误,请检查从站设备");
break;
}
}
0x001E无效输入映射 — Configuration0x001D无效输出映射 — Configuration0x0011无效邮箱配置 — Configuration0x002D同步错误 — Transient0x0032DC 同步超时 — Transient0x0050EEPROM 错误 — Hardware
C# 可用 slave.ErrorCode.Category() / IsTransient() / IsMailboxError() / NeedsDowngradeTo(EcState.PreOp) 等扩展方法, 配合 switch 表达式直接分类映射到处理动作, 也可一行 label.Text = slave.ErrorCode.DescribeCategory() 输出中文标签. 详见 Pattern Matching: AL Status 分类.
从站错误计数器
ReadSlaveErrorCounters(int slaveIndex)
public SlaveErrorCounters ReadSlaveErrorCounters(int slaveIndex)
读取指定从站的错误计数器汇总(CRC 错误、帧错误、丢帧统计)。
参数:
slaveIndex(int) — 从站索引(1-based)
返回值:
SlaveErrorCounters— 错误计数器汇总
相关结构:
public class SlaveErrorCounters
{
public int SlaveIndex { get; } // 从站编号
public uint Port0CrcErrors { get; } // 端口 0 CRC 错误
public uint Port1CrcErrors { get; } // 端口 1 CRC 错误
public uint Port2CrcErrors { get; } // 端口 2 CRC 错误
public uint Port3CrcErrors { get; } // 端口 3 CRC 错误
public uint FrameErrors { get; } // 帧错误计数
public uint LostFrames { get; } // 丢帧计数
}
示例:
var counters = master.Diagnostics.ReadSlaveErrorCounters(1);
if (counters.Port0CrcErrors + counters.FrameErrors + counters.LostFrames > 0)
{
Console.WriteLine($"从站 1 错误计数:");
Console.WriteLine($" CRC 错误: P0={counters.Port0CrcErrors}, P1={counters.Port1CrcErrors}, P2={counters.Port2CrcErrors}, P3={counters.Port3CrcErrors}");
Console.WriteLine($" 帧错误: {counters.FrameErrors}");
Console.WriteLine($" 丢帧: {counters.LostFrames}");
}
诊断消息
slave.CoE.ReadDiagnosticMessages()
public List<DiagnosticMessage> ReadDiagnosticMessages()
通过 CoE 读取从站对象 0x10F3(诊断历史对象,ETG.1020)中的诊断消息。返回从站记录的诊断事件列表,包含时间戳、错误码和描述信息。
访问路径: slave.CoE.ReadDiagnosticMessages()
返回值:
List<DiagnosticMessage>— 诊断消息列表,无消息时返回空列表
示例:
foreach (var slave in master.Slaves)
{
var messages = slave.CoE?.ReadDiagnosticMessages();
if (messages == null || messages.Count == 0) continue;
foreach (var msg in messages)
{
Console.WriteLine($"[从站 {slave.SlaveNum}] " +
$"代码=0x{msg.DiagCode:X8}, {msg}");
}
}
并非所有从站都支持 0x10F3 诊断历史对象。不支持的从站调用时返回空列表。此方法通过 SDO 读取,不建议在实时路径中高频调用。
按问题分块的诊断字段指南
本章按现场症状分块, 帮你定位问题在哪。每个字段都有真实数据来源, 没有永远 0 的虚假数据。
现场问题 → 字段映射
| 现场症状 | 看哪几个字段 |
|---|---|
| 是不是丢包? | LostFrames + PacketLossRate |
| 是偶发还是持续? | master.Groups[g].ConsecutiveMiss (≥5 = 持续) |
| 哪个从站不稳定? | SlaveDiagnosticsData.SlaveOfflineCount[i] (累计离线次数) |
| 网线接触不稳? | SlaveDiagnosticsData.SlaveLostLinks[i] (链路抖动次数) |
| CRC / 信号问题? | SlaveDiagnosticsData.SlaveInvalidFrames[i] (从 ESC 0x0304) |
| 哪根网线断了? | BreakPoint (定位到具体从站和端口) |
| 主副口收发是否平衡? | PrimaryPortTx vs PrimaryPortRx |
| 冗余真在工作吗? | RedundancyActive + SecondaryPortTx > 0 |
| RT 内核稳定吗? | WdkDropouts |
| DC 同步精度? | master.GetMaxSyncDifference() |
| 总线带宽够用吗? | BusLoadPercent |
🟦 1. 帧错误统计 (TX vs RX 对比)
主站发了多少帧 vs 收回来多少, 差值就是丢失。最核心的网络层诊断。
| 字段 | 类型 | 检测什么 |
|---|---|---|
LostFrames | uint | 帧根本没回来 (PDO 周期内 BUF_TX 状态被超时清掉) |
TimeoutFrames | uint | PDO 周期内未收到响应 (兜底分类) |
TotalWkcMismatches | uint | 累计 WKC 不匹配 (收到帧但从站没全部响应) |
master.Groups[g].ConsecutiveMiss | ushort | 连续 WKC 不足次数 (区分偶发/持续的核心, 按组查询) |
FrameErrors | uint | 派生 = LostFrames + Timeout + WkcErrors |
ChecksumErrors | uint | 物理层 CRC 错 (从 ESC 0x0304 派生, 真实数据) |
🟦 2. 端口收发计数
诊断主副口收发是否平衡 (冗余诊断核心)。
| 字段 | 检测什么 |
|---|---|
PrimaryPortTx / PrimaryPortRx | 主网卡累计发送/接收帧数 |
SecondaryPortTx / SecondaryPortRx | 副网卡 (冗余模式) |
PrimaryPortErrors / SecondaryPortErrors | 5 秒滑动窗口端口错误 |
判断逻辑:
PrimaryPortTx == PrimaryPortRx→ 主路径 OKPrimaryPortTx > PrimaryPortRx→ 主路径丢帧SecondaryPortTx > 0 && SecondaryPortRx > 0→ 冗余真在工作SecondaryPortTx > 0 && SecondaryPortRx == 0→ 副路径回不来
🟦 3. per-slave 物理层 (ESC 寄存器)
定位到具体哪个从站、哪条网线有问题。来自从站 ESC 寄存器, 主站每秒读取并累加。
通过 master.GetDiagnostics() 返回的 SlaveDiagnosticsData 对象访问:
| 字段 | 类型 | 来源 ESC 寄存器 |
|---|---|---|
SlaveRxErrors[i] | uint[] | 0x0300-0x0303 (物理 RX 失败) |
SlaveInvalidFrames[i] | ushort[] | 0x0304-0x0307 (CRC 错) |
SlaveLostLinks[i] | ushort[] | 0x0310-0x0313 (网线接触不稳) |
SlaveLinkQualityPercent[i] | short[] | SDK 内核计算 (0-100, -1=未知) |
索引 1-based, [0] 不用。
🟦 4. 从站离线统计
识别最不稳定的从站, 区分"瞬时抖动"vs"长期掉线"。
| 字段 | 检测什么 |
|---|---|
SlaveOfflineCount[i] | 从站 i 累计离线次数 (从开机) |
SlaveOfflineTotalMs[i] | 从站 i 累计离线总时长 (毫秒) |
用法:
- 100 次抖动 + 300ms = 接触不稳 (平均每次 3ms)
- 1 次离线 + 5000ms = 长期断开后恢复
🟦 5. RT 性能 + 内核稳定性
| 字段 | 检测什么 |
|---|---|
BusCycleHz | RT 内核实际帧发送频率 |
BusMaxJitterUs / BusAvgJitterUs / BusCleanMaxJitterUs | 总线抖动 |
BusRoundtripUs (RTT) | 帧往返延迟 |
BusLoadPercent | 通讯负载 = RTT/周期 (>70% 必须降频) |
SmiCount / SmiPeakUs | 系统中断 (BIOS / 硬件) 干扰 |
WdkDropouts | RT 内核失联次数 (KernelHeartbeat 5s 不更新算 1 次) |
🟦 6. 持续 vs 偶发分类
master.Groups[g].ConsecutiveMiss 阈值衍生:
- 0 → 健康
- 1-4 → 偶发 (USB NIC 抖动, 一般可恢复)
- ≥5 → 持续 (真问题, 必须查)
完整诊断流程示例
var diag = master.Diagnostics;
var slaveDiag = master.GetDiagnostics();
// 1. 看持续 vs 偶发 (按组查询连续 WKC 不足次数)
ushort consecutiveMiss = master.Groups[0].ConsecutiveMiss;
if (consecutiveMiss >= 5)
Console.WriteLine($"⚠️ 持续 WKC 错误 ({consecutiveMiss})");
else if (consecutiveMiss > 0)
Console.WriteLine($"偶发 WKC 错误, 一般可恢复");
// 2. 看丢包率
if (diag.PacketLossRate > 0.01)
Console.WriteLine($"⚠️ 丢包率 {diag.PacketLossRate:P2} 严重");
// 3. 故障定位 — BreakPoint 直接给出哪个从站哪个端口
var bp = diag.BreakPoint;
if (bp != null)
Console.WriteLine($"故障点: {bp}");
// 4. 找最不稳定的从站
if (slaveDiag?.SlaveOfflineCount != null)
{
int worst = 0; uint worstCount = 0;
for (int i = 1; i < slaveDiag.SlaveOfflineCount.Length; i++)
if (slaveDiag.SlaveOfflineCount[i] > worstCount)
{ worstCount = slaveDiag.SlaveOfflineCount[i]; worst = i; }
if (worstCount > 0)
Console.WriteLine($"最不稳定从站: #{worst} ({worstCount} 次, {slaveDiag.SlaveOfflineTotalMs[worst]} ms)");
}
// 5. 物理层 CRC 定位
if (slaveDiag?.SlaveInvalidFrames != null)
{
for (int i = 1; i < slaveDiag.SlaveInvalidFrames.Length; i++)
if (slaveDiag.SlaveInvalidFrames[i] > 0)
Console.WriteLine($"从站 {i} CRC 错: {slaveDiag.SlaveInvalidFrames[i]}");
}
// 6. 总线带宽
if (diag.BusLoadPercent > 70)
Console.WriteLine($"⚠️ 总线负载 {diag.BusLoadPercent:F1}%, 必须降频");
// 7. RT 内核失联
if (slaveDiag?.WdkDropouts > 0)
Console.WriteLine($"⚠️ RT 内核失联 {slaveDiag.WdkDropouts} 次");