Wednesday, July 3, 2019

UGS NX3

2005 年底, 機械設計工程系導入 UGS NX3, 大約一年半之後, Siemens 以 35 億美元購入 UGS, 期間又經歷了 13 年, 終於又再有機會使用 NX, 這時已經到了 12 版.

當年的 NX3 總共導入 100 套完整教育版本, 花了 9 萬台幣, 經過大約八年閒置期, 想要重新啟動認證主機, 事情當然沒有想像中簡單.

當年所使用的 Windows 操作系統為 XP 32 位元版本, 前後使用大約 6 年, 到 2011 年, 操作系統轉為 Windows 7, 同樣是 32 位元版本, 認證主機的架構除了納入 64 位元系統, IPv6 也在 2012 年正式登場, 但也正在這個時候 NX 來到第八版,  教育版的價位不斷攀升之後, 只得以相同合理的價位換為 PTC Creo 2.0.

於情於理, 一個自認能夠培養未來產業頂尖人才的科技大學, 實在沒有理由花大錢導入商用套件. 反而, 軟體代理商除了應該要贈送軟體, 並且額外付費請學校使用該特定套件教學才對.

2012-2015 年持續使用 Creo 3 之後, 終於在 2015 年底 Onshape 提供全球免費教育版, 於是在最佳化電腦輔助設計室的網路環境之後, 至今的電腦輔助設計與協同產品設計實習課程, 全面採用 Onshape.

至於目前看待 NX3 乃至 NX12 的目的, 僅在於了解其程式基本架構, 當年的 NX3, 已經可以透過 nx3.c:

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// for block creation
#include 
#include 

// for stl 資料轉出
#include 

#include 

// NX initialization
extern int nx3init(void)
{
    UF_initialize();
    return 0;
}

static PyObject* mod_nx3init(PyObject *self, PyObject *args)
{
    int fail = 0;
    // ii 表示兩個輸入變數都是整數
    nx3init();
    // i 表示 s 為整數
    return Py_BuildValue("i",fail);
}

extern int nx3term(void)
{
    UF_terminate();
    return 0;
}

static PyObject* mod_nx3term(PyObject *self, PyObject *args)
{
    int fail = 0;
    // ii 表示兩個輸入變數都是整數
    nx3term();
    // i 表示 s 為整數
    return Py_BuildValue("i",fail);
}

// 定義內部運算的函式內容
extern int nx3(int a, int b)
{
  tag_t  part_tag;
  tag_t  reopen_part_tag;
  /* Open a new part then save it. */
  //UF_initialize();
  UF_PART_new("a_blank_part.prt",UF_PART_ENGLISH,&part_tag);
  UF_PART_save();
  //UF_terminate();
  return a+b;
}

// sum 函式的 interface
static PyObject* mod_nx3(PyObject *self, PyObject *args)
{
    int a;
    int b;
    int s;
    // ii 表示兩個輸入變數都是整數
    if (!PyArg_ParseTuple(args,"ii",&a,&b))
       return NULL;
    // 這裡的 sum2 則是內部的函式定義, 與外部呼叫模組或函式名稱沒有直接關係
    s = nx3(a,b);
    // i 表示 s 為整數
    return Py_BuildValue("i",s);
}

// 定義內部運算的函式內容
extern int nx4(int a, int b)
{
  tag_t  part_tag;
  tag_t  reopen_part_tag;
  //UF_initialize();
  UF_PART_new("abc_blank_part.prt",UF_PART_ENGLISH,&part_tag);
  UF_PART_save();
  //UF_terminate();
  return a-b;
}

// sum 函式的 interface
static PyObject* mod_nx4(PyObject *self, PyObject *args)
{
    int a;
    int b;
    int s;
    // ii 表示兩個輸入變數都是整數
    if (!PyArg_ParseTuple(args,"ii",&a,&b))
       return NULL;
    // 這裡的 sum2 則是內部的函式定義, 與外部呼叫模組或函式名稱沒有直接關係
    s = nx4(a,b);
    // i 表示 s 為整數
    return Py_BuildValue("i",s);
}

// 建立 block 的函式處理
extern int nx3block(double corner0, double corner1, double corner2, char* edge0, char* edge1, char* edge2)
{
    double corner[3] ={corner0,corner1,corner2};
    // 指定長寬高
    char* edge[3] = {edge0,edge1,edge2};
    tag_t  block_tag;
    UF_PART_new("abc_blank_part.prt",UF_PART_ENGLISH,&block_tag);
    UF_MODL_create_block(UF_NULLSIGN,NULL_TAG,corner,edge,&block_tag);
    return 0;
}

static PyObject* mod_nx3block(PyObject *self, PyObject *args)
{
    double corner0, corner1, corner2;
    char *edge0, *edge1, *edge2;
    int fail = 0;
    // ii 表示兩個輸入變數都是整數
    if (!PyArg_ParseTuple(args,"dddsss",&corner0,&corner1,&corner2,&edge0,&edge1,&edge2))
       return NULL;
    fail = nx3block(corner0,corner1,corner2,edge0,edge1,edge2);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

// 開啟零件檔案
extern int partopen(char* filename)
{
    UF_PART_load_status_t load_status;
    tag_t part;
    int ifail = 0;
    // 根據使用者所要處理的檔案, 將零件檔案打開
    ifail = UF_PART_open(filename,&part,&load_status);
    return ifail;
}

static PyObject* mod_partopen(PyObject *self, PyObject *args)
{
    char* filename;
    int fail = 0;
    if (!PyArg_ParseTuple(args,"s",&filename))
       return NULL;
    fail = partopen(filename);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

// 關閉零件檔案
extern int partclose(char* filename)
{
    int ifail = 0;
    tag_t part_tag;
    part_tag = UF_PART_ask_part_tag(filename);
    ifail = UF_PART_close(part_tag, 1, 1);
    return ifail;
}

static PyObject* mod_partclose(PyObject *self, PyObject *args)
{
    char* filename;
    int fail = 0;
    if (!PyArg_ParseTuple(args,"s",&filename))
       return NULL;
    fail = partclose(filename);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

extern int partsaveas(char* filename)
{
    UF_PART_save_as(filename);
    return 0;
}

static PyObject* mod_partsaveas(PyObject *self, PyObject *args)
{
    char* filename;
    int fail = 0;
    // ii 表示兩個輸入變數都是整數
    if (!PyArg_ParseTuple(args,"s",&filename))
       return NULL;
    fail = partsaveas(filename);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

extern int editexp(char* exp)
{
    // exp should be something like "p0=10"
    int ifail = 0;
    ifail = UF_MODL_edit_exp(exp);
    return ifail;
}

static PyObject* mod_editexp(PyObject *self, PyObject *args)
{
    char *exp, *str;
    double dimension;
    int fail = 0;
    // ii 表示兩個輸入變數都是整數
    if (!PyArg_ParseTuple(args,"sd",&exp,&dimension))
       return NULL;
    str = malloc(256);
    sprintf(str,"%s=%G",exp,dimension);
    fail = editexp(str);
    free(str);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

extern int modelupdate()
{
    // exp should be something like "p0=10"
    int ifail = 0;
    ifail = UF_MODL_update();
    return ifail;
}

static PyObject* mod_modelupdate(PyObject *self, PyObject *args)
{
    int fail = 0;
    fail = modelupdate();
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

// 計算特定零件的體積資料

// 將零件轉為 STL 檔案, 文字格式或二位元格式
// 開啟文字格式的 STL 檔案
extern int savetextstl(char* filename)
{
    void * file_handle;
    int ifail = 0;
    tag_t body_tag;
    int num_errors;
    UF_STD_stl_error_p_t  error_info;
    ifail = UF_STD_open_text_stl_file(filename,TRUE,&file_handle);
    // 擷取零件中的實體 (type=70, subtype=0) 資料 
    UF_MODL_ask_object(70,0,&body_tag);
    // 由 UF_MODL_ask_object 取得的 tag 確實為 body tag
    ifail = UF_STD_put_solid_in_stl_file(
    file_handle,
    NULL_TAG,
    body_tag,
    0.1,
    0.5,
    0.05,
    &num_errors,
    &error_info);
    ifail = UF_STD_close_stl_file (file_handle);
    return ifail;
}

static PyObject* mod_savetextstl(PyObject *self, PyObject *args)
{
    char* filename;
    int fail = 0;
    if (!PyArg_ParseTuple(args,"s",&filename))
       return NULL;
    fail = savetextstl(filename);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

// 開啟二位元格式的 STL 檔案
extern int savebinstl(char* filename)
{
    void * file_handle;
    int ifail = 0;
    tag_t body_tag;
    int num_errors;
    UF_STD_stl_error_p_t  error_info;
    // TRUE append
    // FALSE not append
    // apply filename as header
    ifail = UF_STD_open_binary_stl_file(filename,TRUE,filename,&file_handle);
    /*
extern int UF_STD_open_binary_stl_file (
char * file_name,
logical append,
char * header,
void ** file_handle );
char *  file_name Input Name of STL file to be written
logical  append Input Append flag:
TRUE = Append
FALSE = No append
char *  header Input Header text for start of binary file
void **  file_handle Output Handle of file
*/
    // 擷取零件中的實體 (type=70, subtype=0) 資料 
    UF_MODL_ask_object(70,0,&body_tag);
    // 由 UF_MODL_ask_object 取得的 tag 確實為 body tag
    ifail = UF_STD_put_solid_in_stl_file(
    file_handle,
    NULL_TAG,
    body_tag,
    0.1,
    0.5,
    0.05,
    &num_errors,
    &error_info);
    ifail = UF_STD_close_stl_file (file_handle);
    return ifail;
}

static PyObject* mod_savebinstl(PyObject *self, PyObject *args)
{
    char* filename;
    int fail = 0;
    if (!PyArg_ParseTuple(args,"s",&filename))
       return NULL;
    fail = savebinstl(filename);
    // i 表示 fail 為整數
    return Py_BuildValue("i",fail);
}

// ask part volume, 這裡使用的單位為 cm^3
extern double askvolume()
{
    void * file_handle;
    int ifail = 0;
    tag_t body_tag;
    char *str;
    // 配合設定不同物理條件的精確度
    double  acc_val[11] = {.01,0,0,0,0,0,0,0,0,0,0};
    double  massprop[47];
    double  massprop_stat[13];
    // 擷取零件中的實體 (type=70, subtype=0) 資料 
    UF_MODL_ask_object(70,0,&body_tag);
    // 這裡假設零件檔案中只有一個實體
    //求質量特性,
    UF_MODL_ask_mass_props_3d(&body_tag,1,1,3,1.0,1,acc_val,massprop,massprop_stat);
    return massprop[1];
}

static PyObject* mod_askvolume(PyObject *self, PyObject *args)
{
    double volume;
    int fail = 0;
    volume = askvolume();
    // i 表示 fail 為整數
    return Py_BuildValue("d",volume);
}

// 與函式對應的結構定義
static struct PyMethodDef Mod_Nx3[] = {
    {"nx3", mod_nx3, METH_VARARGS, "Description.."},
    {"nx4", mod_nx4, METH_VARARGS, "Description.."},
    {"nx3init", mod_nx3init, METH_VARARGS, "NX3 initilization function"},
    {"nx3term", mod_nx3term, METH_VARARGS, "NX3 termination function"},
    {"nx3block", mod_nx3block, METH_VARARGS, "Block Creation"},
    {"partsaveas", mod_partsaveas, METH_VARARGS, "part save as"},
    {"partopen", mod_partopen, METH_VARARGS, "part open"},
    {"partclose", mod_partclose, METH_VARARGS, "part open"},
    {"editexp", mod_editexp, METH_VARARGS, "edit expression"},
    {"modelupdate", mod_modelupdate, METH_VARARGS, "model update"},
    {"savetextstl", mod_savetextstl, METH_VARARGS, "open text stl"},
    {"savebinstl", mod_savebinstl, METH_VARARGS, "open binary stl"},
    {"askvolume", mod_askvolume, METH_VARARGS, "ask part volume"},
    {NULL,NULL,0,NULL},
};

// 模組結構定義
// 配合 Python 3 新增的模組結構定義
static struct PyModuleDef ModNx3 = {
   PyModuleDef_HEAD_INIT,
   "Nx3Module",   /* 模組名稱 */
   "Nx3Module_doc", /* 模組文件, may be NULL */
   -1,       /* size of per-interpreter state of the module,
                or -1 if the module keeps state in global variables. */
   Mod_Nx3// 函式結構名稱
};

// 模組起始
// PyInit_ 後必須使用"名稱".pyd 中的模組名稱, 以便啟動
// 換言之, 若編譯連結後的動態模組名稱為 nx3.pyd, 則此地的起始函式名稱必須為 PyInit_nx3
PyMODINIT_FUNC 
PyInit_nx3(void)
{
    // 建立模組的起始, 輸入為模組結構名稱之 address
    (void) PyModule_Create(&ModNx3);
}

所編譯成的動態連結程式庫, nx3.pyd, 讓下列 Python 3 程式呼叫:

#coding: utf-8
# 導入 nx3.pyd
from nx3 import *

##############################
# 啟動 nx3
if (nx3init() == 0):
    print("nx3 啟動")
else:
    print("nx3 無法啟動")
##############################
# 開啟既有的零件檔案
partopen("block.prt")
# 針對所開啟的零件檔案, 設定其中的尺寸變數

for i in range(1,21):
    for j in range(1,21):
        editexp("p0",i*1.6543)
        editexp("p1",j*1.4563)
        editexp("p2",6)
        # 進行零件的資料更新
        modelupdate()
        '''
        # 進行相關繪圖函式的呼叫
        corner = [10,10,0]
        edge = ["10","10","10"]

        nx3block(corner[0],corner[1],corner[2],edge[0],edge[1],edge[2])
        '''
        # 將尺寸變更後的零件進行存檔
        #partsaveas("finalblock.prt")
        #savebinstl("finalblock.stl")
        print(askvolume()/2.54/2.54/2.54, "立方英吋")
partsaveas("finalblock.prt")
partclose("finalblock.prt")
##############################
# 終止 nx3
if (nx3term() == 0):
    print("nx3 終止")
else:
    print("nx3 無法終止")
##############################

NX10 之後, 已經直接支援 Python, 只是 NX12 使用的 Python 版本內定為 3.6.1, 比可攜程式系統中的 3.7.3 舊, 因此相容性必須經過測試才能確定.

最後, 學員只要登入 @gm 電子郵箱, 就可以下載 NX3_portable.7z, 解開壓縮檔放入隨身碟, 並且連上網路擷取授權認證後, 就可以使用.

利用 NX3 執行零件繪製與受力變形分析, 請參考:



No comments:

Post a Comment

NX12

NX 12 Help https://docs.plm.automation.siemens.com/tdoc/nx/12/nx_help Python related https://docs.plm.automation.siemens.com/tdoc/nx/...