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 文件,由以下方法完成:
- 导入 ESI —
EsiLoader::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);
}
}
}
}
}