Tcl/Tk 命令與C/C++的整合研究

Augusdi發表於2017-01-13


Tcl/Tk 命令與C/C++的整合研究


1. 問題來源
      基於虛擬現實的維修性分析評價系統(以下簡稱 VMSAS)是為適應產品無紙設計、分析、評審的需要,以人機工效商業軟體JACK 為平臺,由本單位進行二次開發形成的一個維修性設計分析評價系統,主要功能包括樣機建模、動作建模、維修模擬與檢測、維修性分析與評價等。
      VMSAS 的使用全過程涉及大量資料。為確保VMSAS 滿足並行設計和整合設計的要求,成為一種實用、先進的維修性設計分析工具,能夠與CAD 系統、工程分析系統緊密整合,就必須要解決資料獲取的授權、資料一致的保證、資料的整合、提交、稽核與批准等統一管理及許可權等問題。

       以上要求 VMSAS 與產品資料管理(PDM)系統進行整合。二者之間的整合方案如圖1 所示。
PDM系統
應用程式介面
系統整合介面
產品配置管理
產品結構管理
變更管理
文件管理
工作流管理
物件管理框架
RMS軟體
CAD軟體
Microsoft
Office
Adobe
Acorbat
VisView
VMSAS
系統
資料獲取
樣機準備
虛擬樣機成熟
拆卸過程模擬
維修性分
析與評價
模擬過程分析
模擬過程記錄
分析與評價結果
人機和諧介面
人機互動介面
虛擬場景生成
虛擬環境管理
拆卸過程控制
Oracle
產品基本資訊
產品可靠性資料
產品故障資訊
分析評價準則
虛擬設施工具等
產品CAD資料
維修性設計要求
虛擬人體模型
維修知識經驗
維修資源模型
維修任務及其
模擬分析資料
圖1 VMSAS 系統與PDM 系統整合框架
      VMASA 以JACK 系統為平臺進行二次開發的,JACK 有一套應用程式開發介面(API)——JACKScript,JACKScript 提供了對JACK 場景進行有效控制的途徑,可以使用Tcl、Python、Lisp 三種語言進行互動以及二次開發。我們以Tcl/Tk 開發應用程式,以Python 開發模擬指令碼。選用UGS PLM Solution 公司的TeamCenter Enterprise 系統進行開發來管理VMASA 中的資料。TeamCenter 系統中的應用程式介面API 函式都是以C/C++語言開發的,以DLL 檔案形式提供給使用者呼叫。故要在Tcl/Tk 環境中呼叫這些API 函式,就要解決Tcl/Tk 命令與C/C++函式之間的介面問題。本文圍繞這個問題進行討論。
      基金專案:國家自然科學基金資助專案:並行設計中產品維修性模型研究(編號:50005023),“十五”武器裝備預研專案資助。
      作者簡介:樑偉傑(1980.05~),男,漢族,碩士生,研究方向為維修性理論與應用。通訊地址:石家莊軍械工程學院六系維修工程實驗中心,050003。聯絡電話:0311-86879059。蔣科藝(1977~),男,漢族,博士生,主要研究方向為虛擬維修模擬以及應用。呂劍鋒(1981~),男,漢族,碩士生,研究方向為維修性理論與應用。
http://www.elecfans.com 電子發燒友 http://bbs.elecfans.com 電子技術論壇
2. Tcl/Tk 設計原理
在控制、模擬、檢測等多個領域,涉及大量專用系統的開發以及測試環境的建立,測試環境的建立異常繁瑣。人們力圖尋找一種新的程式語言,它既要有好的程式碼可重用性,又要簡單易學,這樣就促成了Tcl(Tool Command Language,工具命令語言)的產生。其總體結構如圖1所示:
擴充套件層Tcl層應用層
剖析器
初始化
命令迴圈
擴充套件命令內建命令應用擴充套件命令
圖1 TCL總體結構簡圖
Tcl 讓應用程式由包含編譯程式碼的大塊實體和一小部分用於配置和編寫高階命令的Tcl 程式碼組成,把程式設計按照基於元件的方法來進行,不同的元件有不同的功能,用於不同的目的。Tcl 有良好的擴充套件性, 方便使用者為其增添新的功能模組。Tcl 是一種集C/C++靈活強大的功能與BASIC 易學高效的風格於一身的通用程式設計語言。
Tcl 是一種可嵌入的命令指令碼化語言 (Command Script Language)。“可嵌入”是指把很多應用有效,無縫地整合在一起。“命令”是指每一條 Tcl 語句都可以理解成命令加引數的形式:
      Tk(Tool Kit)是基於Tcl 的圖形程式開發工具箱,是Tcl 的重要擴充套件部件。Tcl/Tk 提供了足夠的可程式設計特性(變數、流程控制和過程),使得現有程式可以組裝成符合自己需要的複雜的指令碼程式。Tcl 直譯器可以很容易地新增到已有應用程式中去,這種能力將它與其他Shell 語言區分開來,它是一種擴充套件語言的語言,用來配置和定製應用程式。Tk 隱藏了許多C/C++ 程式的設計細節,可快速地開發基於圖形介面視窗的程式。Tcl/Tk 可快速開發應用程式及應用系統,在自控、模擬、測試系統、網路管理、視覺化應用、CAD
等方面都有大量的應用成果,在歐美的許多大學和實驗室中都有廣泛的應用。
      但Tcl/Tk畢竟是一種指令碼語言,就象其它的一些指令碼語言一樣,也有很多事情不能夠做或很難做。其解決途徑是聯合C/C++與Tcl/Tk共同開發。提供C/C++程式呼叫TCL/TK 的直譯器來執行TCL/TK指令碼。
3. C/C++函式註冊為Tcl/Tk 命令
       用 C/C++語言很容易實現Tcl/Tk 命令的擴充套件,而且用C/C++實現的命令比相應的Tcl 命令執行效率要高。在各種以C/C++實現的模擬檢測程式中可方便地呼叫Tcl/Tk 命令。實現Tcl 命令的C/C++程式碼被稱之為命令過程(command procedure),命令過程的介面非常類似於主程式main 的介面,它的輸入是一個陣列,包含了與Tcl 指令碼命令變元確切對應的值,其結果就是Tcl 命令的結果。
       在使用者的 C/C++程式中,為了能夠訪問TCL/TK 庫,必須在原始碼宣告兩個呼叫庫的標頭檔案,即"tcl.h"
和"tk.h"兩個檔案。要建立混合Tcl/Tk 和C/C++應用程式,須按如下方式進行:
3.1 呼叫"Tcl_Main"函式(Tk_Main 原理相同)
      Tcl_Main(argc, argv, Tcl_AppInit),用來控制整個Tcl 直譯器程式,沒有返回值。這是一種較為高階的介面,可以替使用者建立直譯器、處理命令列變元來執行指令碼,提供互動式命令迴圈等。"Tcl_Main"函式有三個變數:第一個變數表示在這個陣列的元素個數;第三個變數是指向初始化函式的指標;第二個變數是一個字串型陣列,每個字串都有一個特殊的含義。字串陣列通"Tcl_Main"來通知Tcl/Tk 直譯器應用程式的名稱和Tcl/Tk 命令在指令碼中的位置。這個陣列實際上是傳給直譯器的命令列引數。陣列的第一項給出應用程式名稱,第二項給出了執行的指令碼位置。
命令 [引數 1] [引數 2] [引數 3] [引數 4] ...... [引數 N]
http://www.elecfans.com 電子發燒友 http://bbs.elecfans.com 電子技術論壇
3.2 初始化函式
      "Tcl_Main"的呼叫控制了程式在Tcl/Tk 中的整個呼叫,但是在底部初始化之後和Tcl/Tk 指令碼執行之前,能夠執行使用者自定義的函式。在從Tcl/Tk 指令碼中使用命令過程之前,必須提供該命令過程的初始化註冊過程,為了使用Tcl 直譯器,應用程式首先產生一個稱之為直譯器(interpreter)的物件。此時,如果要將自己的擴充套件模組建立為共享庫,此初始化過程的名稱必須以“_Init”來結束,如TclApi_Init,Random_Init等。當Tcl/Tk 指令碼載入這個庫的時候就會自動呼叫這個過程。
3.3 C/C++函式註冊為Tcl/Tk 過程
      C/C++函式在Tcl 中註冊時需呼叫一個特定的原型函式,此函式返回一個整數型別,並設定4 個變數,第一個是Tcl/Tk 庫檔案型別"ClientData";第二個變數是指向直譯器的指標;最後的兩個變數類似於在C/C++ "main"函式中的"argc"和 "argv"這兩個變數被用於傳遞引數給Tcl/Tk 過程。引數"argc"包含了傳遞給Tcl/Tk 過程的引數個數"argv" 是字串陣列,每個字串包含了一個引數。如下所示:
int TclApi(ClientData clientData, Tcl_Interp *interp,int argc,char *argv[]);
當一個函式被註冊作為Tcl/Tk 過程使用時需一個指標與之聯絡,指標通過"ClientData"來傳遞進來。
"ClientData"的概念允許程式設計師聯絡資料結構和物件,呼叫能引用這個物件的過程。3.2 中提到的註冊過程需要呼叫"Tcl_CreateCommand"函式,此函式有5 個引數:第一個引數是指向直譯器的指標;第二個引數是在Tcl/Tk 中的過程名;第三個引數是一個指向函式的指標,它在Tcl/Tk 過程被執行時呼叫;最後兩個引數是"ClientData"項和一個指標刪除例程。如下面例子所示,Tcl/Tk 將"TclApi"函式註冊為" tcl_app"命令:
Tcl_CreateCommand(interp, "tcl_app", TclApi, (ClientData)NULL,(Tcl_CmdDeleteProc *)NULL );
3.4 變數訪問
在執行Tcl/Tk 過程時能呼叫C/C++函式,並可從C/C++中通過一些函式獲得Tcl/Tk 的幫助。
"Tcl_GetVar"函式返回一個指向Tcl/Tk 變數的字串指標。此Tcl/Tk 變數在執行指令碼聯絡到直譯器的當前範圍被訪問,如果在當前範沒有區域性變數則訪問全域性變數,如沒有匹配的全域性變數存在則返報告錯誤。
下面是Tcl/Tk 指令碼中被訪問的一部分程式碼。
set a_prd_tree_name "1966PZ152"
下面是在C/C++程式碼中訪問Tcl/Tk 變數:"a_prd_tree_name"
char PTName_001 [10];
strncpy(PTName_001, Tcl_GetVar( pInterp, "a_prd_tree_name", 0 ), 9 );
這樣,變數PTName_001 的值就變為了"1966PZ152"。
"Tcl_SetVar"函式允許程式修改Tcl/Tk 變數的值。
綜上所述,若要實現Tcl/Tk 命令呼叫C/C++ API,則被呼叫的API 需具有以上特定結構,可給此API構造一個具有以上特性的“外殼”,如圖2 所示:
擁有Tcl直譯器的C/C++函式
函式的Tcl初始化過程
Tcl/Tk指令碼中要呼叫的DLL
中的C/C++函式
C/C++函式註冊為Tcl/Tk命令
Tcl/Tk命令C/C++過程
圖 2 Tcl/Tk 呼叫C/C++函式
4. Tcl/Tk 命令呼叫PDM 系統API 函式的一個例項
如何在 Tcl/Tk 命令實現的系統中呼叫C/C++ DLL 中的函式是筆者在VMSAS 系統與某PDM 系統進行整合時遇到的問題。有人寫過一個可以將C/C++ API 輸出為Tcl 命令的工具,叫做SWIG(Simple WrapperInterface Generator)。這個工具的缺點是C/C++的程式碼必須可知,並且SWIG 生成的C 介面對於指令碼編寫
http://www.elecfans.com 電子發燒友 http://bbs.elecfans.com 電子技術論壇
人員並不友好,而手工編制的Tcl 介面要好得多。
例如,使用者要登入 PDM 系統,首先必須要進行驗證,此時用到一個baseapi.dll 中的函式:
int clLogin2(char UsrName[], char Password[], int mfail)
要在Tcl/Tk 指令碼中呼叫這個函式。可按如下方式進行:
1)、生成DLL 必要資訊
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "tcl.h"
#ifndef DECLSPEC_EXPORT
#define DECLSPEC_EXPORT __declspec(dllexport)
#endif
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { return TRUE; }
2)、引用這個函式
#pragma comment(lib, "/yourPath/baseapi.dll")
extern "C" _declspec(dllimport) int clLogin2(char UsrName[], char Password[], int mfail);
3)、對應的擁有Tcl 直譯器的形式
int clLogin2Cmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);
4)、對此函式進行初始化
EXTERN_C int DECLSPEC_EXPORT clLogin2_Init(Tcl_Interp* interp) // 此處必須加“_Init”字尾
{ // 將此C 過程註冊為Tcl 命令
Tcl_CreateCommand(interp, " clloginto",
(Tcl_CmdProc *)ClLogin2Cmd, // 呼叫第4 步實現
(ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
Tcl_PkgProvide(interp, " clloginto ", "0.1"); // 給此註冊的Tcl 命令一個版本號0.1
}
5)、具體的實現
int clLogin2Cmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[ ] )
{ char usr[]; char psword[];
int error1, error2, error3, mfail, dstat;
clLogin2_Init(interp); // 必須先呼叫函式初始化過程
if (argc > 4)
{ interp->result = "Usage: clloginto ?range?";
return TCL_ERROR; }
if (argc == 4)
{ // 使用 Tcl_GetString 接受字串引數:
error1 = Tcl_GetInt(interp, argv[1], usr);
if (error1 != TCL_OK) return error1;
error2 = Tcl_GetString(interp, argv[2], psword);
if (error2 != TCL_OK) return error2;
error3 = Tcl_GetInt(interp, argv[3], mfail);
if (error3 != TCL_OK) return error3;
}
dstat = ClLogin2(usr, psword, mfail); // 在此呼叫C DLL 中的函式
return dstat; // 返回登入狀態值,由此可以進行使用者登入驗證
}
http://www.elecfans.com 電子發燒友 http://bbs.elecfans.com 電子技術論壇
可將上述實現在C/C++編譯器中生成一個DLL 檔案,這個DLL 中的函式就可以由Tcl/Tk 經過pkg_mkIndex 和package require 命令處理就可呼叫了。以上只是一個簡單的示例,其它函式可以相應地進行處理,也可以用巨集實現大量函式的轉化,此處不再贅述。
5. 小結
通過以上的分析,實現了在Tcl/Tk 指令碼中呼叫C/C++ DLL 中的函式。並在VMSAS 與某PDM 系統整合中進行了驗證。
此種方法對於其它的以Tcl/Tk 實現的模擬或檢測系統中,為提高執行效率或提供新的功能而引入C/C++函式進行擴充套件的情況,都有一定的參考意義。
參考文獻:
[1] 郝建平,蔣科藝等,基於虛擬維修模擬的維修性分析評價及系統實現,數字製造科學,2004.12
[2] Brent B. Welch. Tcl/ tk 組合教程。王道義,喬陶鵬等譯。北京:電子工業出版社,2002.6
[3] 王堅,金革,Tcl/Tk 和 C 語言的介面,計算機應用,2000.12
[4] 周波,楊貫中,蔡宇輝,TCL/TK 語言結構分析及其在網路教學中的應用,計算機工程,2002.4
[5]
http://www.swing.org
[6] news://comp.lang.tcl
[7] http://www.tclchina.com
[8] http://etude.uwaterloo.ca/~ctrudeau/lessons/tcl_tk/tcl_C.html

相關文章