跳到主要内容

MDP — 模块化设备

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

通过 slave.GetMDP() 获取 MDP* 指针。从站不支持 MDP 时返回 nullptr,使用前必须判空。

自动编排 (2.5.x 新增)

进入 OP 时,master.SetState(EcState::OP) 会自动触发 MDP 模块化设备编排: 检测物理模块、写入已配置模块列表 (0xF030)、配置对应 SM/PDO。 GCAN-8200 等 MDP 设备无需手动写 0xF030,避免 SM3 length=0 导致进 SafeOp 报 AL=0x001E。 绝大多数场景下, 用户无需直接操作 MDP 类。

关于热插拔

SDK 支持 MDP 模块热插拔再配置自修复。 但生产环境中几乎不会出现 MDP 模块热插拔的使用场景。 EtherCAT 从站的模块配置在设备上电后即固定, 运行期间不会动态变更。 部署 MDP 模块化设备推荐使用 DENI。

获取 MDP 实例

auto& slave = master.GetSlave(1);
MDP* mdp = slave.GetMDP(); // 从站不支持 MDP 时返回 nullptr
if (!mdp) {
printf("从站 1 不是 MDP 模块化设备\n");
return;
}

GetMDP() 仅在 slave.HasMDP() 为真时返回非空指针,且实例延迟创建、后续访问返回同一缓存对象。

属性

ConfiguredModules()

std::vector<MdpSlotInfo> ConfiguredModules() const;

已配置模块列表 (对象索引 0xF030)。通过 native MDPGetConfigModuleList 直读各槽位模块标识码。

返回值:

  • std::vector<MdpSlotInfo> — 已配置模块列表

相关结构:

struct MdpSlotInfo {
int SlotIndex; // 槽位索引 (从 1 开始)
uint32_t ModuleIdent; // 模块标识码 (32 位)
std::string ModuleName; // 模块名称
uint32_t ModuleType; // 模块设备类型码
std::string Status; // 状态 ("已配置" / "已检测")
uint16_t ObjectDictionaryIndex; // 对象字典基地址
std::string ModuleIOType; // I/O 类型 (默认 "Unknown")
std::string ToString() const; // 格式化为 "Slot N: 名称 (0xIDENT)"
};

示例:

MDP* mdp = slave.GetMDP();
if (!mdp) return;
for (auto& slot : mdp->ConfiguredModules()) {
printf("%s\n", slot.ToString().c_str());
}

DetectedModules()

std::vector<MdpSlotInfo> DetectedModules() const;

已检测模块列表 (对象索引 0xF050), 反映物理上实际插入的模块。通过 native MDPGetDetectedModuleList 直读。

返回值:

  • std::vector<MdpSlotInfo> — 已检测模块列表 (同 ConfiguredModules 结构)

示例:

MDP* mdp = slave.GetMDP();
if (mdp) {
for (auto& slot : mdp->DetectedModules())
printf("Slot %d: %s\n", slot.SlotIndex, slot.ModuleName.c_str());
}

GetAvailableModules()

std::vector<MdpAvailableModule> GetAvailableModules() const;

可用模块列表,由 DetectedModules() 派生 (每个已检测模块映射为一条可用模块记录)。

返回值:

  • std::vector<MdpAvailableModule> — 可用模块列表

相关结构:

struct MdpAvailableModule {
uint32_t ModuleIdent; // 模块标识码
uint32_t ModuleType; // 模块设备类型码
std::string ModuleName; // 模块名称
std::string Description; // 模块描述
};

示例:

MDP* mdp = slave.GetMDP();
if (mdp) {
for (auto& mod : mdp->GetAvailableModules())
printf("%s (0x%08X)\n", mod.ModuleName.c_str(), mod.ModuleIdent);
}

ModuleIndexDistance() / MaxModuleCount()

uint16_t ModuleIndexDistance() const;   // 0xF000:01, 模块索引间距, 默认 0x0008
uint16_t MaxModuleCount() const; // 0xF000:02, 最大模块数量

读取 ETG.5001 Modular Device Profile 对象 0xF000 的核心属性,结果带缓存。

方法

GetModulePdoLayout()

std::vector<MdpModulePdoInfo> GetModulePdoLayout() const;

获取各模块在从站 IOmap 中的 PDO 布局。通过 CoE SDORead 读取 PDO Assignment (0x1C12/0x1C13) 和 PDO Mapping 条目, 累积计算各模块的字节偏移。

备注

需要从站已完成 DENI 配置 (ConfigMap 后可用), 不依赖 ESI 文件。CoE 不可用或未检测到模块时返回空向量。

返回值:

  • std::vector<MdpModulePdoInfo> — 各模块的 PDO 布局信息, 无法获取时返回空向量

相关结构:

struct MdpModulePdoInfo {
int SlotIndex; // 槽位索引
uint32_t InputOffset; // 输入 PDO 偏移
uint16_t InputSize; // 输入 PDO 字节数
uint32_t OutputOffset; // 输出 PDO 偏移
uint16_t OutputSize; // 输出 PDO 字节数
};

示例:

MDP* mdp = slave.GetMDP();
if (mdp) {
for (auto& mod : mdp->GetModulePdoLayout())
printf("Slot %d: In=%uB @%u, Out=%uB @%u\n",
mod.SlotIndex, mod.InputSize, mod.InputOffset,
mod.OutputSize, mod.OutputOffset);
}

Discover()

std::vector<MdpDiscoveredModule> Discover() const;

通过 DLL C 高级 API 快速发现 MDP 模块,返回各槽位的模块 Profile / 标识码 / PDO 偏移信息。

相关结构:

struct MdpDiscoveredModule {
uint16_t SlotIndex; // 槽位索引
uint16_t ModuleProfile; // 模块 Profile
uint16_t ModuleIdent; // 模块标识码
uint16_t PdoInputOffset; // 输入 PDO 偏移
uint16_t PdoInputSize; // 输入 PDO 字节数
uint16_t PdoOutputOffset; // 输出 PDO 偏移
uint16_t PdoOutputSize; // 输出 PDO 字节数
};

DetectModules() / IsModuleConfigConsistent()

bool DetectModules() const;             // 触发从站重新扫描已安装模块 (写 0xF002:00)
bool IsModuleConfigConsistent() const; // 已配置模块与已检测模块是否一致

AutoConfigureFromDetectedModules()

int AutoConfigureFromDetectedModules() const;

将已检测模块 (0xF050) 写入已配置模块列表 (0xF030) 完成自动配置。

返回值:

  • int — 成功配置的模块数量,-1 表示失败
提示

进入 OP 时 master.SetState() 已自动执行 MDP 编排,通常无需手动调用 AutoConfigureFromDetectedModules()

自动配置

MDP 设备进入 OP 时由 master.SetState(EcState::OP) 自动完成模块编排 (检测 → 写 0xF030 → 配置 SM/PDO)。 推荐部署方式:

  • 使用 DENI — Darra 配置工具导出,master.SetENI("xxx.deni") 加载即包含完整 MDP 配置
ESI 加载边界

C/C++ SDK 不提供运行期 ESI 文件加载接口 (EtherCATMaster 没有 SetEsiFile() / SetEsiFiles() 成员)。C++ SDK 中从站信息通过扫描自动从 EEPROM 读取, 或通过 SetENI() 加载的 ENI/DENI 配置文件获取。ESI 文件管理是 C# 层 EsiManager 的功能。