← 返回首页

CATIA V5 CAA 命令、工作台与 Add-in 开发指南

本文讨论一个 CAA 开发中很常用的部分:如何把自己的功能接入 CATIA V5 的交互界面,让用户可以在工作台中点到命令、选择对象、输入参数、确认创建,并且在菜单、工具栏、资源文件和运行时加载机制之间保持一致。

如果说自定义特征回答的是“我要在模型里创造什么对象”,那么命令和工作台接入回答的是“用户如何在 CATIA 里自然地使用它”。在实际项目中,这两部分经常绑定出现:一个新几何特征需要创建命令,一个分析工具需要选择命令,一个批处理能力也可能需要放到工具栏或菜单中。

1. 先建立三层心智模型

CAA 里的一个可点击功能通常由三层组成:

层次 典型类/对象 作用
命令实现 CATCommand

CATStateCommandCATDlgDialog 派生类
真正执行业务逻辑:选择对象、弹出面板、创建特征、修改文档。
命令头 CATCommandHeader

 派生类或 MacDeclareHeader 生成类
把一个菜单项/按钮和某个 DLL 中的某个命令类绑定起来,同时关联标题、图标和帮助资源。
入口布局 CATCmdWorkbench

CATCmdContainerCATCmdStarter
描述命令出现在什么工作台、工具栏、菜单、子菜单或分隔符之后。

很多初学者会把“命令类”和“工具栏按钮”混在一起看。更稳妥的理解是:按钮本身不是命令,按钮是一个 CATCmdStarterCATCmdStarter 指向命令头;命令头知道要加载哪个库、实例化哪个命令类;命令类才真正运行。

典型调用链如下:

用户点击工具栏按钮
  -> CATCmdStarter
  -> CATCommandHeader
  -> 加载命令所在模块
  -> 实例化 CATCommand/CATStateCommand 派生类
  -> 命令开始接收交互事件并修改文档

2. 选择命令类型

CAA 文档把用户可见的命令大致分为三类。选择哪一类,取决于用户是否需要继续交互。

场景 推荐命令类型 说明
点击后立即执行 CATCommand

 派生类
例如 Update All、切换显示状态、执行一次检查。
需要固定面板输入参数 CATDlgDialog

 或对话框命令
例如设置选项、输入若干参数后 OK/Apply。
需要选择对象、按步骤输入、反复预览 CATStateCommand

 派生类
例如选择曲线、选择方向、输入半径、确认创建特征。

在机械/几何 CAA 开发中,最常见也最值得掌握的是 CATStateCommand。它把交互过程建模为状态机:每个状态收集一类输入,输入满足条件后通过 transition 进入下一个状态,并执行 action。

例如一个“创建测量特征”的命令可以设计为:

初始状态:选择被测几何
  -> 选择有效后保存输入
第二状态:选择测量模式或输入参数
  -> 参数有效后预览或创建
结束状态:调用 factory 创建特征并插入模型

3. 写一个 State Command 骨架

一个 state command 需要继承 CATStateCommand,声明资源文件,覆盖 BuildGraph,并根据需要实现生命周期函数。

头文件骨架可以类似这样:

#include"CATStateCommand.h"
#include"CATBoolean.h"

classCATPathElementAgent;
classCATDialogAgent;
classCATNotification;

class MyCompanyCreateMeasureCmd : public CATStateCommand
{
CmdDeclareResource(MyCompanyCreateMeasureCmd, CATStateCommand);

public:
MyCompanyCreateMeasureCmd();
virtual ~MyCompanyCreateMeasureCmd();

virtualvoidBuildGraph();

CATStatusChangeRC Activate(CATCommand *cmd, CATNotification *notif);
CATStatusChangeRC Desactivate(CATCommand *cmd, CATNotification *notif);
CATStatusChangeRC Cancel(CATCommand *cmd, CATNotification *notif);

private:
CATBoolean CheckSelectedObject(void *data);
CATBoolean StoreSelectedObject(void *data);
CATBoolean CreateMeasureFeature(void *data);

private:
  CATPathElementAgent *_selectionAgent;
  CATDialogAgent *_okAgent;
};

实现类通常是一个普通 CAA implementation class:

#include"MyCompanyCreateMeasureCmd.h"
#include"CATPathElementAgent.h"
#include"CATDialogState.h"

CATImplementClass(
  MyCompanyCreateMeasureCmd,
  Implementation,
  CATStateCommand,
  CATNull);

MyCompanyCreateMeasureCmd::MyCompanyCreateMeasureCmd()
  : CATStateCommand("MyCompanyCreateMeasureCmd"),
    _selectionAgent(NULL),
    _okAgent(NULL)
{
}

MyCompanyCreateMeasureCmd::~MyCompanyCreateMeasureCmd()
{
  _selectionAgent = NULL;
  _okAgent = NULL;
}

BuildGraph 是 state command 的核心。它创建状态、创建 dialog agent,并把状态之间的条件和动作串起来。

void MyCompanyCreateMeasureCmd::BuildGraph()
{
  CATDialogState *selectState = GetInitialState("SelectInputState");

  _selectionAgent = newCATPathElementAgent("SelectInputAgent");
  selectState->AddDialogAgent(_selectionAgent);

AddTransition(
    selectState,
NULL,
IsOutputSetCondition(_selectionAgent),
Action((ActionMethod)&MyCompanyCreateMeasureCmd::StoreSelectedObject));
}

实际项目会比这个例子多几件事:

  • 给 CATPathElementAgent 设置过滤条件,只接受目标接口、目标 StartUp 或目标几何类型。

  • 从 agent 的输出中取得 CATPathElement,再沿路径找到模型对象或 CATBaseUnknown

  • 在 action 中只保存选择结果,不要把所有业务逻辑塞进 guard condition。

  • 在命令结束前统一调用 factory 或服务类修改文档。

  • 对创建、替换、删除这类文档修改考虑 undo/redo 和 warm start。

4. 命令生命周期和运行模式

CAA 命令运行在 command tree 中,同一时刻只有一个面向用户的 dialog command 拿到 focus。命令启动模式会影响它和已有命令的关系:

模式 常量 适用场景
Exclusive CATCommandModeExclusive 创建、编辑、选择这类主命令。启动时会取消当前命令栈中的其他命令。
Shared CATCommandModeShared 可以和已有命令短暂共存的辅助命令。
Undefined CATCommandModeUndefined 不由 command selector 管理的内部命令;state command 不适合设为 undefined。

常见生命周期函数含义如下:

函数 调用时机 建议处理
构造函数 命令实例化时 初始化成员、设置命令模式、准备轻量状态。
BuildGraph state command 构建状态图时 创建 state、agent、transition、condition、action。
Activate 命令取得 focus 时 刷新提示、恢复临时显示、激活面板。
Desactivate shared 命令抢占 focus 时 暂停高亮、临时预览或面板响应。
Cancel 命令结束或被 exclusive 命令取消时 清理临时几何、释放选择状态、关闭面板。

如果命令会修改 CATPart 或 CATProduct,不要只关心“点 OK 后成功”。还要验证这些情况:取消命令是否清理预览,切换工作台是否不会残留 agent,选择另一个 exclusive 命令时是否能安全退出,撤销/重做后模型是否一致。

5. 用 Command Header 让命令可被启动

命令类写好后,还不能被工具栏直接调用。你需要在工作台或 add-in 的 CreateCommands 中创建 command header。

官方示例常用 MacDeclareHeader 快速生成命令头类:

#include"CATCommandHeader.h"
MacDeclareHeader(MyCompanyWorkbenchHeader);

然后在 CreateCommands 中为每个命令实例化一个 header:

void MyCompanyWorkbench::CreateCommands()
{
newMyCompanyWorkbenchHeader(
"MyCompanyCreateMeasureHdr",
"MyCompanyCommands",
"MyCompanyCreateMeasureCmd",
    (void *)NULL);
}

四个参数分别是:

参数 含义
MyCompanyCreateMeasureHdr command header 实例 ID。后续 SetAccessCommand 和资源 key 都要用它。
MyCompanyCommands 命令实现所在共享库/DLL 名,不写 lib 前缀,也不写平台后缀。
MyCompanyCreateMeasureCmd 要实例化的命令类名。
(void *)NULL 可选启动参数;同一个命令类服务多个动作时可传入动作 ID。

如果需要传整数参数,在 64 位环境中不要直接强转,应使用文档示例中的 CATINT32ToPtr 这类宏,避免指针宽度问题。

6. 把命令放进 Workbench

如果你拥有目标 workbench 的源代码,或者要创建一个完全独立的工作台,可以在 workbench 描述类中实现两件事:

  1. CreateCommands

    :创建 command headers。

  2. CreateWorkbench

    :创建工作台、工具栏、菜单和命令入口。

一个简化的工具栏布局如下:

CATCmdWorkbench *MyCompanyWorkbench::CreateWorkbench()
{
NewAccess(CATCmdWorkbench, workbench, MyCompanyWorkbench);

NewAccess(CATCmdContainer, measureToolbar, MyCompanyMeasureTlb);
SetAccessChild(workbench, measureToolbar);
AddToolbarView(measureToolbar, 1, Right);

NewAccess(CATCmdStarter, createMeasureStarter, MyCompanyCreateMeasureStr);
SetAccessCommand(createMeasureStarter, "MyCompanyCreateMeasureHdr");
SetAccessChild(measureToolbar, createMeasureStarter);

return workbench;
}

几个标识符要分清:

标识符 示例 用在哪里
Workbench ID MyCompanyWorkbench NewAccess(CATCmdWorkbench, ...)

,也是 workbench 资源文件名基础。
Toolbar ID MyCompanyMeasureTlb 工具栏资源、默认显示位置、用户自定义布局。
Starter ID MyCompanyCreateMeasureStr 工具栏/菜单中的具体入口资源。
Header ID MyCompanyCreateMeasureHdr SetAccessCommand

 和 command header 资源。

菜单和子菜单也使用 CATCmdContainer,菜单项仍然使用 CATCmdStarter。工具栏和菜单可以指向同一个 command header,因此同一个命令可以同时出现在 toolbar 和 menu 中,而不需要写两份命令类。

7. 用 Add-in 接入已有 Workbench

如果你不想修改已有 workbench,通常应使用 add-in。Add-in 的好处是解耦:目标 workbench 暴露一个 add-in interface,你实现这个接口,把自己的工具栏和命令注册进去。

一个 add-in 描述类通常像这样:

#include"CATBaseUnknown.h"

classCATCmdContainer;

classMyCompanyMeasureAddin : public CATBaseUnknown
{
  CATDeclareClass;

public:
MyCompanyMeasureAddin();
virtual ~MyCompanyMeasureAddin();

voidCreateCommands();
CATCmdContainer *CreateToolbars();
};

实现时它是某个 late type 的 DataExtension,并通过 TIE 声明实现目标 workbench 暴露的 add-in interface:

#include"MyCompanyMeasureAddin.h"
#include"CATCommandHeader.h"
#include"CATCreateWorkshop.h"

MacDeclareHeader(MyCompanyMeasureAddinHeader);

CATImplementClass(
  MyCompanyMeasureAddin,
  DataExtension,
  CATBaseUnknown,
  MyCompanyMeasureAddinComponent);

#include"TIE_MyTargetWorkbenchAddin.h"
TIE_MyTargetWorkbenchAddin(MyCompanyMeasureAddin);

dictionary 中要声明 component late type 实现了 add-in interface:

MyCompanyMeasureAddinComponent MyTargetWorkbenchAddin libMyCompanyMeasureAddin

CreateToolbars 中创建 toolbar、starter,并用 SetAccessCommand 指向 command header:

CATCmdContainer *MyCompanyMeasureAddin::CreateToolbars()
{
NewAccess(CATCmdContainer, measureToolbar, MyCompanyMeasureAddinTlb);

NewAccess(CATCmdStarter, createMeasureStarter, MyCompanyCreateMeasureStr);
SetAccessCommand(createMeasureStarter, "MyCompanyCreateMeasureHdr");
SetAccessChild(measureToolbar, createMeasureStarter);

AddToolbarView(measureToolbar, -1, Right);

return measureToolbar;
}

注意 add-in 的能力边界:它适合增加工具栏和有限的菜单入口,但不应该假设能任意重排目标 workbench 的菜单结构。对于已有菜单,通常只能按目标框架允许的方式追加入口。

8. 准备 CATNls、CATRsc 和图标

命令能启动只是第一步。要让用户看到正常标题、提示和图标,还要提供资源文件。

8.1 Command Header 资源

如果 header 类名为 MyCompanyWorkbenchHeader,则资源文件通常是:

MyCompanyWorkbenchHeader.CATNls
MyCompanyWorkbenchHeader.CATRsc

放置在:

YourFramework/CNext/resources/msgcatalog

CATNls 中常见 key 由“header 类名 + header 实例 ID + 资源项”拼成:

MyCompanyWorkbenchHeader.MyCompanyCreateMeasureHdr.Title     = "Measure Feature";
MyCompanyWorkbenchHeader.MyCompanyCreateMeasureHdr.ShortHelp = "Measure Feature";
MyCompanyWorkbenchHeader.MyCompanyCreateMeasureHdr.Help      = "Create a persistent measure feature";
MyCompanyWorkbenchHeader.MyCompanyCreateMeasureHdr.Category  = "MyCompany";

CATRsc 中常用于指定图标、快捷键、助记键等非翻译资源。图标文件通常进入 CNext/resources/graphic,并随运行时视图复制到对应 OS 目录。

8.2 State Command 资源

CATStateCommand 自己也可以有资源文件,例如:

MyCompanyCreateMeasureCmd.CATNls

它用于命令状态提示、undo prompt 或面板文案。因为命令类中写了:

CmdDeclareResource(MyCompanyCreateMeasureCmd, CATStateCommand);

所以运行时会按命令类名查找资源。状态 ID 也应保持稳定,方便 NLS 资源和代码对应。

8.3 Workbench/Add-in 资源

Workbench、toolbar、menu container 也需要自己的资源。比如 MyCompanyWorkbench 对应:

MyCompanyWorkbench.CATNls
MyCompanyWorkbench.CATRsc

Add-in 通常也会有自己的 .CATNls,用于工具栏标题等。

资源相关路径常见如下:

类型 开发态路径 运行时环境变量
.CATNls

.CATRsc
CNext/resources/msgcatalog CATMsgCatalogPath
图标、.CATfct 等 graphic 资源 CNext/resources/graphic CATGraphicPath
interface dictionary CNext/code/dictionary CATDictionaryPath

9. 把命令和自定义特征串起来

如果这篇和“自定义特征”一起看,一个完整功能通常会这样拆分:

模块 职责
StartUp .CATfct 定义持久化对象、输入、输出和属性。
类型接口 DataExtension 封装 StartUp 属性访问。
Factory DataExtension 在 CATPrtCont 或目标容器中实例化特征。
Build/Replace/Edit 行为 让特征参与 update、替换、编辑和规格树行为。
State Command 负责用户选择、参数面板、预览、确认/取消。
Workbench/Add-in 负责把命令放到 CATIA UI 中。
资源文件 负责标题、帮助、图标、提示和本地化。

创建命令中不要直接手写 StartUp 属性字符串,也不要在 UI 代码中散落模型细节。更清晰的做法是:

  1. command 只处理交互和校验。

  2. factory 负责创建实例。

  3. 类型接口负责写入输入和参数。

  4. 插入服务或命令收尾逻辑负责放入当前 Body、OGS、Geometrical Set 或 MechanicalSet。

  5. update 交给 CATIA 更新机制或明确调用对应服务。

这样做的好处是:同一个 factory 可被命令、脚本、批处理或测试复用;同一个命令也可以在创建模式和编辑模式之间共享大部分逻辑。

10. 构建和运行验证闭环

一个最小验证流程如下:

  1. 在 CAA workspace 中创建 command module 和 add-in/workbench module。

  2. 编写命令类,确认 CATImplementClass、头文件导出和 Imakefile/mj 依赖正确。

  3. 在 workbench 或 add-in 的 CreateCommands 中实例化 command header。

  4. 在 CreateWorkbench 或 CreateToolbars 中创建 CATCmdStarter,并用 SetAccessCommand 指向 header ID。

  5. 更新 interface dictionary,确认 late type、interface、library 三列都正确。

  6. 放置 .CATNls.CATRsc、图标,并刷新运行时视图。

  7. 启动 CNEXT,进入目标文档和工作台,确认工具栏/菜单出现。

  8. 点击命令,验证选择过滤、状态提示、OK/Cancel、预览清理、撤销/重做。

  9. 保存文档、关闭、重开,确认命令创建的对象和更新行为正常。

调试时可以先把命令做成最小 one-shot:点击后弹出消息或打印 trace,确认 header、library、class name 和资源链路没问题;再逐步加入 state graph 和业务逻辑。

11. 常见坑

问题 典型原因 处理建议
工具栏按钮不显示 CATCmdStarter

 没有 SetAccessCommand,或 header ID 不匹配
检查 CreateToolbars/CreateWorkbench 中 starter 和 header 的字符串。
点击按钮没反应 header 指向的 library 名或 command class 名错误 library 名不要写 lib 前缀和平台后缀,class 名要和 CATImplementClass 的实现类一致。
进入工作台后 add-in 不出现 dictionary 未声明 add-in interface,或 late type 名冲突 检查 .dicoCATImplementClass 第四参、TIE 宏和目标 workbench 暴露的 add-in interface。
标题显示内部 ID 缺少 command header .CATNls 或 key 拼错 按“HeaderClass.HeaderId.Title”格式检查资源 key。
图标不显示 .CATRsc

、图标名或 resources/graphic 路径错误
确认图标进入运行时视图,并检查 CATGraphicPath
选择不受限制 CATPathElementAgent

 未设置过滤
给 agent 增加接口过滤、类型过滤或自定义 guard condition。
取消命令后残留高亮/临时几何 Cancel

/Desactivate 没有清理临时对象
所有 preview、临时 body、agent 输出都应有集中清理路径。
命令创建对象但 undo 异常 直接修改模型,未纳入命令/文档修改机制 对文档修改命令检查 undo/redo、warm start 和事务边界。
64 位下启动参数异常 把整数直接强转成 void * 使用 CATINT32ToPtr 等兼容宏,或传稳定对象/字符串。
修改资源后无变化 运行时视图未刷新,或 CATIA 使用缓存 刷新 RTV,重启 CNEXT,确认资源文件在 OS 目录下。

12. 推荐阅读的本地 CAADoc 页面

以下路径均来自当前安装库:

  • CAADoc/Doc/online/CAADegTechArticles/CAADegCommandModel.htm:CAA command model、命令类型、command tree、focus 和运行模式。

  • CAADoc/Doc/online/CAADegTechArticles/CAADegCreatingCommand.htm:创建 CATStateCommand 的基础步骤、BuildGraph、agent、condition 和 action。

  • CAADoc/Doc/online/CAAAfrTechArticles/CAAAfrIntegratingCommand.htm:命令如何接入 workbench、add-in 和 warm start。

  • CAADoc/Doc/online/CAAAfrTechArticles/CAAAfrCommandHeaders.htm:command header 的职责、资源 key 和命令加载关系。

  • CAADoc/Doc/online/CAAAfrUseCases/CAAAfrSampleStdCommandHeader.htm:使用 MacDeclareHeader 创建标准 command header 的示例。

  • CAADoc/Doc/online/CAAAfrUseCases/CAAAfrSampleWorkbench.htm:创建 workbench、toolbar、menu、command starter 的完整示例。

  • CAADoc/Doc/online/CAAAfrUseCases/CAAAfrSampleAddin.htm:创建 workbench add-in,并通过 CreateCommands/CreateToolbars 接入命令。

  • CAADoc/Doc/online/CAAAfrUseCases/CAAAfrSampleGeneralWksAddin.htm:接入 general workshop 的 add-in 示例。

  • CAADoc/Doc/online/CAAAfrTechArticles/CAAAfrI18NHeader.htm:command header 的 .CATNls/.CATRsc 资源写法。

  • CAADoc/Doc/online/CAAAfrTechArticles/CAAAfrI18NWorkshop.htm:workshop/workbench、toolbar 和 menu container 的资源写法。

评论