跳到主要内容

Slave 属性与方法

通过 master.slave(n) 获取 (1-based 索引)。

提示
  • PDO 输入输出(结构体映射、字节切片、索引访问等)请参见 PDO 输入输出 页面。
  • 从站事件请参见 事件 页面。
  • 从站诊断请参见 从站诊断 页面。

属性

类别方法返回类型说明
基本标识index()u16从站索引 (1-based)
master_index()u16主站索引
name()String设备名称(从 EEPROM group_name 读取)
drive_name()String驱动/设备名称(从 SDO 0x1008 读取)
设备信息vendor_id()u32制造商 ID(从 SII EEPROM 读取)
vendor_name()String制造商名称
product_id()u32产品 ID
rev_id()u32修订版本号
serial_number()u32序列号(从 SII EEPROM 读取)
has_mdp()bool是否支持模块化设备配置文件(ETG.5001)
block_lrw()boolLRW 逻辑读写操作阻止标志
地址config_addr()u16物理配置地址
alias_address()u16别名地址
状态state()Option<EcState>从站当前状态 (可能读自缓存)
state_raw()u8从站当前状态 (原始值)
state_live()Option<EcState>从站当前状态 — 强制实时读, 绕过缓存
error_code()EcALStateAL Status Code 错误码 (枚举, 可能读自缓存)
error_code_raw()u16AL Status Code 错误码 (原始值)
error_code_live()EcALStateAL Status Code — 强制实时读, 绕过缓存
is_lost()bool从站是否丢失(断开连接)
诊断镜像wc_contributed()WcContribution该从站本周期对工作计数器(WKC)的贡献状态(薄读零帧),详见 WKC 贡献语义
wc_contributed_raw()u8WKC 贡献状态原始值(1=贡献 / 0=未贡献 / 0xFF=未知)
al_status_mirror()SlaveAlMirror内核 AL 状态镜像(解析为 al_state + al_status_code + raw,薄读零帧)
al_status_mirror_raw()u16AL 状态镜像原始 16bit(低字节 AL State / 高字节 AL Code)
mailbox_healthy()bool该从站邮箱是否健康(mailbox_health_raw == 0,薄读零帧)
mailbox_health_raw()u8邮箱健康原始值(0=正常, 1=Healthy, 2=Degraded)
free_run_demoted()bool该从站是否被迫从 DC 降级到 FreeRun(薄读零帧,半失效根源观测点)
health_degraded_count()u32该从站邮箱半失效连续累计计数(≈秒数,0=健康)
拓扑topology()u8拓扑类型 (0=无链接, 1=端点, 2=中间, 3=分支, 4=交叉)
parent_station()u16父从站的站地址
parent_port()u8父端口号
entry_port()u8入口端口号
active_ports()u8激活端口位掩码
physical_type()u8物理端口类型
child_count()u16子模块数量
PDO 数据input_bits() / output_bits()u16输入/输出数据位数
input_bytes() / output_bytes()u32输入/输出数据字节数
input_offset() / output_offset()u32输入/输出在 IOmap 中的偏移
input_startbit() / output_startbit()u8输入/输出起始位
ESI/配置has_esi()bool是否已加载 ESI 文件
esi_version()StringESI 版本号
config_by_esi()Result<()>根据 ESI 文件自动配置从站 (SM + DC)
EEPROMeep_8byte_addressing()boolEEPROM 寻址模式(true=8字节, false=4字节)
eep_pdi()u8物理设备接口(PDI)类型
ebus_current()i16E-bus 电流消耗(mA)
邮箱mbx_proto()u16支持的邮箱协议类型(MailboxType 位掩码)
mbx_length()u16邮箱发送缓冲区大小
mbx_read_length()u16邮箱接收缓冲区大小
mbx_read_offset() / mbx_write_offset()u16邮箱读/写偏移
mbx_count()u8邮箱协议计数器
协议详情coe_details()u8CoE 协议功能标志(EcCoEDetails 位掩码)
eoe_details()u8EoE 协议功能标志(EcEoEDetails 位掩码)
foe_details()u8FoE 协议详情(来自 SII EEPROM)
soe_details()u8SoE 协议详情(来自 SII EEPROM)
FMMUfmmu0_func()u8FMMU0 功能类型(bit 0=输出, bit 1=输入)
fmmu1_func()u8FMMU1 功能类型
fmmu2_func()u8FMMU2 功能类型
fmmu3_func()u8FMMU3 功能类型
sync_manager_count()u16SyncManager 数量
DChas_dc()bool是否支持 DC,详见 DC 同步
dc_active()u16DC 激活状态(0=禁用, 非0=已激活),详见 DC 同步
dc_cycle0()i32SYNC0 周期(纳秒),详见 DC 同步
dc_cycle1()i32SYNC1 周期(纳秒),详见 DC 同步
dc_shift()i32相位偏移(纳秒),详见 DC 同步
dc_next()u16DC 链中下一个从站索引
dc_previous()u16DC 链中上一个从站索引
dc_parent_port()u8DC 父端口号
dc_receive_time_a()i32端口 A DC 接收时间(纳秒)
dc_receive_time_b()i32端口 B DC 接收时间(纳秒)
dc_receive_time_c()i32端口 C DC 接收时间(纳秒)
dc_receive_time_d()i32端口 D DC 接收时间(纳秒)
propagation_delay()i32帧从主站到达此从站的传播延迟(纳秒),详见 DC 同步
拓扑扩展children()Vec<u16>子从站索引列表(配合 child_count 使用,导航拓扑树)
supports_frame_repeat() / set_supports_frame_repeat(bool)bool是否支持帧重复功能(ETG.1500 5.4.3,读写)
冗余redundancy_activated()bool冗余是否已激活
primary_link_broken()bool主线路是否断路
secondary_link_broken()bool冗余线路是否断路
配置group() / set_group(u8)u8从站分组号(0-7,0=默认组,必须在 SAFE_OP 前设置),详见 从站分组
is_optional() / set_optional(bool)bool可选从站标记,缺席时不影响 WKC 检查、不触发组离线告警(必须在 OP 前设置)
EcDeviceType 枚举值
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcDeviceType {
Undefined = 0, // 未定义
Static = 1, // 静态设备,无IO映射,如EK1100耦合器
InputNoMailbox = 2, // 输入设备(无邮箱)
OutputNoMailbox = 3, // 输出设备(无邮箱)
InputWithMailbox = 4, // 输入设备(有邮箱)
OutputWithMailbox = 5, // 输出设备(有邮箱)
IONoMailbox = 6, // 输入输出设备(无邮箱)
IOWithMailbox = 7, // 输入输出设备(有邮箱)
}
EcTopologyType / EcPortType 枚举值
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcTopologyType {
NoLink = 0, // 无链接
EndPoint = 1, // 端点
Line = 2, // 中间节点(线性拓扑)
Fork = 3, // 分支点
Cross = 4, // 交叉点
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcPortType {
NotUsed = 0, // 未使用
MII = 1, // MII
EBUS = 2, // EBUS
EBUSEnhanced = 3, // EBUS 增强型
}
MailboxType 枚举值
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MailboxType {
ErrorMailbox = 0x00, // 错误邮箱
ADSOverEtherCAT = 0x01, // AoE
EthernetOverEtherCAT = 0x02, // EoE
CANopenOverEtherCAT = 0x03, // CoE
FileOverEtherCAT = 0x04, // FoE
ServoOverEtherCAT = 0x05, // SoE
VendorOverEtherCAT = 0x0F, // VoE
}
EcCoEDetails 位标志

EcCoEDetails 是一个 u8 newtype 结构体(非 bitflags! 宏),标志位以关联常量提供, contains() / union() 做位判断与合并。

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EcCoEDetails(pub u8);

impl EcCoEDetails {
pub const NONE: Self = Self(0x00);
pub const SDO: Self = Self(0x01); // 支持 SDO
pub const SDO_INFO: Self = Self(0x02); // 支持 SDO Info
pub const PDO_ASSIGN: Self = Self(0x04); // 支持 PDO Assign
pub const PDO_CONFIG: Self = Self(0x08); // 支持 PDO Config
pub const STARTUP: Self = Self(0x10); // 支持 Startup
pub const COMPLETE_ACCESS: Self = Self(0x20); // 支持 Complete Access

pub fn contains(self, other: Self) -> bool;
pub fn union(self, other: Self) -> Self;
}
EcEoEDetails 位标志

EcEoEDetails 同样是 u8 newtype 结构体(非 bitflags! 宏)。

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EcEoEDetails(pub u8);

impl EcEoEDetails {
pub const NONE: Self = Self(0x00);
pub const SEND_FRAME: Self = Self(0x01); // 支持发送帧
pub const RECEIVE_FRAME: Self = Self(0x02); // 支持接收帧
pub const SET_IP_PARAM: Self = Self(0x04); // 支持设置 IP 参数
pub const GET_IP_PARAM: Self = Self(0x08); // 支持获取 IP 参数

pub fn contains(self, other: Self) -> bool;
pub fn union(self, other: Self) -> Self;
}
WcContribution 枚举值(WKC 贡献状态)
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WcContribution {
NotContributed = 0, // 该从站本周期未贡献 WKC(掉站 / 未响应)
Contributed = 1, // 该从站本周期正常贡献 WKC
Unknown = 0xFF, // 内核尚无镜像 — 诚实不臆造(其余非 0/1 值一律归此)
}

impl WcContribution {
pub fn from_raw(raw: u8) -> Self; // 1=>Contributed, 0=>NotContributed, 其余=>Unknown
}
SlaveAlMirror 结构(AL 状态镜像解析视图)

al_status_mirror() 把内核 16bit AL 镜像解析为结构化视图:低字节 = AL State(含 Error 位),高字节 = AL Status Code。raw == 0 诚实表示 Unknown(无镜像)。

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SlaveAlMirror {
pub al_state: EcSlaveStatus, // AL State(含 Error 位的完整命名状态;未命名复合值兜底为 None)
pub al_status_code: u16, // AL Status Code(高字节,0 = 无错误码 / Unknown)
pub raw: u16, // native getter 返回的原始 16bit(调试 / 诚实回溯用)
}

impl SlaveAlMirror {
pub fn from_raw(raw: u16) -> Self;
}

子对象

方法返回类型说明
cia401()CiA401CiA 401 I/O 模块辅助实例
esi()EsiLoaderESI 文件加载器
events()SlaveEvents从站级事件,详见 从站事件
diagnostics()SlaveDiagnostics诊断信息访问器,详见 从站诊断
startup()StartupParameterList启动参数列表
sdo_read_value::<T>()Result<T>CoE 类型化读取
sdo_write_value()Result<()>CoE 类型化写入
soe()SoEInstanceServo over EtherCAT
foe()FoEInstanceFile over EtherCAT
eoe()EoEInstanceEthernet over EtherCAT
aoe()AoEInstanceADS over EtherCAT
voe()VoEInstanceVendor over EtherCAT
dc()SlaveDCDC 同步配置

枚举描述

所有枚举统一使用 .description() 方法获取中文描述:

use darra_ethercat::EcState;
// 描述辅助函数位于 statics 模块 (未在 crate 根重新导出)
use darra_ethercat::statics::{cia402_state_description, cia402_mode_description};

// EcState 格式化
let state = slave.state().unwrap_or(EcState::None);
println!("{}", state); // Display trait: "OP"

// CiA 402 状态描述 (独立函数)
let desc = cia402_state_description(4); // "运行使能"
let mode_desc = cia402_mode_description(8); // "周期同步位置 (CSP)"
统一规范

项目中所有枚举描述均通过 .description() 方法获取,包括 EcStateEcALStateSDOErrorFSoEStateFSoEErrorStateCiA402ModeCiA402 等。

硬件信息查询

fmmus()

pub fn fmmus(&self) -> Vec<u8>

获取从站当前的 FMMU 配置原始数据(从站结构体中 fmmu_buffer, 4条目 x 16字节),用于调试或诊断地址映射问题。

sync_managers()

pub fn sync_managers(&self) -> Vec<u8>

获取从站当前的 SyncManager 配置原始数据(从站结构体中 sm_buffer, 8条目 x 8字节)。

sync_manager_types()

pub fn sync_manager_types(&self) -> Vec<u8>

获取从站 SyncManager 类型数组(8 字节, 对应 8 个 SM 的类型)。

返回值:

  • Vec<u8> — 类型数组:0=未使用, 1=邮箱输出, 2=邮箱输入, 3=过程数据输出, 4=过程数据输入

诊断

提示

从站诊断信息(通信异常率、冗余状态、DC 同步)通过 slave.diagnostics() 访问,详见 从站诊断

ESI 方法

set_esi_file(esi_file_name)

pub fn set_esi_file(&self, esi_file_name: &str) -> bool

设置从站使用的 ESI 文件。

返回值:

  • bool — 成功返回 true,失败返回 false

过程数据看门狗

set_watchdog(timeout_ms)

pub fn set_watchdog(&self, timeout_ms: u32) -> Result<()>

设置从站过程数据看门狗超时。从站在超时时间内未收到过程数据帧时触发看门狗错误(ALStatusCode 0x001B)。

参数:

  • timeout_ms (u32) — 超时时间(毫秒),0 = 禁用,最大 6553ms

返回值:

  • Result<()> — 成功或错误
备注

应在 SafeOp 或 OP 状态下调用。

set_pdi_watchdog(timeout_ms)

pub fn set_pdi_watchdog(&self, timeout_ms: u32) -> Result<()>

设置从站 PDI 看门狗超时。PDI 看门狗监控从站本地应用(微控制器固件)是否正常运行。

参数:

  • timeout_ms (u32) — 超时时间(毫秒),0 = 禁用

返回值:

  • Result<()> — 成功或错误

示例:

slave.set_watchdog(100)?;  // 100ms 超时
slave.set_watchdog(0)?; // 禁用看门狗

watchdog_config()

pub fn watchdog_config(&self) -> Result<WatchdogConfig>

读取从站看门狗当前配置。读取失败返回 Err

返回值:

  • Result<WatchdogConfig> — 看门狗配置

相关结构:

pub struct WatchdogConfig {
pub divider: u16, // 看门狗分频器
pub pdi_timeout: u16, // PDI 看门狗超时值(分频器单位)
pub pd_timeout: u16, // 过程数据看门狗超时值(分频器单位)
}

示例:

if let Ok(config) = slave.watchdog_config() {
println!("PD超时: {} × {:.2}us", config.pd_timeout, config.divider as f64 * 0.04);
}

watchdog_status()

pub fn watchdog_status(&self) -> Result<WatchdogStatus>

读取从站看门狗运行状态。读取失败返回 Err

返回值:

  • Result<WatchdogStatus> — 看门狗状态

相关结构:

pub struct WatchdogStatus {
pub expired: bool, // 看门狗是否已过期
pub counter: u8, // 看门狗计数器(过期累计次数)
pub divider: u16, // 当前分频器值
pub timeout_value: u16, // 当前超时值
}

示例:

if let Ok(status) = slave.watchdog_status() {
if status.expired {
println!("看门狗过期! 计数: {}", status.counter);
}
}

状态切换

从站状态机仅暴露 1 个公开 API: set_state, 同步执行单个从站的状态切换, 协议层按 EtherCAT 标准状态机自动处理中间状态。失败原因通过 Result::Err 返回 (见 DarraError)。

set_state()

pub fn set_state(&self, state: EcState, timeout_ms: u32) -> Result<(), DarraError>

切换单个从站到目标状态, 阻塞调用线程直到完成或超时。用于手动恢复单个从站、热插拔后重连入网, 或在 PDO 运行中将单从站切换到 PreOp / Init 维护。

参数:

  • state (EcState) — 目标状态
  • timeout_ms (u32) — 单步超时 (毫秒), 推荐 3000 ~ 5000

返回值:

  • Ok(()) — 已到达 state
  • Err(DarraError::StateChangeFailed(state)) — 切换失败 (超时 / AL 拒绝等), 错误描述可直接 Display 输出
备注

状态切换遵循 EtherCAT 标准状态机流程, 协议层自动处理中间状态。例如从 Init 切换到 OP 会自动经过 PreOp → SafeOp → OP。

示例:

// 手动恢复单个从站到 OP
if slave.state() != Some(EcState::Operational) {
slave.set_state(EcState::Operational, 5000)?;
}

// 将从站切换到 Init (重置)
slave.set_state(EcState::Init, 5000)?;

实时状态读取 — state_live() / error_code_live()

pub fn state_live(&self) -> Option<EcState>
pub fn error_code_live(&self) -> EcALState

state() / error_code() 可能读自内核维护的状态缓存 (周期刷新, 适合高频 UI 轮询)。state_live() / error_code_live() 绕过缓存, 每次发起一次实时探测 — 用于状态切换后立即确认、热插拔检测等对时效敏感的场景。

返回类型仍为枚举: state_live() 返回 Option<EcState>, error_code_live() 返回 EcALState

示例:

// 状态切换后立即用 live 接口确认, 不等缓存刷新
slave.set_state(EcState::Operational, 5000)?;
if slave.state_live() != Some(EcState::Operational) {
println!("从站未进 OP, AL 错误码: {}", slave.error_code_live());
}
何时用 live 接口
  • state() / error_code() — UI 刷新、周期性巡检, 走缓存零额外帧开销。
  • state_live() / error_code_live() — 切换后确认、故障诊断, 需要"此刻"的真值。

实时诊断镜像 (薄读零帧)

wc_contributed() / al_status_mirror() / mailbox_healthy() / free_run_demoted() / health_degraded_count() 都是内核 per-slave 诊断缓存的薄读:每次调用直接读 native,不收发帧(零帧),不二次缓存,读到即此刻总线现实,无需手动刷新。

WKC 贡献语义

wc_contributed() 反映此刻该从站是否真的在响应

  • Contributed — 该从站正常贡献工作计数器(WKC),即聚合 WKC 完整(actual == expected,亦即缺口 WcDeficit == 0)时各从站逐字节比对都"满"。
  • NotContributed — 该从站此刻没在贡献 WKC(疑似掉站 / 热插拔恢复中)。这不是 master 故障,从站修复后内核镜像自然回到 Contributed
  • Unknown — 内核镜像尚无该从站值(未运行 PDO 循环 / 索引越界),诚实暴露,不臆造为"已贡献"。
R1 可靠性铁律

本接口仅"如实反映总线现实", 不参与任何 WKC 篡改 / 迁就逻辑。逐从站贡献位是聚合 WKC 缺口(expected - actual)的逐站分解:聚合满则该站贡献,缺口大于 0 表示有从站此刻未贡献,应报警 + 诊断,不下调 expected、不停 OP

示例:

for i in 1..=master.slave_count() {
let slave = master.slave(i);
if slave.wc_contributed() == WcContribution::NotContributed {
let al = slave.al_status_mirror();
println!("从站 {} 此刻未贡献 WKC(疑似掉站),AL Code=0x{:04X}", i, al.al_status_code);
}
if !slave.mailbox_healthy() {
println!("从站 {} 邮箱半失效, 已持续 {}s", i, slave.health_degraded_count());
}
}

recover_mailbox_health()

pub fn recover_mailbox_health(&self) -> bool

手动触发该从站邮箱半失效修复。通常无需手动调用 —— 底层在半失效持续 ≥3 秒后会自动修复。返回 true = 已触发修复流程。这是显式运维动作,不是静默掩盖。

recover_to_op()

pub fn recover_to_op(&self) -> Option<EcState>

将该从站经 INIT→…→OP 状态机重新拉回 OP(合法热插拔恢复),返回最终到达的 AL 状态枚举。单从站恢复期间 PDO 循环不停。

ESC 寄存器访问 (高级)

直接读写从站 ESC (EtherCAT Slave Controller) 寄存器,用于故障诊断自定义 ESC 操作底层调试。协议层走 FPRD/FPWR 数据报,自动 primary → secondary → APWR 三级回退。

高级 API

正常使用 SDK 时无需调用 — 状态切换 / PDO / 邮箱等流程 SDK 已自动配置寄存器。此 API 用于深度诊断特殊场景(例如读取错误计数器、强制端口策略、调试 ESI 烧写不生效等)。

寄存器定义见 ETG.1000.4 §6 / ETG.1000.6 §5(公开标准),例如:

寄存器说明
0x0000Type / Revision / Build(设备类型)
0x0030AL Control(主站发起状态请求)
0x0130AL Status(从站当前状态)
0x0134AL Status Code(错误码)
0x0300-0x030F端口 0-3 错误计数器
0x0400-0x043F看门狗配置 / 计数

read_register(reg_addr, len)

pub fn read_register(&self, reg_addr: u16, len: u32) -> Result<Vec<u8>>

读取从站 ESC 寄存器(FPRD)。

参数:

  • reg_addr (u16) — 寄存器地址(例如 0x0130 = AL Status)
  • len (u32) — 读取字节数(1 / 2 / 4 等)

返回值:

  • Result<Vec<u8>> — 成功返回字节向量;失败(从站离线 / 超时)返回 Err

write_register(reg_addr, data)

pub fn write_register(&self, reg_addr: u16, data: &[u8]) -> Result<()>

写入从站 ESC 寄存器(FPWR)。

参数:

  • reg_addr (u16) — 寄存器地址
  • data (&[u8]) — 写入数据

返回值:

  • Result<()> — 成功返回 Ok(())

示例:

let slave = master.slave(1);

// 读取 AL Status (0x0130, 2 字节)
let al_status = slave.read_register(0x0130, 2)?;
let state = u16::from_le_bytes([al_status[0], al_status[1]]);
println!("AL Status = 0x{:04X} (state={}, err={})",
state, state & 0x0F, (state & 0x10) != 0);

// 读取 AL Status Code (0x0134, 错误码)
let al_code = slave.read_register(0x0134, 2)?;
let code = u16::from_le_bytes([al_code[0], al_code[1]]);
println!("AL Status Code = 0x{:04X}", code);

// 写 AL Control = 0x04 (请求 SafeOp)
slave.write_register(0x0030, &[0x04, 0x00])?;

EEPROM (SII) 访问

读写从站 SII EEPROM (Slave Information Interface, ETG.1000.6 §6)。EEPROM 存储 vendor_id / product_code / revision_no / serial_no / SyncManager / FMMU / PDO 映射 / Strings 等设备身份与配置信息。SDK 在 config_init 阶段已自动读取,应用一般无需直接访问。

危险操作

EEPROM 写入慎用 — 写错可能导致从站身份信息错乱,严重时永久 brick 从站, 需厂家工具恢复。仅在以下场景使用:

  • 烧写 alias 地址(Hot-Connect 别名)
  • 修复出厂数据被误覆盖
  • 厂商授权的固件 / 参数烧录

EEPROM 写需要从站处于 Init / PreOp 状态,OP 状态下写入会被拒绝。

EEPROM 大小通常 1 KB - 16 KB(按 word 寻址,1 word = 2 byte)。起始 8 word 为厂商基本信息,之后是 Category 链表(Strings / General / FMMU / SyncM / TxPdo / RxPdo / DC / End=0xFFFF)。

read_eeprom(byte_offset, byte_length)

pub fn read_eeprom(&self, byte_offset: u16, byte_length: u16) -> Result<Vec<u8>>

读取从站 SII EEPROM 字节区域 (按 word 循环)。SDK 自动处理 BUSY 轮询、字对齐, 推荐应用层优先使用此高层 API。

参数:

  • byte_offset (u16) — 起始字节偏移 (建议偶数对齐)
  • byte_length (u16) — 读取字节数 (建议偶数)

返回值:

  • Result<Vec<u8>> — 成功返回 byte_length 字节数据

write_eeprom(byte_offset, data)

pub fn write_eeprom(&self, byte_offset: u16, data: &[u8]) -> Result<()>

写入从站 SII EEPROM 字节区域 (按 word 循环)。byte_offsetdata.len() 都必须是偶数。

参数:

  • byte_offset (u16) — 起始字节偏移 (必须偶数)
  • data (&[u8]) — 写入字节 (长度必须偶数)

返回值:

  • Result<()> — 全部 word 成功写入返回 Ok(()), 否则 Err(DarraError::InvalidParameter)

示例:

let slave = master.slave(1);

// 读 vendor_id (EEPROM 字节偏移 0x10, 长度 4)
let data = slave.read_eeprom(0x10, 4)?;
let vendor_id = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
println!("VendorID = 0x{:08X}", vendor_id);

// 读取头部 16 字节 (含 PDIControl/StationAlias 等)
let header = slave.read_eeprom(0, 16)?;
println!("EEPROM 头部: {:02X?}", header);

// 写 alias address (EEPROM 字节偏移 0x08)
// 必须从站处于 Init/PreOp 状态!
if slave.state() == Some(EcState::Init) {
slave.write_eeprom(0x08, &[0x01, 0x00])?; // alias = 1
}
API 选择
  • 首选: read_eeprom / write_eeprom (按字节, 自动字对齐)
  • 次选: slave.vendor_id() / product_id() / serial_number() 等已封装属性

DL Port 端口控制

直接读写 ESC 的 DL Port Control 寄存器 (0x0101),用于端口故障注入测试冗余/环拓扑的手动诊断

高级 API

正常运行时无需调用。大部分用户应该通过订阅 on_slave_port_link_changed 事件和读取 端口错误计数器 来诊断端口状态。仅在需要主动模拟端口故障(测试冗余切换)或排查特定端口问题时使用。

ESC 有 4 个物理端口 P0 / P1 / P2 / P3DL Port Control 寄存器的位定义如下:

DLPORT 值行为
0x00Auto — 所有端口由 ESC 自动管理(默认)
0x03关闭 P0
0x0C关闭 P1
0x30关闭 P2
0xC0关闭 P3

SDK 自动采用 primary → secondary → APWR 三级回退写入路径,即使 P0 关闭后仍能通过副网口/广播恢复。

write_dl_port(value)

pub fn write_dl_port(&self, value: u8) -> bool

写入从站 DL Port 控制寄存器(0x0101)。

参数:

  • value (u8) — DLPORT 值(见上表)

返回值:

  • bool — 成功返回 true

read_dl_port()

pub fn read_dl_port(&self) -> Option<u8>

读取从站 DL Port 控制寄存器的当前值。

返回值:

  • Option<u8>Some(dlport) 读取成功;None 读取失败(如从站离线)

示例:

// 模拟 P1 端口故障 (测试冗余切换)
let ok = slave.write_dl_port(0x0C);
println!("关闭 P1: {}", if ok { "成功" } else { "失败" });

// 读回确认
if let Some(dlport) = slave.read_dl_port() {
println!("当前 DLPORT = 0x{:02X}", dlport);
}

// 故障恢复后还原
slave.write_dl_port(0x00); // 恢复 Auto
配合冗余诊断

关闭一个端口后,观察 master.events().on_slave_port_link_changedmaster.diagnostics_info().break_point() 验证冗余切换是否生效。

Startup 配置

方法返回类型说明
startup()StartupParameterList启动参数列表
pdo_assignment_enabled() / set_pdo_assignment_enabled(bool)bool启动时是否写入 PDO Assignment,默认 true
pdo_configuration_enabled() / set_pdo_configuration_enabled(bool)bool启动时是否写入 PDO Configuration,默认 false
supports_complete_access() / set_supports_complete_access(bool)bool从站是否支持 SDO Complete Access

StartupParameterList 方法(通过 slave.startup() 获取):

  • add(param: StartupParameter) — 添加启动参数
  • add_sdo_write(index, sub_index, data, transition) — 添加 SDO 写入启动参数
  • remove(idx: usize) — 按列表下标移除启动参数,返回 Option<StartupParameter>
  • clear() — 清除所有启动参数
  • count() / params() — 参数数量 / 参数切片
  • sort_by_priority() — 按优先级排序
  • sync_to_dll() — 把列表同步到 DLL,返回 Result<i32>
  • apply(transition: u8, timing: u8) — 应用启动参数,返回 Result<i32>
  • apply_single(param: &StartupParameter) — 应用单条参数
slave.startup() 每次返回新实例

slave.startup() 每次调用都返回一个新构造StartupParameterList。要让 add() 的参数真正下发,需在同一个绑定变量上操作后调用 sync_to_dll() / apply(),或直接用 slave.add_startup_parameter(...) 把参数加到从站。

StartupParameter 结构(注意 transition / write_timing枚举,不是字符串):

pub struct StartupParameter {
pub index: u16, // SDO 索引
pub sub_index: u8, // SDO 子索引
pub data: Vec<u8>, // 写入数据
pub transition: StartupTransition, // 状态转换阶段 (枚举)
pub write_timing: StartupWriteTiming, // 写入时机 (枚举)
pub priority: i32, // 优先级 (数值越小越优先)
pub complete_access: bool, // 完整访问模式
pub description: String, // 描述信息
}

示例:

use darra_ethercat::{StartupParameter, StartupTransition};

// 用关联构造函数 (transition 为枚举)
let param = StartupParameter::with_transition(
0x1C12, 0, vec![0], StartupTransition::PS);

let mut list = slave.startup();
list.add(param);
list.sync_to_dll()?; // 同步到 DLL 后才生效

StartupTransition 枚举值(默认写入时机):

  • IP — Init → PreOp,默认 AfterTransition
  • PS — PreOp → SafeOp,默认 BeforeTransition
  • SO — SafeOp → Op,默认 BeforeTransition
  • OS — Op → SafeOp,默认 AfterTransition
  • SP — SafeOp → PreOp,默认 AfterTransition
  • PI — PreOp → Init,默认 BeforeTransition

子对象访问

Rust SDK 在 Slave 上仅暴露两个子对象访问器, 其余字段通过扁平的属性方法直接读取 (见上文"属性"表):

slave.dc()

pub fn dc(&self) -> crate::slave::dc::SlaveDC

返回 SlaveDC 高级 DC 接口, 提供 configure / configure_mode / disable / has_dc / propagation_delay / sync_window_status 等方法。

let dc = slave.dc();
if dc.has_dc() {
// configure(sync0_ns, sync1_ns, shift_ns: Option<i32>)
dc.configure(1_000_000, 0, Some(0))?; // 1ms SYNC0, 无 SYNC1, 0 shift
println!("传播延迟 {} ns", dc.propagation_delay());
}

slave.identity()

pub fn identity(&self) -> Result<SlaveIdentity>

返回 vendor/product/revision/serial 四元组。比单独调 vendor_id() / product_id() / rev_id() / serial_number() 更方便, 错误统一为 Result

let id = slave.identity()?;
println!("0x{:08X} 0x{:08X} 0x{:08X} 0x{:08X}",
id.vendor_id, id.product_code, id.revision_no, id.serial_no);
Rust 特有语法糖

Rust 提供 From<&Slave> for SlaveIdentity, 一行 let id: SlaveIdentity = (&slave).into(); 直接拿身份四元组, 不必逐字段 unwrap. 详见 derive 类型转换.

Rust SDK 不提供 SII / SyncManager 子对象

C# / Java 中存在的 slave.sii() / slave.mailbox() / slave.topology() / slave.metadata() / slave.runtime() / slave.sm_fmmu() / slave.proto_mbx() / slave.capabilities() 等 sub-struct 子对象, Rust 实现选择了扁平方法: 全部字段通过 slave.input_bits() / mbx_protocol() / physical_type() / topology() (返回拓扑类型 u8) / coe_details() 等直接读取, 与 Rust 习惯的 zero-cost accessor 对齐。

具体映射:

  • C# slave.Topology (sub-object) → Rust slave.topology() / parent_station() / entry_port() / active_ports() / physical_type() / child_count() / children()
  • C# slave.Mailbox → Rust slave.mbx_length() / mbx_protocol() / mbx_read_length() / mbx_write_offset() / mbx_read_offset()
  • C# slave.SmFmmu → Rust slave.fmmu0_func() / fmmu1_func() / fmmu2_func() / fmmu3_func() / sync_manager_count()
  • C# slave.Capabilities → Rust slave.coe_details() / eoe_details() / foe_details() / soe_details() / has_mdp() / supports_frame_repeat()
  • C# slave.Sii.read_word/write_word/read_category → Rust SDK 未导出 SII 直读 API, 通过 slave.detailed_info() 获取已解析的 EEPROM 字段。

如需访问完整原始结构体, 使用 slave.detailed_info() 返回的 SlaveDetailedInfo

Rust 特有语法糖

扁平方法读到的 raw 字节也可一行转枚举: let st: EcState = slave.state_raw().into(); / let lk: LinkState = raw.into();, 由 sugar 模块的 From<u8> 派生. 详见 derive 类型转换.

启动配置自动能力探测

pub fn capabilities(&self) -> StartupAutoCapabilities

注: 此 capabilities() 来自 crate::slave::startup 模块, 返回启动自动配置能力集合 (含 pdo_assignment / pdo_configuration / complete_access 等启动期能力布尔位), 与 C# Slave.Capabilities 子对象语义不同 — 前者是"启动器自动判定的能力", 后者是"SII EEPROM 协议详情位"。Rust SDK 中后者直接走 slave.coe_details() 等扁平方法。