跳到主要内容

MDP — 模块化设备

Modular Device Profile (MDP) 是 EtherCAT 中描述模块化设备的标准协议。

通过 mdp_discover() / mdp_discover_safety() 模块级函数访问。

自动处理

内部会自动处理 MDP 设备的模块检测和 PDO 配置。 绝大多数场景下,用户无需直接操作 MDP。

属性

configured_modules()

pub fn configured_modules(&self) -> Vec<MdpSlotInfo>

已配置模块列表。每次访问实时通过 CoE 读取(索引 0xF030)。

detected_modules()

pub fn detected_modules(&self) -> Vec<MdpSlotInfo>

已检测模块列表,反映物理上实际插入的模块(索引 0xF050)。

available_modules()

pub fn available_modules(&self) -> Vec<MdpAvailableModule>

ESI 中定义的可选模块列表。

pub struct MdpSlotInfo {
pub slot_index: i32, // 槽位索引(从 1 开始)
pub module_ident: u32, // 模块标识码
pub module_name: String, // 模块名称
pub module_type: u32, // 模块设备类型码
pub status: String, // 状态("已配置" / "已检测")
pub object_dictionary_index: u16, // 对象字典基地址
pub module_io_type: String, // I/O 类型
}

pub struct MdpAvailableModule {
pub module_ident: u32, // 模块标识码
pub module_type: u32, // 模块设备类型码
pub module_name: String, // 模块名称
pub description: String, // 模块描述
}

示例:

use ethercat::{mdp_discover, mdp_is_mdp_device};

if mdp_is_mdp_device(master.index(), 1) {
let modules = mdp_discover(master.index(), 1)?;
for m in &modules {
println!("{}", m); // Display: "模块[01] 分类:数字量输入 VID:0x..."
}
}

方法

get_module_pdo_layout()

pub fn get_module_pdo_layout(&self) -> Option<Vec<MdpModulePdoInfo>>

获取各模块在从站 IOmap 中的 PDO 布局。

pub struct MdpModulePdoInfo {
pub slot_index: i32, // 槽位索引
pub input_offset: u32, // 输入 PDO 偏移
pub input_size: u16, // 输入 PDO 字节数
pub output_offset: u32, // 输出 PDO 偏移
pub output_size: u16, // 输出 PDO 字节数
}

示例:

if let Some(layout) = mdp.get_module_pdo_layout() {
for m in &layout {
println!("Slot {}: In={}B @{}, Out={}B @{}",
m.slot_index, m.input_size, m.input_offset,
m.output_size, m.output_offset);
}
}

自动配置

MDP 设备的自动配置需要先导入对应的 ESI 文件,由以下方法完成:

  • 导入 ESIEsiLoader::load_path() / EsiLoader::add_file()
  • 自动配置(主站)master.enable_auto_startup()
  • 自动配置(从站)slave.auto_startup()
关于热插拔

SDK 支持 MDP 模块热插拔再配置自修复。 但生产环境中几乎不会出现 MDP 模块热插拔的使用场景。 EtherCAT 从站的模块配置在设备上电后即固定,运行期间不会动态变更。 部署 MDP 模块化设备推荐使用 DENI; 如需自动配置,请先导入对应的 ESI 文件,再调用 auto_startup()

完整示例

use ethercat::{EtherCATMaster, EsiLoader, mdp_discover, mdp_is_mdp_device};

// 加载 ESI 文件
EsiLoader::preload_default(None);

let mut master = EtherCATMaster::from_json_file("config.json")?;
master.set_state(EcState::PreOp)?;

let slave_count = master.slave_count();
for i in 1..=slave_count {
if mdp_is_mdp_device(master.index(), i as u16) {
println!("从站 {} 是 MDP 设备", i);

// 发现模块
let modules = mdp_discover(master.index(), i as u16)?;
for m in &modules {
println!(" {}", m);
}

// 获取 PDO 布局
let slave = master.get_slave(i as u16);
if let Some(mdp) = slave.mdp() {
// 已配置模块
for slot in mdp.configured_modules() {
println!(" 已配置: Slot {} - {} (0x{:08X})",
slot.slot_index, slot.module_name, slot.module_ident);
}

// 已检测模块
for slot in mdp.detected_modules() {
println!(" 已检测: Slot {} - {}", slot.slot_index, slot.module_name);
}

// 可用模块(ESI 定义)
for m in mdp.available_modules() {
println!(" 可选: {} - {}", m.module_name, m.description);
}

// PDO 布局
if let Some(layout) = mdp.get_module_pdo_layout() {
for m in &layout {
println!(" PDO: Slot {} In={}B @{}, Out={}B @{}",
m.slot_index, m.input_size, m.input_offset,
m.output_size, m.output_offset);
}
}
}
}
}