从站诊断
每个从站提供独立的诊断属性和子对象,可查询端口错误、冗余、DC 同步等信息。
从站基础属性
从站状态(state、error_code、is_lost)等基础属性请参考从站 API。
配合事件使用
建议通过 事件 驱动异常处理(如 slave_state_changed、slave_offline、dc_sync_lost),而非轮询。
功能概览
| 功能 | 访问路径 | 说明 |
|---|---|---|
| WKC 与健康镜像 | slave.diagnostics.* | per-slave WcState/AL/邮箱健康内核镜像(薄读零帧,实时) |
| 通信诊断 | slave.diagnostics.read_port_errors() | ESC 端口错误计数器 |
| 冗余诊断 | slave.diagnostics.* | 冗余激活、主/冗余线路断路检测 |
| DC 同步 | slave.diagnostics.dc.* | 同步状态、时间差 |
WKC 与健康镜像
这些属性挂在 slave.diagnostics 上,全部为内核 per-slave 诊断缓存的薄读零帧——内核每周期(WKC 异常时立即)维护缓存,读到即此刻真实总线现实,无需调任何刷新。配合主站级 master.diagnostics_info.wc_deficit 可在掉站时定位到具体从站。
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| WKC 贡献 | wc_contributed | WcContribution | 只读 | 该从站对工作计数器(WKC)的贡献状态。NOT_CONTRIBUTED ≠ master 故障,是该从站此刻没在响应(疑似掉站/热插拔恢复中);内核未填充时诚实返回 UNKNOWN,不臆造 |
| AL 状态镜像 | al_status_mirror | SlaveAlStatusMirror | 只读 | 该从站 AL Status 镜像解析对象(含 al_state / has_error / al_status_code)。镜像不可用时返回 raw=0 的对象 |
| 邮箱健康 | mailbox_health | EcMailboxHealth | 只读 | 该从站邮箱健康度(内核每秒更新镜像薄读)。DEGRADED 表示在 OP 但邮箱半失效(CoE/SDO 可能阻塞),如实反映不参与 WKC 篡改 |
| is_free_run_demoted | bool | 只读 | 该从站是否被迫降级到 FreeRun 同步模式。True 仅当“配置期要 DC 但运行中被迫退到 FreeRun”——半失效根源观测点 | |
| health_degraded_count | int | 只读 | 邮箱半失效连续累计计数(≈秒数)。0 = 健康或未评估;持续累加表示半失效未恢复,内核在 >=3 时触发自动修复 | |
| recover_mailbox_health() | bool | — | 手动触发邮箱半失效修复。通常无需手动调用——内核 >=3 秒后自动修复。通讯异常返回 False 不抛异常 |
WcContribution
class WcContribution(IntEnum):
NOT_CONTRIBUTED = 0 # 该从站此刻没在贡献 WKC(疑似掉站 / 热插拔恢复中)
CONTRIBUTED = 1 # 该从站正常贡献 WKC(在总线上响应)
UNKNOWN = 0xFF # 内核镜像未填充(尚未运行 PDO 循环 / 索引越界)— 诚实暴露,不臆造
SlaveAlStatusMirror
@dataclass
class SlaveAlStatusMirror:
# raw 为内核 16bit AL 镜像:低字节 = AL State(bit4 = Error),高字节 = AL Status Code
al_state: EcState # AL Status 状态(低字节去掉 Error bit)
has_error: bool # 从站是否处于错误状态(bit4)
al_status_code: int # AL Status Code 高字节(0-255),仅 has_error 时有诊断意义
EcMailboxHealth
class EcMailboxHealth(IntEnum):
UNKNOWN = 0 # 未知 / 无邮箱 / 不在 OP — 邮箱健康度此时无意义
HEALTHY = 1 # 健康 — 在 OP 且邮箱可用
DEGRADED = 2 # 降级 — 在 OP 但邮箱半失效(handler 丢失 / DC 降级伴 0x001F)
示例:
from darra_ethercat import WcContribution, EcMailboxHealth
for slave in master.slaves:
d = slave.diagnostics
# WKC 贡献:掉站定位
if d.wc_contributed == WcContribution.NOT_CONTRIBUTED:
al = d.al_status_mirror
print(f"从站 {slave.slave_num} 未贡献 WKC, AL=0x{al.al_status_code:02X}")
# 邮箱半失效(在 OP 但 CoE 可能阻塞)
if d.mailbox_health == EcMailboxHealth.DEGRADED:
print(f"从站 {slave.slave_num} 邮箱半失效 {d.health_degraded_count}s"
+ (" (被迫 FreeRun)" if d.is_free_run_demoted else ""))
if d.health_degraded_count >= 5:
d.recover_mailbox_health() # 通常内核已自动修复,此为手动介入
通信诊断
read_port_errors()
def read_port_errors(self) -> Optional[EscPortErrors]
读取从站 ESC 端口错误计数器。
EscPortErrors 数据类:
@dataclass
class EscPortErrors:
rx_error: List[int] # 各端口 RX 错误计数 [Port0-3]
invalid_frame: List[int] # 各端口无效帧计数 [Port0-3]
lost_link: List[int] # 各端口链路丢失计数 [Port0-3]
@property
def has_errors(self) -> bool # 是否存在任何错误 (由三个列表派生, 只读)
示例:
for slave in master.slaves:
errors = slave.diagnostics.read_port_errors()
if errors is not None and errors.has_errors:
print(f"从站 {slave.slave_num}: "
f"RX错误={errors.rx_error}, "
f"无效帧={errors.invalid_frame}, "
f"链路丢失={errors.lost_link}")
冗余诊断
通过 slave.diagnostics 访问从站的冗余状态。
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 冗余诊断 | redundancy_activated | bool | 只读 | 冗余被激活 |
| primary_link_broken | bool | 只读 | 主线路断路 | |
| secondary_link_broken | bool | 只读 | 冗余线路断路 |
示例:
for slave in master.slaves:
if not slave.diagnostics.redundancy_activated:
continue
msg = f"从站 {slave.slave_num}: 冗余激活"
if slave.diagnostics.primary_link_broken:
msg += ", 主线路断路"
if slave.diagnostics.secondary_link_broken:
msg += ", 冗余线路断路"
print(msg)
DC 同步
通过 slave.diagnostics.dc 访问从站的 DC 同步状态。
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| DC 同步 | is_in_sync | bool | 只读 | 是否在同步窗口内 |
| sync_time_difference | int | 只读 | 当前与参考时钟的时间差(纳秒) |
SyncWindowStatus 数据类 (唯一定义来源: slave/slave_stats.py):
@dataclass
class SyncWindowStatus:
diff_ns: int = 0 # 当前时间差 (纳秒)
max_diff_ns: int = 0 # 最大时间差 (纳秒)
min_diff_ns: int = 0 # 最小时间差 (纳秒)
in_sync: bool = False # 是否在同步窗口内
out_of_sync_count: int = 0 # 超出同步窗口次数
通过 DcConfig.get_sync_window_status() 获取一次性快照。
示例:
for slave in master.slaves:
if not slave.has_dc:
continue
dc = slave.diagnostics.dc
print(f"从站 {slave.slave_num}: "
f"时间差={dc.sync_time_difference}ns, "
f"同步={dc.is_in_sync}")
完整示例
for slave in master.slaves:
print(f"--- 从站 {slave.slave_num}: {slave.name} ---")
# 端口错误
errors = slave.diagnostics.read_port_errors()
if errors is not None and errors.has_errors:
print(f" 端口错误: RX={errors.rx_error}, "
f"无效帧={errors.invalid_frame}, "
f"链路丢失={errors.lost_link}")
# 冗余(仅冗余模式下有意义)
if slave.diagnostics.redundancy_activated:
print(f" 冗余激活: "
f"主线路断={slave.diagnostics.primary_link_broken}, "
f"冗余线路断={slave.diagnostics.secondary_link_broken}")
# DC 同步
if slave.has_dc:
dc = slave.diagnostics.dc
print(f" DC: 时间差={dc.sync_time_difference}ns, "
f"同步={dc.is_in_sync}")