基于Python的SCPI命令解释器的设计

2019-09-20 00:39
测控技术 2019年5期
关键词:关键字省略字典

(北京航空航天大学 自动化科学与电气工程学院,北京 100191)

现如今,随着可程控测量仪器的不断发展,SCPI(Standard Commands for Programmable Instruments,可编程仪器标准命令)依然是用于控制可程控测量仪器的重要标准。如安捷伦、泰克等大型仪器厂商生产的各型号测量仪器依然在广泛使用。

目前,SCPI命令解释器拥有多种设计思路,经总结如下:

① 建立词法、语法分析器,利用字符串比较的方式进行解析[1];

② 采用XML文档描述SCPI命令,解析时通过查询XML文档判断匹配项[2];

③ 使用链表的数据结构按照一定顺序将SCPI命令信息进行存储,解析时遍历查找[3];

④ 使用链式二叉树的数据结构来构建和存储命令集,解析时遍历查找[4-6];

⑤ 以XML文档描述SCPI命令,以哈希表构建命令树,遍历解析SCPI命令[7]。

如上方法均为可行设计,但是依然存在不足之处。如方法1、方法3、方法4所设计的SCPI解释器将SCPI命令树写进程序中,不具有通用性,若使用仪器发生变更就需要重新编写程序。方法2的设计在解析时直接对位于文件系统中的文本文件进行查询,而与文件系统交换数据速度远慢于RAM,所以其解析速度很慢,不适于实际应用。方法5的设计虽具有通用性,但是针对存在可省略关键字的等效命令无法准确识别。所以,基于Python语言提出一种新的设计思路,致力于解决上述常用SCPI命令解释器存在的如通用性不强、解析效率低、等效命令识别不清等问题。

1 SCPI命令介绍

1.1 SCPI命令简介

SCPI于1990年与IEEE 488.2协议一起面世。它定义了一套用于控制可编程仪器的标准语法命令格式,于各大仪器厂商间普遍使用,是现在控制可编程仪器的重要标准。

1.2 SCPI命令语法

SCPI命令分为两种。第一种是与IEEE 488.2共用的公用命令,结构图如图1所示。语法有以下两种格式。

① 执行命令。

语法格式:“*”+<程控助记符>

示例:*CLS 表示清除状态

② 查询命令。

语法格式:“*”+<程控助记符>+‘?’

示例:*ESE? 表示标准事件状态使能查询

图1 SCPI公用命令格式

第二种是仪器特定控制命令,结构图如图2所示。语法格式有以下两种。

① 执行命令。

语法格式:“:”+<关键字>+<空格>+<参数>

示例::CONFigure:VOLTage:AC 5,0.01 表示配置测量交流电压的量程为5 V,精度为0.01 V,以空格字符为界,前部分:CONFigure:VOLTage:AC表示命令,后部分5,0.01为参数

② 查询命令。

语法格式:“:”+<关键字>+“?”

示例::CONFigure? 表示查询配置信息

在SCPI命令中,存在某些可省略关键字。例如前面举例的命令:CONFigure:VOLTage:AC,若这条命令

图2 SCPI特定命令格式

在去掉VOLTage关键字后不存在其他歧义,则VOLTage关键字为可省略关键字,即上述命令等同于:CONFigure:AC。

SCPI命令既可以单条命令使用,也可以以“;”连接,多条命令串联使用。

SCPI命令树是用于定义SCPI的命令集,它描述了各关键字之间的层次关系,相关的参数以及必要的注释。

1.3 SCPI命令解析难点

通过前面对SCPI语法简单的介绍得知SCPI命令分为公用命令与特定控制命令两种结构。公用命令结构简单,而特定控制命令由关键字及符号组成,结构较复杂。整体形成一个表示命令层次关系的树形结构称为命令树。而特定控制命令中存在某些可省略关键字,会为解析带来困难。

从解析的角度而言,SCPI命令实际为一命令字符串,而解析过程即为通过字符串操作于命令树中寻找匹配的关键字,最终获取相应信息。所以,SCPI命令树的构建是解析SCPI命令的关键,而且构建命令树的方式直接关乎于解释器的通用性。为提高解析速度,命令树需存储于内存中。难点在于需要设计合适的存储结构在内存中存储SCPI命令树的节点信息。其中,每一个关键字应为一节点,节点中需存储如父节点、兄弟节点地址等相应信息,终叶节点还需存储对应命令的操作码等信息。

所以,对于设计SCPI命令解释器共有以下难点:

① 设计命令树的构建方式使设计的SCPI解释器具有通用性;

② 设计合适的存储结构存储节点信息;

③ 处理可省略关键字,使等效命令得到相同的预期结果。

2 SCPI命令解释器整体设计

由以上分析得知,难点之一为需设计合适的存储结构存储节点信息,这需要选取合适的数据结构。目前大部分的设计均以C语言实现,实现复杂而且代码不易维护,使用的数据结构多为链表或链式二叉树,SCPI命令集的添加或删除操作都很复杂。而且匹配命令时通过遍历命令树实现,查询次数多效率慢。本文欲采用Python语言开发。Python是面向对象的高级编程语言,可拓展性极强,具有丰富的开源库,能快速实现应用程序所需的各种功能。以Python实现,代码简单可维护性强。同时,Python中一些新型的数据结构可以简化查找过程,提高查找效率。

同时,要解析SCPI命令,需要在内存中建立SCPI命令树。而建立的方式关乎于SCPI命令解释器的通用性。如前文中提到,大多数SCPI解释器仅针对单个仪器设计,而不同仪器间SCPI特定控制命令有所不同,他们在设计时将SCPI命令树直接写进程序,导致若是使用仪器发生更改就需重新编写程序,通用性较差。

为提高SCPI命令解释器的通用性,欲将SCPI命令树以某固定文本格式存储于ROM中,当开机时将其解析加载进内存中,解析过程通过查询内存中命令树的方式进行。以此种方式实现,当使用仪器发生变化时,仅需按照固定格式重写文本文件,无需修改程序,这样就提高了SCPI命令解释器的通用性。考虑到XML文件的书写格式为树形结构,与SCPI命令树结构相似,故决定采用XML文件来描述SCPI命令树。

所以,首先需设计XML文档来描述SCPI命令树,之后设计程序解析XML文档将其加载进内存中,最后设计SCPI命令解释程序,整体结构设计如图3所示。

图3 SCPI解释器整体结构

3 SCPI命令树建立过程的设计

3.1 SCPI命令树XML文档的设计

SCPI命令有公用命令与特定控制命令两种。这里需根据这两种命令分别设计XML标签。

根节点标签,属性Instrument表示当前SCPI命令树所属仪器类别。

特定控制命令标签,属性Cmd、Level、LastLevel、IsEnd、Omissible、ImplementID、QueryID分别表示关键字长型助记符、当前关键字所属层级、当前关键字上一层级、此节点是否为终叶节点、此关键字是否可省略、执行函数ID、查询函数ID。

公用命令标签,属性Cmd、ImplementID、QueryID分别表示公用命令、查询函数ID、查询函数ID。

下面为部分特定控制命令封装为XML文档的实例:

上述的XML文档实例表示的SCPI命令树如图4所示。

图4 XML文档对应SCPI命令树

3.2 内存节点存储结构设计

在第1节分析中得知难点之一就是要设计合适的存储结构存储节点信息。SCPI命令树是一种典型的树形结构。常用的SCPI解释器通常使用链表或链式二叉树来存储节点信息。这种方式在解析时,查询次数多、效率慢。同时不易进行添加、删除等操作。

SCPI命令树拥有严格的层级之分,而解析时即为在各层级中查询匹配关键字。所以这里拟采用Python中的字典结构来实现查询操作。字典是一种可变容器模型,且可存储任意类型对象。字典的每个键值对用“:”分割,每个键值对之间用“,”分割,整个字典包括在“{}” 中。字典中不可出现重复的“键”,键与值一一对应,它内置实现利用了hash函数,所以无论字典多么庞大,它的查询和删除都是常数时间的。故而设计这样一个节点结构:其中存储节点各相关信息,其中一个信息为下层与之关联节点的地址集,以字典结构实现。字典的键为简写关键字,与之对应的值为对应关键字的节点地址。则内存中SCPI命令树结构如图5所示。以此种方式实现的命令树,在解析时若SCPI命令由3个关键字组成,则仅需经过3次查询即可得到结果,与常用的链表及链式二叉树的方式相比大大减少了查询次数,提高了效率。

图5 内存中SCPI命令树结构

3.3 可省略关键字的处理

前面提到,SCPI特定控制命令中存在某些可省略关键字,那么删除或保留可省略关键字的两条命令应为等效命令,在解析后应该得到同样的结果。

仍以之前示例为例,:CONFigure:VOLTage:AC中VOLTage为可省略关键字。则针对可省略关键字的处理过程为,将可省略关键字VOLT的下层地址集字典拼接至上层的地址集字典中。那么在解析完整命令时,在地址集中可以逐级向下查找得到最终结果,而在解析删除了可省略关键字的等效命令时,即可越过可省略关键字查询到下一级的命令。处理方法如图6所示。

图6 可省略关键字处理方法

3.4 内存命令树建立程序设计

确定节点存储结构后,需设计解析建立程序。其中需按照公用命令与特定控制命令分别设计。

公用命令若以层级划分仅有一层,即无需上面设计的结构,仅以一个字典结构即可满足存储。故而公用命令的建立过程如图7所示。

图7 内存公用命令集建立流程

特定控制命令的SCPI命令树建立比较复杂。由于每个子系统命令树的深度并不固定,所以这里拟采用一个递归函数来建立,以XML文档中的IsEnd属性来作为递归终止条件。递归函数流程如图8所示。命令树的创建首先通过解析XML文档获取根节点,然后获取相应的属性同时建立层级节点地址集,最终以参数传入相应属性值以及地址集来调用递归函数实现SCPI命令树于内存中的建立,而命令树建立的具体流程如图9所示。

图8 递归建立函数流程

图9 内存中特定控制命令树建立流程

4 SCPI命令解析程序设计

目前,大多SCPI命令解释器均以字符串比较的方式实现。而本文中由于使用了字典结构,那么在每一层级字典中查询时即完成了关键字的匹配,无需进行进一步的字符串比较。而在此之前,首先要对SCPI命令进行一些处理。

SCPI命令可单条使用亦可以“;”连接多条使用。首先,若传入的为多条串联的命令,则需以“;”为分隔符将其分割为单条命令。Python中有一split函数可非常方便地实现此功能。它可通过指定分隔符对字符串进行切片,结果存储于list结构中,若不指定分隔符则默认以空格作为分隔符。

在分离出单条命令后,针对每条命令提取首字符,分辨其为“:”还是“*”。若首字符为“:”则为特定控制命令;若首字符为“*”则为公用命令,对两种命令需分别做处理。

之后,提取尾字符,分辨尾字符是否为“?”,若尾字符为“?”则为查询命令,否则为执行命令。若为公用命令的查询命令,则去掉尾字符匹配命令获取对应执行函数的操作码;若为公用命令的执行命令,若有参数则首先分离参数,之后匹配命令获取操作码。若为特定控制命令的查询命令,则去掉尾字符,以“:”分割各关键字,于命令树中逐层查询匹配关键字最终获取操作码;若为特定控制命令的执行命令,首先以空格为分隔符分离命令与参数,之后以“:”为分隔符分离各关键字,于命令树中逐层查询匹配关键字最终获取操作码。得到操作码及参数后即可由下层进行函数调用。具体流程如图10所示。

5 结束语

本文叙述了SCPI命令解释器的整个设计过程。与常用的SCPI命令解释器设计思路及实现方式均有所不同。大部分常用的SCPI命令解释器的设计均以C语言实现,实现复杂且代码不易维护,使用的数据结构多为链表或链式二叉树,在匹配命令时以遍历的方式查询命令树,查询次数多效率慢。而本文叙述的SCPI命令解释器采用Python语言开发。通过灵活运用Python语言的优势,设计出的SCPI命令解释器简洁实用,代码易维护。通过使用Python的字典结构设计的内存存储结构来存储SCPI命令树,在解析时减少了查询次数,提高了解析效率。如常用的链表及链式二叉树的方式是一种遍历的思路,若解析一个由3个关键字构成的命令时最理想的情况是查询3次,但绝大部分情况查询次数应大于甚至远大于3次,即最不理想的状况应为遍历完整个命令树。而本文利用Python字典实现的存储结构,在解析时仅需进行3次查询即可得到结果。

同时,本文亦针对可省略关键字给出处理方式,解决了等效命令识别不清的问题。即通过将可省略关键字的下层地址集字典拼接至上层的地址集字典中实现,使其在查找时能越过可省略关键字直接查询下层地址。

而且通过以XML文档描述SCPI命令集,当开机时将其解析加载进内存中的方式,使SCPI命令解释器

拥有通用性。针对不同仪器,仅需根据固定格式重写XML文档即可实现SCPI命令树的替换而无需修改程序。

由于Python语言跨平台的特性,本文设计的SCPI解释器同样也具有跨平台的特性。通过实验验证,在PC及ARM上均运行良好,解析结果快速准确。

猜你喜欢
关键字省略字典
履职尽责求实效 真抓实干勇作为——十个关键字,盘点江苏统战的2021
偏旁省略异体字研究
成功避开“关键字”
字典的由来
大头熊的字典
正版字典
中间的省略
智能垃圾箱
省略
省略