跳到主要内容

从站诊断

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

从站基础属性

从站状态(stateerror_codeis_lost)等基础属性请参考从站 API。

配合事件使用

建议通过 事件 驱动异常处理(如 slave_state_changedslave_offlinedc_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_contributedWcContribution只读该从站对工作计数器(WKC)的贡献状态。NOT_CONTRIBUTED ≠ master 故障,是该从站此刻没在响应(疑似掉站/热插拔恢复中);内核未填充时诚实返回 UNKNOWN,不臆造
AL 状态镜像al_status_mirrorSlaveAlStatusMirror只读该从站 AL Status 镜像解析对象(含 al_state / has_error / al_status_code)。镜像不可用时返回 raw=0 的对象
邮箱健康mailbox_healthEcMailboxHealth只读该从站邮箱健康度(内核每秒更新镜像薄读)。DEGRADED 表示在 OP 但邮箱半失效(CoE/SDO 可能阻塞),如实反映不参与 WKC 篡改
is_free_run_demotedbool只读该从站是否被迫降级到 FreeRun 同步模式。True 仅当“配置期要 DC 但运行中被迫退到 FreeRun”——半失效根源观测点
health_degraded_countint只读邮箱半失效连续累计计数(≈秒数)。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_activatedbool只读冗余被激活
primary_link_brokenbool只读主线路断路
secondary_link_brokenbool只读冗余线路断路

示例:

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_syncbool只读是否在同步窗口内
sync_time_differenceint只读当前与参考时钟的时间差(纳秒)

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}")