MDP — 模块化设备
Modular Device Profile (MDP, ETG.5001) 是 EtherCAT 中描述模块化设备 (槽位 + 可插拔模块) 的标准协议。
通过 slave.mdp 访问 (返回 MdpDiscovery)。从站不支持 MDP 时为 None。
进入 OP 时, SDK 会对模块化设备 (如 GCAN-8200) 自动完成模块检测与 PDO 配置。
绝大多数场景下用户无需直接操作 MDP — 通过 master.enable_auto_startup() 即可。
SDK 支持 MDP 模块热插拔再配置自修复。
但生产环境中几乎不会出现 MDP 模块热插拔的使用场景。
部署 MDP 模块化设备推荐使用 DENI;
如需自动配置,请先导入对应的 ESI 文件,再调用主站 enable_auto_startup() 或从站级配置。
属性
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 模块列表 | configured_modules | List[MdpSlotInfo] | 只读 | 已配置模块列表 (转调 native MDPGetConfigModuleList, 0xF030) |
| detected_modules | List[MdpSlotInfo] | 只读 | 已检测模块列表, 反映物理实际插入 (转调 native MDPGetDetectedModuleList, 0xF050) | |
| config_module_idents | List[int] | 只读 | 从 native 直读 0xF030 ConfigModuleIdent 数组 | |
| detected_module_idents | List[int] | 只读 | 从 native 直读 0xF050 DetectedModuleIdent 数组 | |
| modules | List[MdpModule] | 只读 | 最近一次 discover() 的结果快照 | |
| 设备属性 | module_index_distance | int | 只读 | 模块对象字典索引间距 (0xF000:01, 默认 0x0800) |
| max_module_count | int | 只读 | 设备支持的最大模块数量 (0xF000:02) |
MdpSlotInfo 类
模块槽位信息 (ETG.5001), 普通类:
class MdpSlotInfo:
slot_index: int # 槽位索引 (从 1 开始)
module_ident: int # 模块标识码 (32 位)
module_name: str # 模块名称 (形如 "Module 0x........")
module_type: int # 模块设备类型码 (module_ident 低 16 位)
status: str # 状态 ("已配置" / "已检测")
object_dictionary_index: int # 模块对象字典基地址 (= index_distance × slot_index)
module_io_type: str # I/O 类型 ("Input" / "Output" / "I/O" / "Unknown")
示例:
for slot in slave.mdp.configured_modules:
print(f"Slot {slot.slot_index}: {slot.module_name} (0x{slot.module_ident:08X})")
for slot in slave.mdp.detected_modules:
print(f"Slot {slot.slot_index}: 检测到模块 0x{slot.module_ident:08X}")
MdpModule 类
discover() 返回的模块信息:
class MdpModule:
slot: int # 槽位号
profile_id: int # 模块 Profile ID (raw 高 16 位)
vendor_id: int # 厂商 ID (0xF051)
module_type: int # 模块类型 (raw 低 16 位)
revision: int # 修订号 (0xF052)
方法
discover()
def discover(self) -> List[MdpModule]
执行 MDP 模块发现, 通过 CoE 读取 0xF050 模块列表对象, 返回 MdpModule 列表。
rescan()
def rescan(self) -> List[MdpSlotInfo]
重新扫描已配置 (0xF030) 与已检测 (0xF050) 模块列表, 清空索引间距 / 最大模块数缓存, 返回已检测模块列表。
check_module_match()
def check_module_match(self) -> Tuple[bool, int]
转调 native MDPCheckModuleMatch, 检查已配置模块与已检测模块是否一致。
返回值:
Tuple[bool, int]—(is_match, first_mismatch_slot)。is_match=True时全部匹配;False时第二项为首个不匹配槽位 (无法判定返回 -1)
示例:
ok, mismatch_slot = slave.mdp.check_module_match()
if not ok:
print(f"模块配置不一致, 首个不匹配槽位: {mismatch_slot}")
get_module_pdo_layout()
def get_module_pdo_layout(self) -> Optional[List[MdpModulePdoInfo]]
获取各模块在从站 IOmap 中的 PDO 布局 (输入/输出字节数和偏移)。通过 CoE 读取 PDO Assignment (0x1C12/0x1C13) 与 PDO Mapping 累积计算。无可用模块或读取失败返回 None。
MdpModulePdoInfo 类:
class MdpModulePdoInfo:
slot_index: int # 槽位索引
input_offset: int # 输入 PDO 在 IOmap 中的字节偏移
input_size: int # 输入字节数
output_offset: int # 输出 PDO 在 IOmap 中的字节偏移
output_size: int # 输出字节数
示例:
layout = slave.mdp.get_module_pdo_layout()
if layout:
for info in layout:
print(f"Slot {info.slot_index}: "
f"输入 {info.input_size}B @{info.input_offset}, "
f"输出 {info.output_size}B @{info.output_offset}")
auto_configure_from_detected_modules()
def auto_configure_from_detected_modules(self) -> int
根据检测到的模块自动配置 — 读取 0xF050 (已检测模块), 逐个写入 0xF030 (已配置模块), 使已配置列表与物理实际一致。
返回值:
int— 成功配置的模块数量;0表示无检测到的模块;-1表示写入失败
示例:
n = slave.mdp.auto_configure_from_detected_modules()
if n > 0:
print(f"已按物理实际配置 {n} 个模块")
elif n < 0:
print("模块配置写入失败")
自动配置
MDP 设备的自动配置需要先导入对应的 ESI 文件:
- 导入 ESI (主站) —
master.set_esi_file()/master.set_esi_files() - 标记自动配置 (主站) —
master.enable_auto_startup()(在build()时执行) - 导入 ESI (从站) —
slave.set_esi_file()
链式示例:
from darra_ethercat import EtherCATMaster, EcState
master = (EtherCATMaster()
.set_network(r"\\Device\\NPF_{GUID}")
.set_esi_files(r"C:\EtherCAT\ESI")
.enable_auto_startup() # 进 OP 时自动编排模块化设备
.build())
master.set_state(EcState.OP)