岳 萍
(電子科技大學(xué)成都學(xué)院電子工程系,四川 成都611731)
組態(tài)軟件以集中式控制系統(tǒng)為基礎(chǔ),由此發(fā)展而來的。體現(xiàn)了“分散控制、集中操作、組態(tài)方便、分級(jí)管理”的思想。作為集散控制系統(tǒng)軟件平臺(tái)出現(xiàn)的組態(tài)軟件,具有友好的人機(jī)功能,采用圖像顯示,能在線進(jìn)行系統(tǒng)組態(tài)和參數(shù)設(shè)置。而嵌入在組態(tài)軟件中的腳本模塊則能夠?yàn)槠涮峁┒伍_發(fā)功能,使系統(tǒng)的開發(fā)更加靈活,組態(tài)更加地方便,功能更加地豐富和完善。
綜合多種流行的組態(tài)軟件分析,其中的腳本語言實(shí)現(xiàn)方式大致有以下三種:第一種是內(nèi)置的類似于C或者Basic的語言;第二種是采用VBA的編程語言;第三種是目前來講只有少數(shù)組態(tài)軟件所采用的面向?qū)ο蟮哪_本語言。本文以目前在瀏覽器Mozilla FireFox上用到的底層引擎SpiderMonkey為技術(shù)基礎(chǔ),實(shí)現(xiàn)基于SpiderMonkey的腳本引擎功能,并實(shí)現(xiàn)腳本模塊與組態(tài)軟件各模塊間的實(shí)時(shí)通訊。
腳本模塊在組態(tài)軟件中的地位非常關(guān)鍵,它是連接前臺(tái)的圖形界面開發(fā)/運(yùn)行模塊和臺(tái)的數(shù)據(jù)庫(kù)模塊的紐帶。圖形界面開發(fā)模塊通過調(diào)用腳本模塊進(jìn)行配置,而腳本模塊又從數(shù)據(jù)庫(kù)中讀取或?qū)懭雽?shí)時(shí)數(shù)據(jù)。當(dāng)圖形界面的運(yùn)行模塊進(jìn)入運(yùn)行時(shí),就會(huì)周期性的執(zhí)行在開發(fā)模塊中配置好的腳本程序。
本設(shè)計(jì)中設(shè)計(jì)腳本模塊是基于SpiderMonkey的,而SpiderMonkey是一個(gè)JavaScipt解釋器引擎的API庫(kù)。它最大的特點(diǎn)是其開放性和可移植性。這使得該腳本模塊可以在多種平臺(tái)上運(yùn)行。
應(yīng)用程序腳本模塊的設(shè)計(jì)的根本在于怎樣將JavaScript引擎嵌入到應(yīng)用程序中去。嵌入的過程如下:
JavaScript引擎利用Runtime來管理對(duì)象、上下文和與JavaScript的函數(shù)及腳本相關(guān)的變量,為應(yīng)用程序需要的對(duì)象、上下文句柄、變量和腳本分配所需要的內(nèi)存。該引擎在執(zhí)行函數(shù)或腳本程序之前,首先要通過調(diào)用JavaScript引擎的API函數(shù)JS_NewRuntime來初始化一個(gè)RunTime,語句為:JSRuntime*rt=JS_NewRuntime(1000000L);
Runtime可以創(chuàng)建一個(gè),也可以創(chuàng)建多個(gè)。當(dāng)不再需要Runtime時(shí),則通過調(diào)用其API函數(shù)JS_DestroyRunTime來釋放內(nèi)存資源。
定義上下文句柄的目的在于保護(hù)同時(shí)存在的腳本執(zhí)行狀態(tài)和程序結(jié)果。而定義多少個(gè)上下文取決于需要在應(yīng)用程序中同時(shí)使用的腳本數(shù)。一個(gè)上下文代表應(yīng)用程序中的一個(gè)腳本,JavaScript引擎?zhèn)鬟f上下文信息給運(yùn)行腳本的線程。可以通過調(diào)用JavaScript中的API函數(shù)JS_NewContext來創(chuàng)建新的上下文,一個(gè)上下文和一個(gè)Runtime關(guān)聯(lián),語句為:JSContext*cx=JS_NewContext(rt,8192);
釋放上下文的函數(shù)為JS_DestroyContext。
首先,通過調(diào)用JS_NewObject函數(shù)創(chuàng)建全局對(duì)象,創(chuàng)建完畢后,則調(diào)用JS_InitStandardClasses函數(shù)初始化全局對(duì)象。
通過創(chuàng)建Runtime,創(chuàng)建上下文,創(chuàng)建全局對(duì)象三步之后,就可以把JavaScript引擎嵌入到應(yīng)用程序中了,這時(shí)的應(yīng)用程序就可以對(duì)輸入的JavaScript語言進(jìn)行編譯運(yùn)行了。
此時(shí)只是完成了JavaScript引擎嵌入的最基本的工作,而如果想使該腳本引擎更加靈活,則需要用到自定義的對(duì)象屬性方法。
可以采用將定義了對(duì)象的性質(zhì)和方法的代碼插入到應(yīng)用程序中的方法,調(diào)用引擎來初始化一個(gè)新的對(duì)象。這種方式適用于實(shí)時(shí)性較高的工業(yè)自動(dòng)化領(lǐng)域。
創(chuàng)建自定義對(duì)象的步驟如下:
1)創(chuàng)建一個(gè)JSPropertySpec以及一個(gè)JSFunctionSpec數(shù)據(jù)類型。
2)調(diào)用JS_NewObject和JS_ConstructObject函數(shù)來實(shí)例化該對(duì)象。
3)調(diào)用JS_DefineFunctions函數(shù)來創(chuàng)建對(duì)象的方法。
4)調(diào)用JS_DefineProperties函數(shù)來創(chuàng)建對(duì)象的屬性。
JavaScript有自己獨(dú)特的數(shù)據(jù)類型,這些數(shù)據(jù)類型,有的可以直接應(yīng)用于C語言,但是有的需要經(jīng)過轉(zhuǎn)換才能夠被C語言所識(shí)別??梢酝ㄟ^JavaScript引擎提供的API函數(shù)將數(shù)據(jù)類型在JavaScript和C之間進(jìn)行轉(zhuǎn)換。這些API函數(shù)包括:JS_ValueToString函數(shù)、JS_GetStringChars函數(shù)、JS_NewStringCopyN函數(shù)、STRING_TO_JSVAL函數(shù)等。讀者可以自行查找各個(gè)函數(shù)的用法。
在執(zhí)行腳本之前,可以調(diào)用API函數(shù)JS_CompileScript來進(jìn)行預(yù)編譯:
JS_CompileScript(Context(),GlobalObject(),lines,strlen(lines),0,0);
編譯正確,返回JS_TRUE,否則,返回JS_FALSE。編譯正確后,通過調(diào)用JS_EvaluateScript方法進(jìn)行對(duì)腳本執(zhí)行:
JS_EvaluateScript(Context(),GlobalObject(),lines,strlen(lines),0,0,rr);
同樣,如果執(zhí)行正確,則返回JS_TRUE,否則,返回JS_FALSE。
跟其它的程序運(yùn)行過程一樣,在程序結(jié)束前,需要對(duì)腳本引擎進(jìn)行清理,SpiderMonkey采用的是自動(dòng)內(nèi)存管理機(jī)制,調(diào)用下面兩個(gè)函數(shù)就可以將垃圾清理干凈:JS_DestroyContext(cx);JS_DestroyRuntime(rt);
通過對(duì)組態(tài)軟件進(jìn)行JavaScript引擎的嵌入,使得組態(tài)軟件的可擴(kuò)展性得到了更好的發(fā)揮,靈活性更進(jìn)一步得到了提高。