我做到了,尽管我对结果很不满意。自从我接触到这些已经三年了,所以我不知道为什么我会以这种方式做一些事情。我知道我在组织一切以实现便携性方面遇到了很多麻烦。我敢肯定,在漫长的等待之后,我会收到大量关于我应该如何做的建议。
我编写了解析函数来解释 SCPI 命令。他们可以将命令与参数分开,确定必需和可选参数,识别查询等。例如:
def command_name(scpi_command):
cmd = scpi_command.split(' ')[0]
new = ''
for i, c in enumerate(cmd):
if c in '[]':
continue
if c.isupper() or c.isdigit():
new += c.lower()
continue
if c == ':':
new += '_'
continue
if c == '?':
new += '_qry'
break
return new
来自CONFigure[:VOLTage]:DC [{<range>|AUTO|MIN|MAX|DEF} [, {<resolution>|MIN|MAX|DEF}]] 的命令可以解析为conf_volt_dc 或conf_dc。我在实验中没有使用删节选项。我认为我的一些不满源于超长的方法名称。
我编写了一个构建器脚本来读取文件中的命令,解析它们,然后编写一个新脚本,其中包含扩展“命令处理程序”的“命令集”类。每个命令都被解析并转换为几个样板方法之一,例如:
query_str_no_args = r'''
def {name}(self):
"""SCPI instrument query.
{s}
"""
cmd = '{s}'
return self._command_handler(command=cmd)
'''
解析器还包括一个命令处理程序类来处理命令和参数。每个命令都是从它的示例文本中解析出来的,ala >>> _command_handler(0.1, 'MAX', command='CONFigure[:VOLTage]:DC [{<range>|AUTO|MIN|MAX|DEF} [, {<resolution>|MIN|MAX|DEF}]]'。验证参数(如果有),并重建命令字符串并将其发送到仪器,
class Cmd_Handler():
def _command_handler(self, *args, command):
# command string 'SENS:VOLT:RANG'
cmd_str = command_string(command)
# argument dictionary {0: [True, '<range>, 'AUTO', 'MIN', 'MAX', 'DEF'],
# 1: [False, '<resolution>', 'MIN', 'MAX', 'DEF']}
arg_dict = command_args(command)
if debug_mode: print(arg_dict)
for k, v in arg_dict.items():
for i, j in enumerate(v[1:]):
...
# Validate arguments
# count mandatory arguments
if len(args) < len([arg_dict[k] for k in arg_dict.keys() if arg_dict[k][0]]):
...
if '?' in cmd_str:
return self._query(cmd_str + arg_str)
else:
return self._write(cmd_str + arg_str)
构建器完成后,您会得到一个大脚本(Ag34401 和 Ag34461 文件分别为 2600 和 3600 行),如下所示:
#!/usr/bin/env python3
from scpi.scpi_parse import Cmd_Handler
class Ag34461A_CS(Cmd_Handler):
...
def calc_scal_stat(self, *args):
"""SCPI instrument command.
CALCulate:SCALe[:STATe] {OFF|ON}
"""
cmd = 'CALCulate:SCALe[:STATe] {OFF|ON}'
return self._command_handler(*args, command=cmd)
def calc_scal_stat_qry(self):
"""SCPI instrument query.
CALCulate:SCALe[:STATe]?
"""
cmd = 'CALCulate:SCALe[:STATe]?'
return self._command_handler(command=cmd)
然后你用你的短而甜美的乐器类扩展这个类:
#!/usr/bin/env python3
import pyvisa as visa
from scpi.cmd_sets.ag34401a_cs import Ag34401A_CS # Extend the command set
class Ag34401A(Ag34401A_CS):
def __init__(self, resource_name=None):
rm = visa.ResourceManager()
self._inst = None
if resource_name:
inst = rm.open_resource(resource_name)
else:
for resource in rm.list_resources():
inst = rm.open_resource(resource)
if '34401' in inst.query("*IDN?"):
self._inst = inst
break
if not self._inst:
raise visa.errors.VisaIOError
assert isinstance(self._inst, visa.resources.GPIBInstrument)
self._query = self._inst.query
self._write = self._inst.write
我还有一个 SCPI 仪器父类,我试图用 所有 星号命令(*IDN?、*CLS 等)填充它。我对之前的结果不满意并且被一些星指令的日益复杂性(其中一些似乎真的是特定于仪器的)吓倒了,并放弃了它。这是第一位
class SCPI_Instrument(object, metaclass=ABCMeta):
@abstractmethod
def query(self, message, delay):
pass
@abstractmethod
def write(self, message, delay):
pass
# *CLS - Clear Status
def cls(self, termination = None, encoding = None):
"""Clears the instrument status byte by emptying the error queue and clearing all event registers.
Also cancels any preceding *OPC command or query."""
return self.write("*CLS", termination, encoding)
最后,我能够挤进去的文档并没有我想要的那么有用。自动完成功能无法按名称提示输入参数,并且建议每条命令 的帮助没有我预期的那么大。尝试保持导入路径对我来说已经够难了,更不用说最终可能会使用我的代码的人了。
最终,编码从未成为我的工作描述的一部分,我被调到了一个更不可能尝试仪器控制的职位。