编程基础

编程方式

NX 二次开发编程方式:

1、GRIP(Graphics Interactive Programming, 图形交互编程);

2、KF(Knowledge Fusion, 知识熔接技术);

3、SNAP(Simple NX Application Programming, 简易 NX 应用编程);

4、NXOpen C++,也称为 User Function,简称 UFUN;

5、NXOpen.NET;

6、NXOpen Java;

7、NXOpen Python。


NX 二次开发架构关系:


NX 二次开发内部机制:

无论采用哪种方式编程,最终都是调用共享的 C/C++ API。

官方在开发 NX 时使用的是 C/C++,再通过 *.ja 文件控制是否要自动生成其他开发方式对应的 API。

开发流程

NX 二次开发流程:

设计菜单与功能区 -> 设计对话框 -> 设计代码

NX 系统中的原生工具通常由三部分组成:菜单与功能区、对话框、代码。


设计菜单与功能区:

菜单与功能区使用 MenuScript 编写。


设计对话框:

对话框使用 NX 中 Block UI Style 模块的相关 UI Block 设计。


设计代码:

代码使用 Visual Studio 调用 NXOpen C/C++ 相关 API 完成。

配置 VS 开发环境

编译器

NX 版本与编译器的对应关系(C/C++):

NX 12(NX 版本) - Visual Studio 2015 Build 19.00.23026(Windows 下编译器) - gcc 4.8.2(Linux 下编译器)

NX 1926/NX 1953(NX 版本) - Visual Studio 2019 Professional 16.0.5 Build 19.20.27508.1(Windows 下编译器) - gcc 7.3.1(Linux 下编译器)


使用与 NX 版本不匹配的编译器设计代码时:

编译链接能通过,但在 NX 中运行应用程序时系统报错。

解决方法:

使用 NX 对应版本的 Visual Studio 重新编译链接。


在低版本 NX 中开发的应用程序不能在高版本 NX 中运行:

高版本 NX 中的 *.lib 文件发生了变化,需要在高版本 NX 对应的 Visual Studio 中使用最新的库,修改代码再编译链接它。


高低版本通用情况:

通常情况下,在低版本 NX 中开发应用程序时,如果只使用 libufun.lib、libnxopencpp.lib、libugopenint.lib、libnxopenuicpp.lib 这四个库,是可以在高版本 NX 中运行应用程序的。

关闭检查警告

手工搭建开发环境

创建项目:

启动 VS,单击 Create a new project(新建项目) 选项,在弹出的对话框中单击 Windows Desktop Wizard() 选项,再单击 Next 按钮。

配置 NXOpen C++ Wizard

将 %UGII_BASE_DIR%\UGOPEN\vs_files\VC 文件夹合并到 Visual Studio 中的 VC 文件夹中。


点击 Finish 即可。


NXOpen C++ Wizard(开发向导) 方式会自动完成手工搭建环境的一系列设置,并创建一个 *.cpp 文件。在该文件中,系统默认创建了 MyClass 类,还添加了 ufusr()ufusr_ask_unload() 函数。

Hello World!

VS:

do_it() 函数中添加代码:

void MyClass::do_it()

{

UF_initialize();

lw->Open();

lw->WriteLine("Hello World!");

UF_terminate;

// TODO: add your code here

}

点击 Build(生成(B)) -> Build Solution(生成解决方案(B)),对项目进行编译链接,生成 *.dll 动态链接库文件。


NX:

在 NX 中单击 FIle(文件(F)) -> Execute(执行(T)) -> NX Open,选择该动态链接库文件 *.dll,运行结果如下:

DLL 文件

库文件(DLL 文件):

库文件是计算机上的一类文件,提供给使用者一些开箱即用的变量、函数或类。

可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。


库文件分为静态库和动态库:

在库文件的发展史上经历了 无库 - 静态链接库 - 动态链接库 的时代。

静态链接库与动态链接库都是共享代码的方式。

静态库和动态库的区别体现在程序的链接阶段:

1、静态库在程序的链接阶段被复制到了程序中;

也就是说,如果使用静态链接库,库中的指令都被直接包含在最终生成的可执行文件中了。

2、动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库系统只需载入一次,不同的程序可以得到内存中相同的动态库的副本,因此节省了很多内存,而且使用动态库也便于模块化更新程序。

也就是说,如果使用动态链接库,该库文件则不必被包含在最终可执行文件中,可执行文件执行时可以“动态”地引用和卸载这个与可执行文件独立的库文件。


文件扩展名:

Windows 的静态库文件扩展名是 .lib,动态库文件扩展名是 .dll (Dynamic-Link Libraries),Linux 的静态库扩展名是 .a,动态库扩展名是 .so (Shared Object)。


应用程序与动态库的关系:

在操作系统中,许多应用程序并不是只有一个完整的可执行文件,大多数程序模块被分割成一些相对独立的动态库。当执行某一个程序时,相应的动态库文件就会被调用。一个应用程序可使用多个动态库文件,一个动态库文件也可能被不同的应用程序使用,这样的动态库文件被称为共享库文件。

NXOpen C

NXOpen C 概述

NXOpen C 命名约定

NXOpen C 命名约定:

NXOpen C API 有两种命名约定,即 标准命名约定遗留命名约定


标准命名约定(Standard Naming Convention):

根据 API 所在的模块与实现的功能赋予有意义的名称。这一类 API 格式是 UF_<area>_<name>

1、UF 是 User Function(用户函数) 的简写;

2、<area> 通常是对应用模块或功能领域的说明;

例如,UF_MODL_create_sphere 中的 MODL 表示 Modeling(/ˈmɒdlɪŋ/, 建模) 模块。

3、<name> 通常是对实现的具体功能的描述,一般由动词或名词组成。

常见的词语有 ask(查询)、get(获取)、create(创建)、new(新建)、edit(编辑)、delete(删除)、set(设置)、init(初始化)、remove(移除)等。


遗留命名约定(Legacy Naming Convention):

这一类 API 格式是 uc---- 或者 uf----,其中 ---- 通常是4位数字或者3位数字加1字母,NX 早期采用 FORTRAN 语言开发,FORTRAN77 中变量名长度限制为6。

这一类型的 API 大部分已经被其他标准命名约定的 API 替代,但还有一部分被开发者青睐,如 uc1601、uf5947,它们的名字没有规律可循,只需记忆。


以 Sketch(/sketʃ/, 草图)为例:

希望利用 NXOpen C 中的 API 创建一个 Sketch,根据模块分类这个 API 应该是以 UF_SKET 开头的,例如,UF_SKET_create_sketch。

NXOpen C 数据类型

NXOpen C 数据类型:

除 C/C++ 标准的数据类型外,NXOpen C 还大量使用结构体、联合结构体、枚举、指针等数据类型。


NXOpen C 数据类型约定:

1、后缀 _t:数据类型(Data type);

2、后缀 _p_t:数据类型指针(Pointer to that type);

3、后缀 _s:结构体类型(Structure tag);

4、后缀 _e:枚举类型(Enumeration type);

5、后缀 _u_t:联合结构体类型(Union type);

6、后缀 _u_p_t:联合结构体类型指针(Pointer to a union type);

7、后缀 _f_t:函数指针(Pointer to a function)。


除了后缀满足上述约定,数据类型的命名约定还与 API 命名约定一致。

结构体 UF_MODL_bsurface_s 来自与建模相关的头文件(uf_modl_types.h):

struct UF_MODL_bsurface_s

{

int num_poles_u;

int num_poles_v;

int order_u;

int order_v;

int is_rational;

double *knots_u;

double *knots_v;

double (*poles)[4]; /* <len:num_poles_u*num_poles_v> */

} ;

typedef struct UF_MODL_bsurface_s UF_MODL_bsurface_t, *UF_MODL_bsurface_p_t;


tag_t:

在 NXOpen C 中,使用最多的数据类型是 tag_t(/tæɡ/, 标签,标识符),在文件 %UGII_BASE_DIR%\UGOPEN\uf_defs.h 中的定义如下:

typedef unsigned int tag_t;

数据类型 tag_t 早期被称为 Eid(Entity Identifier, 实体标识符),现在通常被称为 对象标识符(Object Identifier)

在 NX 系统中,每一个对象都有唯一的标识符,可以理解为每一个对象都是由大于0的不重复整数来标识。


Handle(句柄):

对象标识符是由 NX 系统根据一定的算法规则临时生成的,它不会随着部件的保存而保存。例如,开发者希望当前工作部件(Part)中指定的面(Face) 在下次打开部件时,自动高亮显示,而一般情况下,同一部件在不同的计算机上打开,或者被再次打开后,对象标识符的值是不同的。此时可考虑使用 handle(句柄)来解决这个问题。handle 会随着部件的保存而被保存。

相关 API 如下:

UF_TAG_ask_handle_of_tag(查询标识符的句柄)

UF_TAG_ask_tag_of_handle(查询句柄的标识符)

UF_TAG_compose_handle(构造句柄)

UF_TAG_decompose_handle(分解句柄)

NXOpen C 对象

NX 中常见的对象:

Feature(/ˈfiːtʃə(r)/, 特征)

Point(/pɔɪnt/, 点)

Edge(/edʒ/, 边)

Face(/feɪs/, 面)

Body(/ˈbɒdi/, 体)


对象拥有 Type(/taɪp/, 类型)和 Subtype(/ˈsʌbˌtaɪp/, 子类型) 两部分定义,其中 Subtype 可以更详细地描述对象。


对于 NX 中已经存在的对象,可以通过以下 API 获取:

API说明
UF_OBJ_ask_type_and_subtype获取对象的 Type 和 Subtype
UF_MODL_ask_object查询对象
UF_OBJ_cycle_all遍历
UF_OBJ_cycle_by_name
UF_OBJ_cycle_by_name_and_type_extended
UF_OBJ_cycle_objs_in_part
UF_OBJ_cycle_typed_objs_in_part

遍历当前工作部件中所有的 Body 示例代码:

static void do_it(void)

{

int type = UF_solid_type, stype = UF_solid_body_subtype;

tag_t obj = NULL_TAG;

while(UF_MODL_ask_object(type, stype, &obj) == 0 && obj != NULL_TAG)

{

}

}


solid /ˈsɒlɪd/ 实体

NXOpen C 对象的状态

NX 对象存在四种状态:

说明
UF_OBJ_ALIVE对象是活跃的。
UF_OBJ_DELETED对象已删除。
UF_OBJ_TEMPORARY对象是临时的,不保存在部件中。例如,利用 UF_CSYS_create_temp_csys 创建的 CSYS。
UF_OBJ_CONDEMNED这种状态的对象通常在 NX 中不显示,例如,矩阵对象、智能对象等。

在开发应用程序时,为了保证程序代码的稳定性,一般情况下会先获取对象的状态,如果对象的状态是 UF_OBJ_ALIVE,再执行其他操作。


可以利用 UF_OBJ_ask_status 获取对象的状态。

NXOpen C 对象转换

考虑这样一种情况,NXOpen C 中的 UF_MODL_create_block 创建了一个 Block,输出的是 Block Feature 的标识符,此时如果要对所有的 Edge(边)进行 Edge Blend 操作,那么传入的对象应当是 Edge 的标识符,而不是 Feature 的标识符。这就涉及到对象之间的相互转换问题。


常见的对象转换 API:

API说明
UF_MODL_ask_feat_body通过 Feature 查询 Body
UF_MODL_ask_feat_edges通过 Feature 查询 Edges
UF_MODL_ask_feat_faces通过 Feature 查询 Faces
UF_SKET_ask_feature_sketches通过 Feature 查询 Seketches
UF_SKET_ask_sketch_features通过 Sketch 查询关联 Features
UF_MODL_ask_body_features通过 Body 查询 Features
UF_MODL_ask_body_faces通过 Body 查询 Faces
UF_MODL_ask_body_edges通过 Body 查询 Edges
UF_MODL_ask_face_body通过 Face 查询 Body
UF_MODL_ask_face_feats通过 Face 查询 Features
UF_MODL_ask_edge_body通过 Edge 查询 Body
UF_MODL_ask_edge_faces通过 Edge 查询 Faces
UF_MODL_ask_edge_feats通过 Edge 查询 Features
NXOpen C 头文件与 API 分类

头文件与 API 分类:

在使用 NXOpen C API 时,应该添加对应的头文件,头文件与 API 分类一一对应。

例如,新建一个部件(part)使用的 API 是 UF_PART_new,应当添加 uf_part.h 头文件。

NXOpen C API 声明

NXOpen C 提供的 API 符合 ANSI/ISO C 标准,在相应的头文件中定义的原型格式为:

<return data type> <function name> (argument list)


<return data type>:

大部分的 NXOpen C API 返回都是一个整数(int),

1、为0时,表明该 API 执行正常;

2、非0时,表明该 API 执行异常。

如果开发者希望获得非0整数所代表的意义,需要使用 API UF_get_fail_message。

需要注意的是,有部分 API 返回非0整数时,不表示执行有异常,而是有其他某种具体的含义。例如,UF_PART_ask_num_parts API 返回的是当前 NX 会话中加载部件的数量。


<function name>:

遵循 NXOpen C 命名约定。


(argument list):


在 API 帮助文档中,API 的描述一般由以下部分组成:

1、Defined in:描述 API 被定义在哪个头文件中;

2、Overview:描述 API 实现的功能等;

3、Environment:描述 API 允许的运行模式;

如果描述中不包含关键词 External(/[ɪkˈstɜːnl/, 外部的),说明该 API 不允许以批处理模式(Batch(/bætʃ/, 批), 又称外部模式 External)运行。

例如,uc1601 API 不允许以批处理模式运行。

4、See Also:描述与该 API 相关联或者类似的 API;

若描述中含有 Refer to example 这些字段,表明该 API 有样例,点击它即可查看。

5、History:描述 API 发布的 NX 版本;

6、Required License(s):使用该 API 需要的 License(/ˈlaɪsns/, 许可);

7、API 详细声明:一般格式如下表示

以 UF_MODL_create_plane 为例:

类型参数输入/输出描述
doubleorigin_point[3]InputOrigin point of the plane(平面原点)
doubleplane_normal[3]InputPlane normal(平面法向)
tag_t*plane_tagOutputNew plane(创建的平面)

在创建平面的 API 声明中,创建一个平面需要一个起点和法向量,所以在输入参数时,会要求指定 Plane(/pleɪn/, 平面) 的原点和法向量,如果成功创建就输出创建的 Plane 的标识符。

部件 API(UF_PART_)

草图 API(UF_SKET_)

3D 模型通常是基于草图(Sketch)创建的,有了草图,就可以利用 Extrude(/ɪkˈstruːd/, 拉伸)、Revolve(/rɪˈvɒlv/, 旋转)等工具创建实体形状。


NXOpen C 中草图特征操作常用 API:

API说明
UF_SKET_initialize_sketch初始化草图环境(进入草图环境)。
UF_SKET_terminate_sketch终止(退出)草图环境。
UF_SKET_create_sketch创建一个空的草图。
UF_SKET_update_objects更新指定草图。
UF_SKET_ask_sketch_status查询草图的状态(自由度)。
UF_SKET_ask_active_sketch获取活动草图的标识符。
UF_SKET_ask_feature_sketches获取给定特征(Feature)关联的所有草图。
UF_SKET_ask_sketch_info获取给定草图的信息,例如,矩阵、草图名称、视图等。
UF_SKET_ask_exps_of_sketch获取给定草图的所有表达式。
UF_SKET_ask_sketch_features获取给定草图关联的特征。
UF_SKET_add_objects添加几何对象到指定的草图中。
UF_SKET_ask_geoms_of_sketch获取给定草图的所有几何对象。
UF_SKET_ask_sketch_of_geom通过给定几何对象标识符查询草图标识符。
UF_SKET_mirror_objects根据中心线镜像草图中的对象。
UF_SKET_create_dimension创建尺寸,输出尺寸标识符。
UF_SKET_delete_dimensions删除指定草图的尺寸。
UF_SKET_create_dimensional_constraint创建尺寸约束,输出尺寸约束标识符。
UF_SKET_ask_dimensions_of_sketch获取给定草图的所有尺寸约束。
UF_SKET_create_geometric_constraint创建几何约束。
UF_SKET_ask_geo_cons_of_sketch获取给定草图的所有几何约束。
UF_SKET_ask_geo_cons_of_geometry获取给定草图几何对象的所有几何约束。
UF_SKET_delete_constraints删除指定草图的几何约束。
UF_
UF_SKET_create_sketch

extern UFUNEXPORT int UF_SKET_create_sketch

(

char name[ UF_OBJ_NAME_BUFSIZE ],

/* <I> Sketch name. It can be at most UF_OBJ_NAME_LEN bytes long. */

int option,

/* <I> Option. 1: Sketch on face/datum plane; 2: Specify sketch CSYS. */

double matrix[9],

/* <I> Sketch CSYS (for option = 2): [0-5]: X-AXIS and Y-AXIS of matrix; [6-8]: Origin of CSYS. */

tag_t object[2],

/* <I> Objects (for option = 1): [0]: Solid face/Datum plane object; [1]: Reference object; (edge, datum axis, solid face/datum). */

int reference[2],

/* <I> Reference and direction (for option = 1): [0]: Reference edge 1: Horizontal, 2: Vertical; [1]: Direction 1: Start to end (from vertex1 to vertex2), -1: End to start (from vertex2 to vertex1). */

int plane_dir,

/* <I> Datum plane direction: 1: Outwards from parent body; 2: Inward. */

tag_t *sketch_id

/* <O> Tag of teh sketch created */

);


extern /ˈekstɜːn/ 外部变量声明

bufsize(buffer size) 缓冲区大小

option /ˈɒpʃn/ 选项

datum /ˈdeɪtəm/ 数据

plane /pleɪn/ 平面

Specify /ˈspesɪfaɪ/ 具体说明

matrix /ˈmeɪtrɪks/ 矩阵

axis /ˈæksɪs/ 轴

reference /ˈrefrəns/ 参考,引用

direction /dəˈrekʃn/ 方向

horizontal /ˌhɒrɪˈzɒntl/ 水平的

vertical /ˈvɜːtɪkl/ 垂直的

vertex /ˈvɜːteks/ 顶点

outwards /ˈaʊtwədz/ 向外

inward /ˈɪnwəd/ 向内

草图特征创建流程

NXOpen C 创建草图特征的一般流程:

1、UF_SKET_initialize_sketch 初始化草图环境;

2、UF_SKET_create_sketch 创建空草图;

3、其他操作,例如,UF_SKET_add_objects 添加对象到草图;

4、UF_SKET_terminate_sketch 退出草图环境。

用户入口和出口

需求是在 NX 中执行某一操作时,也执行 NXOpen 应用程序,例如,在保存一个部件时检查文件名是否合法。

为了解决这种需求,NX 在某些位置规定了出口(Exit),可以在指定的出口自动运行编写好的 NXOpen 应用程序。

程序初始化与终止

在使用 NXOpen API 时需要正确地初始化与终止。

1、当使用 NXOpen API 前需要调用 UF_initialize 来初始化 API 环境和获取许可;

2、当不再使用 NXOpen API 时需要调用 UF_terminate 来终止许可。


一般情况下,可以考虑在程序入口点采用下列格式编写代码来实现初始化与终止:

extern "C" DllExport void ufusr(char* param, int* retcod, int param_len)

{

if(UF_initialize() == 0)

{

// 添加用户代码

UF_terminate();

}

}

UF_CALL 函数

大部分的 NXOpen C API 运行完毕时返回一个整数,如果非0表示有异常。当有上万行代码,其中一行代码出现 BUG,运行完毕时才得到非0,那么有没有一种手段,当代码运行异常时,能立马返回,并且自动输出代码行号,错误原因等。

UF_CALL 为解决这一问题提供了可行性。UF_CALL 是一个自定义的宏,开发者可以参考官方思路进行修改。

UF_CALL(UF_PART_new(part_name, UF_PART_ENGLISH, &part));


#include <stdarg.h>

#include <stdio.h>

#include <uf.h>

#include <uf_curve.h>

#include <uf_obj.h>

#include <uf_ui.h>

#define UF_CALL(X) (report(__FILE__, __LINE__, #X, (X)))

static void ECHO(char* format, ...)

{

char msg[UF_UI_MAX_STRING_LEN] = {0};

va_list args;

va_start(args, format);

vsnprintf_s(msg, sizeof(msg), __TRUNCATE, format, args);

va_end(args);

UF_UI_open_listing_window(); // 打开信息窗口

UF_UI_write_listing_window(msg); // 向信息窗口写入内容

}

static int report(char* file, int line, char* call, int irc)

{

if(irc)

{

char err[133] = {0};

UF_get_fail_message(irc, err); // 获取错误信息

ECHO("*** ERROR code %d at line %d in %s:\n", irc, line, file);

ECHO("+++ %s\n", err);

ECHO("%s;\n", call);

}

return(irc);

}

static void do_it(void)

{

// 创建点

tag_t pointA = NULL_TAG;

double pointACoord[3] = {0.0, 0.0, 0.0};

UF_CALL(UF_CURVE_create_point(pointACoord, &pointA));

// 删除点

UF_CALL(UF_OBJ_delete_object(pointA));

// 设置删除点的颜色

UF_CALL(UF_OBJ_set_color(pointA, 186));

}

void ufusr(char* param, int* retcode, int paramLen)

{

if(UF_CALL(UF_initialize()) == 0)

{

do_it();

UF_CALL(UF_terminate());

}

}

int ufusr_ask_unload(void)

{

return (UF_UNLOAD_IMMEDIATELY);

}


curve /kɜːv/ 曲线

report /rɪˈpɔːt/ 输出

terminate /ˈtɜːmɪneɪt/ 终止

immediately /ɪˈmiːdiətli/ 立即