(追記 2014.6.9) 最近の NI-VISA は OS X でも部分的に 64 bit 対応になっているので、通常使用の範囲であれば 32 bit の Python を無理やり使う必要はありません。
Python から USB 接続の実験装置を動かすときに、PySerial はよく使うんですが、VISA という規格もあって、PyVISA というので制御できます。GPIB と USB は備えてるけど GPIB を Mac で動かすのは面倒だし、かつその USB は PySerial で動かないしなんて装置があった場合、PyVISA を使うという手があります。
あと、LAN 接続も可能な機種だと PyVXI11 を使ったりなんてのも可能です。ただ、環境によっては IP 貰えなかったりすることがあるので、PyVISA を使うという解を持っているのは良いことです。
Mac の場合、まずは VISA の library が必要になります。Mac 用の VISA library は恐らく National Instruments (NI) が出しているやつしかなくて、NI-VISA をいうのを使うことになります。Mac 版の最新版は 5.4 です。
.dmg を落としてきて、Mac に install しましょう。10.7 と 10.8 に対応しています。10.9 は試していません。で、ちょっと問題があって、NI-VISA の Mac 版は 32 bit にしか対応していないので (10.8 のみ?)、PyVISA を呼ぶ Python は 32 bit のものを使わないといけません。でも PyROOT とかは 64 bit で動かしたいし、PyVISA のためだけに他の Python 関連のものを全て 32 bit にするのも敗北感があるので、PyVISA だけ 32 bit で動かしたい、と。
2. PyVISA を入れる
$ sudo easy_install pyvisa
これで PyVISA が入ります。
3. PyVISA + NI-VISA を試す
普通に Mac で Python を起動すると、64 bit のものが起動するはずです。32 bit のものを明示的に起動させるには、次のようにします。
$ VERSIONER_PYTHON_PREFER_32_BIT=yes python
さて、Tektronix の AFG3251 という function generator で実験してみます。
from pyvisa.vpp43 import visa_library visa_library.load_library("/Library/Frameworks/Visa.framework/VISA") import visa afg3251 = visa.Instrument("USB0::0x0699::0x0344::C020398::INSTR") afg3251.write("*IDN?") print afg3251.read()
PyVISA が NI-VISA の library を自動で見つけてくれないため、明示的に visa_library.load_library で load する必要があります。これをやらずに import visa をすると、ちゃんと動いてくれません。
その後、VISA の接続に必要な USB の情報を与えてやると、その USB 機器に接続できます。AFG3251 の場合は、本体のメニューを操作すると "USB0::" という情報が出てきます。
後は RS232C の制御と同じような感覚でコマンドを送信するだけです。Delimiter などのややこしい設定は考える必要がありません。
4. subprocess.Process
さて、これだと 32 bit の Python でしか動かないので、64 bit で動いている PyROOT と一緒に使うのは面倒です。ということで、subprocess.Process を使って別の process で動かします。Function generator のように低速な機器であれば、これで全く問題ありません。オシロのように転送速度が効いてくる装置の場合、PyVXI11 などを検討したほうが良いでしょう。
まずは、afg3251.py という script を用意します。これは単純に、第一引数を AFG3251 に送信し、返事を返すことを期待するコマンドであれば stdout にその結果を出力する動作をします。
#!/usr/bin/env python import sys command = sys.argv[1] from pyvisa.vpp43 import visa_library visa_library.load_library("/Library/Frameworks/Visa.framework/VISA") import visa afg3251 = visa.Instrument("USB0::0x0699::0x0344::C020398::INSTR") afg3251.write(command) if command[-1] == "?": sys.stdout.write(afg3251.read())
このファイルは chmod で実行権限を与えておいて下さい。
$ chmod +x afg3251.py
次に、process.py を用意します。これは思いっきりここの真似ですね。32 bit の Python を別 process で起動するため、os.environ に VERSIONER_PYTHON_PREFER_32_BIT を設定しています。これを設定しておけば、subprocess.Popen で別の Python を開いても 32 bit で起動してくれます。
import subprocess import os class AFG3251(object): def __init__(self): pass def excecute(self, command): os.environ["VERSIONER_PYTHON_PREFER_32_BIT"] = "yes" args = ["./afg3251.py", command] logfilename = {"stdout" : "./stdout.log", "stderr" : "./stderr.log" } logfileobj = {} mode = "w" for stream, log in logfilename.iteritems(): try: logfileobj[stream] = file(log, mode) except IOError: print "Cannot open logfile: %s" % log sys.exit(1) subproc_args = { 'stdin' : None, 'stdout' : logfileobj['stdout'], 'stderr' : logfileobj['stderr'], 'close_fds' : True, } try: p = subprocess.Popen(args, **subproc_args) del os.environ["VERSIONER_PYTHON_PREFER_32_BIT"] except OSError: print "Failed to execute command: %s" % args[0] sys.exit(1) ret = p.wait() stdout = open(logfilename["stdout"]).read() stderr = open(logfilename["stderr"]).read() return (stdout, stderr, ret)
afg3251.py 内部で発生した stdout の出力を process.py でそのまま受け取ることはできないので、一度 stdout.log に吐いています。AFG3251 からの応答を見たければ、このファイルを覗けば出力が分ります。
後は、好きなように改造するだけです。