(北京航空航天大學(xué) 自動化科學(xué)與電氣工程學(xué)院,北京 100191)
現(xiàn)如今,隨著可程控測量儀器的不斷發(fā)展,SCPI(Standard Commands for Programmable Instruments,可編程儀器標(biāo)準(zhǔn)命令)依然是用于控制可程控測量儀器的重要標(biāo)準(zhǔn)。如安捷倫、泰克等大型儀器廠商生產(chǎn)的各型號測量儀器依然在廣泛使用。
目前,SCPI命令解釋器擁有多種設(shè)計思路,經(jīng)總結(jié)如下:
① 建立詞法、語法分析器,利用字符串比較的方式進(jìn)行解析[1];
② 采用XML文檔描述SCPI命令,解析時通過查詢XML文檔判斷匹配項[2];
③ 使用鏈表的數(shù)據(jù)結(jié)構(gòu)按照一定順序?qū)CPI命令信息進(jìn)行存儲,解析時遍歷查找[3];
④ 使用鏈?zhǔn)蕉鏄涞臄?shù)據(jù)結(jié)構(gòu)來構(gòu)建和存儲命令集,解析時遍歷查找[4-6];
⑤ 以XML文檔描述SCPI命令,以哈希表構(gòu)建命令樹,遍歷解析SCPI命令[7]。
如上方法均為可行設(shè)計,但是依然存在不足之處。如方法1、方法3、方法4所設(shè)計的SCPI解釋器將SCPI命令樹寫進(jìn)程序中,不具有通用性,若使用儀器發(fā)生變更就需要重新編寫程序。方法2的設(shè)計在解析時直接對位于文件系統(tǒng)中的文本文件進(jìn)行查詢,而與文件系統(tǒng)交換數(shù)據(jù)速度遠(yuǎn)慢于RAM,所以其解析速度很慢,不適于實際應(yīng)用。方法5的設(shè)計雖具有通用性,但是針對存在可省略關(guān)鍵字的等效命令無法準(zhǔn)確識別。所以,基于Python語言提出一種新的設(shè)計思路,致力于解決上述常用SCPI命令解釋器存在的如通用性不強、解析效率低、等效命令識別不清等問題。
SCPI于1990年與IEEE 488.2協(xié)議一起面世。它定義了一套用于控制可編程儀器的標(biāo)準(zhǔn)語法命令格式,于各大儀器廠商間普遍使用,是現(xiàn)在控制可編程儀器的重要標(biāo)準(zhǔn)。
SCPI命令分為兩種。第一種是與IEEE 488.2共用的公用命令,結(jié)構(gòu)圖如圖1所示。語法有以下兩種格式。
① 執(zhí)行命令。
語法格式:“*”+<程控助記符>
示例:*CLS 表示清除狀態(tài)
② 查詢命令。
語法格式:“*”+<程控助記符>+‘?’
示例:*ESE? 表示標(biāo)準(zhǔn)事件狀態(tài)使能查詢
圖1 SCPI公用命令格式
第二種是儀器特定控制命令,結(jié)構(gòu)圖如圖2所示。語法格式有以下兩種。
① 執(zhí)行命令。
語法格式:“:”+<關(guān)鍵字>+<空格>+<參數(shù)>
示例::CONFigure:VOLTage:AC 5,0.01 表示配置測量交流電壓的量程為5 V,精度為0.01 V,以空格字符為界,前部分:CONFigure:VOLTage:AC表示命令,后部分5,0.01為參數(shù)
② 查詢命令。
語法格式:“:”+<關(guān)鍵字>+“?”
示例::CONFigure? 表示查詢配置信息
在SCPI命令中,存在某些可省略關(guān)鍵字。例如前面舉例的命令:CONFigure:VOLTage:AC,若這條命令
圖2 SCPI特定命令格式
在去掉VOLTage關(guān)鍵字后不存在其他歧義,則VOLTage關(guān)鍵字為可省略關(guān)鍵字,即上述命令等同于:CONFigure:AC。
SCPI命令既可以單條命令使用,也可以以“;”連接,多條命令串聯(lián)使用。
SCPI命令樹是用于定義SCPI的命令集,它描述了各關(guān)鍵字之間的層次關(guān)系,相關(guān)的參數(shù)以及必要的注釋。
通過前面對SCPI語法簡單的介紹得知SCPI命令分為公用命令與特定控制命令兩種結(jié)構(gòu)。公用命令結(jié)構(gòu)簡單,而特定控制命令由關(guān)鍵字及符號組成,結(jié)構(gòu)較復(fù)雜。整體形成一個表示命令層次關(guān)系的樹形結(jié)構(gòu)稱為命令樹。而特定控制命令中存在某些可省略關(guān)鍵字,會為解析帶來困難。
從解析的角度而言,SCPI命令實際為一命令字符串,而解析過程即為通過字符串操作于命令樹中尋找匹配的關(guān)鍵字,最終獲取相應(yīng)信息。所以,SCPI命令樹的構(gòu)建是解析SCPI命令的關(guān)鍵,而且構(gòu)建命令樹的方式直接關(guān)乎于解釋器的通用性。為提高解析速度,命令樹需存儲于內(nèi)存中。難點在于需要設(shè)計合適的存儲結(jié)構(gòu)在內(nèi)存中存儲SCPI命令樹的節(jié)點信息。其中,每一個關(guān)鍵字應(yīng)為一節(jié)點,節(jié)點中需存儲如父節(jié)點、兄弟節(jié)點地址等相應(yīng)信息,終葉節(jié)點還需存儲對應(yīng)命令的操作碼等信息。
所以,對于設(shè)計SCPI命令解釋器共有以下難點:
① 設(shè)計命令樹的構(gòu)建方式使設(shè)計的SCPI解釋器具有通用性;
② 設(shè)計合適的存儲結(jié)構(gòu)存儲節(jié)點信息;
③ 處理可省略關(guān)鍵字,使等效命令得到相同的預(yù)期結(jié)果。
由以上分析得知,難點之一為需設(shè)計合適的存儲結(jié)構(gòu)存儲節(jié)點信息,這需要選取合適的數(shù)據(jù)結(jié)構(gòu)。目前大部分的設(shè)計均以C語言實現(xiàn),實現(xiàn)復(fù)雜而且代碼不易維護(hù),使用的數(shù)據(jù)結(jié)構(gòu)多為鏈表或鏈?zhǔn)蕉鏄?,SCPI命令集的添加或刪除操作都很復(fù)雜。而且匹配命令時通過遍歷命令樹實現(xiàn),查詢次數(shù)多效率慢。本文欲采用Python語言開發(fā)。Python是面向?qū)ο蟮母呒壘幊陶Z言,可拓展性極強,具有豐富的開源庫,能快速實現(xiàn)應(yīng)用程序所需的各種功能。以Python實現(xiàn),代碼簡單可維護(hù)性強。同時,Python中一些新型的數(shù)據(jù)結(jié)構(gòu)可以簡化查找過程,提高查找效率。
同時,要解析SCPI命令,需要在內(nèi)存中建立SCPI命令樹。而建立的方式關(guān)乎于SCPI命令解釋器的通用性。如前文中提到,大多數(shù)SCPI解釋器僅針對單個儀器設(shè)計,而不同儀器間SCPI特定控制命令有所不同,他們在設(shè)計時將SCPI命令樹直接寫進(jìn)程序,導(dǎo)致若是使用儀器發(fā)生更改就需重新編寫程序,通用性較差。
為提高SCPI命令解釋器的通用性,欲將SCPI命令樹以某固定文本格式存儲于ROM中,當(dāng)開機時將其解析加載進(jìn)內(nèi)存中,解析過程通過查詢內(nèi)存中命令樹的方式進(jìn)行。以此種方式實現(xiàn),當(dāng)使用儀器發(fā)生變化時,僅需按照固定格式重寫文本文件,無需修改程序,這樣就提高了SCPI命令解釋器的通用性??紤]到XML文件的書寫格式為樹形結(jié)構(gòu),與SCPI命令樹結(jié)構(gòu)相似,故決定采用XML文件來描述SCPI命令樹。
所以,首先需設(shè)計XML文檔來描述SCPI命令樹,之后設(shè)計程序解析XML文檔將其加載進(jìn)內(nèi)存中,最后設(shè)計SCPI命令解釋程序,整體結(jié)構(gòu)設(shè)計如圖3所示。
圖3 SCPI解釋器整體結(jié)構(gòu)
SCPI命令有公用命令與特定控制命令兩種。這里需根據(jù)這兩種命令分別設(shè)計XML標(biāo)簽。
根節(jié)點標(biāo)簽
特定控制命令標(biāo)簽
公用命令標(biāo)簽
下面為部分特定控制命令封裝為XML文檔的實例:
上述的XML文檔實例表示的SCPI命令樹如圖4所示。
圖4 XML文檔對應(yīng)SCPI命令樹
在第1節(jié)分析中得知難點之一就是要設(shè)計合適的存儲結(jié)構(gòu)存儲節(jié)點信息。SCPI命令樹是一種典型的樹形結(jié)構(gòu)。常用的SCPI解釋器通常使用鏈表或鏈?zhǔn)蕉鏄鋪泶鎯?jié)點信息。這種方式在解析時,查詢次數(shù)多、效率慢。同時不易進(jìn)行添加、刪除等操作。
SCPI命令樹擁有嚴(yán)格的層級之分,而解析時即為在各層級中查詢匹配關(guān)鍵字。所以這里擬采用Python中的字典結(jié)構(gòu)來實現(xiàn)查詢操作。字典是一種可變?nèi)萜髂P停铱纱鎯θ我忸愋蛯ο?。字典的每個鍵值對用“:”分割,每個鍵值對之間用“,”分割,整個字典包括在“{}” 中。字典中不可出現(xiàn)重復(fù)的“鍵”,鍵與值一一對應(yīng),它內(nèi)置實現(xiàn)利用了hash函數(shù),所以無論字典多么龐大,它的查詢和刪除都是常數(shù)時間的。故而設(shè)計這樣一個節(jié)點結(jié)構(gòu):其中存儲節(jié)點各相關(guān)信息,其中一個信息為下層與之關(guān)聯(lián)節(jié)點的地址集,以字典結(jié)構(gòu)實現(xiàn)。字典的鍵為簡寫關(guān)鍵字,與之對應(yīng)的值為對應(yīng)關(guān)鍵字的節(jié)點地址。則內(nèi)存中SCPI命令樹結(jié)構(gòu)如圖5所示。以此種方式實現(xiàn)的命令樹,在解析時若SCPI命令由3個關(guān)鍵字組成,則僅需經(jīng)過3次查詢即可得到結(jié)果,與常用的鏈表及鏈?zhǔn)蕉鏄涞姆绞较啾却蟠鬁p少了查詢次數(shù),提高了效率。
圖5 內(nèi)存中SCPI命令樹結(jié)構(gòu)
前面提到,SCPI特定控制命令中存在某些可省略關(guān)鍵字,那么刪除或保留可省略關(guān)鍵字的兩條命令應(yīng)為等效命令,在解析后應(yīng)該得到同樣的結(jié)果。
仍以之前示例為例,:CONFigure:VOLTage:AC中VOLTage為可省略關(guān)鍵字。則針對可省略關(guān)鍵字的處理過程為,將可省略關(guān)鍵字VOLT的下層地址集字典拼接至上層的地址集字典中。那么在解析完整命令時,在地址集中可以逐級向下查找得到最終結(jié)果,而在解析刪除了可省略關(guān)鍵字的等效命令時,即可越過可省略關(guān)鍵字查詢到下一級的命令。處理方法如圖6所示。
圖6 可省略關(guān)鍵字處理方法
確定節(jié)點存儲結(jié)構(gòu)后,需設(shè)計解析建立程序。其中需按照公用命令與特定控制命令分別設(shè)計。
公用命令若以層級劃分僅有一層,即無需上面設(shè)計的結(jié)構(gòu),僅以一個字典結(jié)構(gòu)即可滿足存儲。故而公用命令的建立過程如圖7所示。
圖7 內(nèi)存公用命令集建立流程
特定控制命令的SCPI命令樹建立比較復(fù)雜。由于每個子系統(tǒng)命令樹的深度并不固定,所以這里擬采用一個遞歸函數(shù)來建立,以XML文檔中的IsEnd屬性來作為遞歸終止條件。遞歸函數(shù)流程如圖8所示。命令樹的創(chuàng)建首先通過解析XML文檔獲取根節(jié)點,然后獲取相應(yīng)的屬性同時建立層級節(jié)點地址集,最終以參數(shù)傳入相應(yīng)屬性值以及地址集來調(diào)用遞歸函數(shù)實現(xiàn)SCPI命令樹于內(nèi)存中的建立,而命令樹建立的具體流程如圖9所示。
圖8 遞歸建立函數(shù)流程
圖9 內(nèi)存中特定控制命令樹建立流程
目前,大多SCPI命令解釋器均以字符串比較的方式實現(xiàn)。而本文中由于使用了字典結(jié)構(gòu),那么在每一層級字典中查詢時即完成了關(guān)鍵字的匹配,無需進(jìn)行進(jìn)一步的字符串比較。而在此之前,首先要對SCPI命令進(jìn)行一些處理。
SCPI命令可單條使用亦可以“;”連接多條使用。首先,若傳入的為多條串聯(lián)的命令,則需以“;”為分隔符將其分割為單條命令。Python中有一split函數(shù)可非常方便地實現(xiàn)此功能。它可通過指定分隔符對字符串進(jìn)行切片,結(jié)果存儲于list結(jié)構(gòu)中,若不指定分隔符則默認(rèn)以空格作為分隔符。
在分離出單條命令后,針對每條命令提取首字符,分辨其為“:”還是“*”。若首字符為“:”則為特定控制命令;若首字符為“*”則為公用命令,對兩種命令需分別做處理。
之后,提取尾字符,分辨尾字符是否為“?”,若尾字符為“?”則為查詢命令,否則為執(zhí)行命令。若為公用命令的查詢命令,則去掉尾字符匹配命令獲取對應(yīng)執(zhí)行函數(shù)的操作碼;若為公用命令的執(zhí)行命令,若有參數(shù)則首先分離參數(shù),之后匹配命令獲取操作碼。若為特定控制命令的查詢命令,則去掉尾字符,以“:”分割各關(guān)鍵字,于命令樹中逐層查詢匹配關(guān)鍵字最終獲取操作碼;若為特定控制命令的執(zhí)行命令,首先以空格為分隔符分離命令與參數(shù),之后以“:”為分隔符分離各關(guān)鍵字,于命令樹中逐層查詢匹配關(guān)鍵字最終獲取操作碼。得到操作碼及參數(shù)后即可由下層進(jìn)行函數(shù)調(diào)用。具體流程如圖10所示。
本文敘述了SCPI命令解釋器的整個設(shè)計過程。與常用的SCPI命令解釋器設(shè)計思路及實現(xiàn)方式均有所不同。大部分常用的SCPI命令解釋器的設(shè)計均以C語言實現(xiàn),實現(xiàn)復(fù)雜且代碼不易維護(hù),使用的數(shù)據(jù)結(jié)構(gòu)多為鏈表或鏈?zhǔn)蕉鏄?,在匹配命令時以遍歷的方式查詢命令樹,查詢次數(shù)多效率慢。而本文敘述的SCPI命令解釋器采用Python語言開發(fā)。通過靈活運用Python語言的優(yōu)勢,設(shè)計出的SCPI命令解釋器簡潔實用,代碼易維護(hù)。通過使用Python的字典結(jié)構(gòu)設(shè)計的內(nèi)存存儲結(jié)構(gòu)來存儲SCPI命令樹,在解析時減少了查詢次數(shù),提高了解析效率。如常用的鏈表及鏈?zhǔn)蕉鏄涞姆绞绞且环N遍歷的思路,若解析一個由3個關(guān)鍵字構(gòu)成的命令時最理想的情況是查詢3次,但絕大部分情況查詢次數(shù)應(yīng)大于甚至遠(yuǎn)大于3次,即最不理想的狀況應(yīng)為遍歷完整個命令樹。而本文利用Python字典實現(xiàn)的存儲結(jié)構(gòu),在解析時僅需進(jìn)行3次查詢即可得到結(jié)果。
同時,本文亦針對可省略關(guān)鍵字給出處理方式,解決了等效命令識別不清的問題。即通過將可省略關(guān)鍵字的下層地址集字典拼接至上層的地址集字典中實現(xiàn),使其在查找時能越過可省略關(guān)鍵字直接查詢下層地址。
而且通過以XML文檔描述SCPI命令集,當(dāng)開機時將其解析加載進(jìn)內(nèi)存中的方式,使SCPI命令解釋器
擁有通用性。針對不同儀器,僅需根據(jù)固定格式重寫XML文檔即可實現(xiàn)SCPI命令樹的替換而無需修改程序。
由于Python語言跨平臺的特性,本文設(shè)計的SCPI解釋器同樣也具有跨平臺的特性。通過實驗驗證,在PC及ARM上均運行良好,解析結(jié)果快速準(zhǔn)確。