.. title:: Pythonで心理実験 - 例題21-4
例題21-4:pyAPIUSBPを使う
======================================
**A:** すでに例題21-1、21-3の冒頭で告知していますが、例題21-1、21-3で取り上げたContec社のAPI-USBPライブラリをpythonから利用するパッケージを作ったので少し取り上げておきます。この頃筆者のSが出てきたり私やB君が失踪したりするパターンでマンネリ化しているので、びしっと進めたいと思う。
**B:** おっ、いつもながら最初のうちはびしっとしてますな。いつまで続きますやら。
**A:** うるさい。えーと、pyAPIUSBPの公式サイトは `http://pyapiusbp.sourceforge.net/ `_ です。ダウンロードは `http://sourceforge.net/projects/pyapiusbp/files/ `_ からどうぞ。
**B:** インストーラーがexeのやつとzipのやつがありますね。exeがWindows用でzipがその他のOS用ですね?
**A:** いや、API-USBPがWindows用なので、どちらもWindows用。exeインストーラーが好きな人とdistutilsが好きな人向け。
**B:** ありゃ、Windows専用ですか。根性がないなあ。
**A:** 根性とかいう問題じゃないっつーの。使用方法は `http://pyapiusbp.sourceforge.net/smp/index.html `_ に簡単な例が出ています。 **サンプルを実行する前に必ずAPI-USBPを先にインストールしてDIOユニットまたはAIOユニットを先に使用できるようにしておいてください。**
**B:** ほいほい、ばっちりですよ。
**A:** 詳しい使用法はサンプルとヘルプをご覧ください、というわけにはいかないでしょうからサンプル01を以下に取り上げましょう。アナログ一件入力のサンプルです。
**B:** 一件入力?
**A:** データを一個読み込むってこった。
.. code-block:: python
:linenos:
import pyAPIUSBP
import time
import sys
# Initialize
try:
aio = pyAPIUSBP.AIO.AIO('AIO000')
except:
print 'AIO000 is not found.'
sys.exit()
# Set AI range
aio.setAiRangeAll(pyAPIUSBP.AIO.PM5)
if sys.platform == 'win32':
timefunc = time.clock
else:
timefunc = time.time
startTime = timefunc()
# Read channel 0 for 10 seconds
while timefunc()-startTime<10:
print aio.singleAi(0)
time.sleep(0.5)
**A:** 基本的な方針としては、pyAPIUSBPをimportしてpyAPISUBP.AIO.AIOクラスのインスタンスを生成します。1行目と7行目ですね。6行目からのtry文で初期化しているのは、デバイス名AIO000となるデバイスが接続されていない状態で実行した時にエラーメッセージを表示するためです。
**B:** try文でトラップしなかったらどうなるんですか?
**A:** 普通にエラーで停止する。
**B:** じゃあ別にtry文を使わなくてもいいじゃないですか。
**A:** 文句は作者のSに言ってくれよ。で、7行目が成功したら、変数aioにpyAPISUBP.AIO.AIOクラスのインスタンスが格納されています。こいつのクラスメソッドを用いていろいろな処理を行います。13行目ではsetAiRangeAllというメソッドを用いてアナログ入力のレンジを設定しています。原則として、API-USBPの **Aio** **F** ooBarという関数が **f** ooBarという名前のメソッドとして実装されています。 **Aio** **S** etAiRangeAllが **s** etAiRangeAllに対応しているというわけですね。大文字小文字にご注意ください。
**B:** ふむふむ。
**A:** で、注目していただきたいのがsetAiRangeAllの引数。pyAPIUSBP.AIO.PM5となっていますが、これはAPI-USBPのAIOライブラリで定義されている定数PM5に対応しています。このように、API-USBPライブラリのincludeファイルで宣言されている定数FOOをpyAPISUBP.AIO.FOOという形で利用することが出来ます。
**B:** なるほど。
**A:** さらに言うと、pyAPISUBP.AIO.getRangeStringという関数が用意されています。このメソッドを使うと以下のようにレンジを表す定数から対応する文字列を得ることが出来ます。
::
>>> pyAPIUSBP.AIO.getRangeString(1)
'PM5'
**B:** んー、便利なような、使い道がなさそうな。
**A:** ついでに紹介しておきますと、pyAIOUSBP.AIO.queryAioDeviceNameという関数を使うと、接続されているAIO機能を持つモジュールのデバイス名とモデル名を得ることが出来ます。AIOクラスのインスタンスを生成する前に実行できますので、プログラム作成時に接続されているデバイスの名前が確定していない場合はqueryAioDeviceNameで接続されているモジュールのデバイス名を確認してから処理を進めることが出来ます。
::
>>> pyAPIUSBP.AIO.queryAioDeviceName()
[['AIO000', 'AIO-120802LN-USB']]
**B:** おお、これは便利かも。ふむふむ。
**A:** 15行目からのif文は時間を計測するためのtime関数の設定ですね…って、これってWin32でしか動かないのになんでwin32かどうか判定してんだよ。作者は何考えてんだ。
**B:** 何も考えてないんじゃないですか。
**A:** あとで文句言っとかなきゃ。ぶつぶつ。さて、22行目からのwhile文は20行目を実行してから10秒間、0.5秒おきにsingleAiメソッドを用いてアナログ入力のチャンネル0の値を読んで表示するということを繰り返しているだけですね。特に解説は不要でしょう。
**B:** なんだ、最後が肝心じゃないんですか。なんでそんなにあっさりしてるんですか。
**A:** ふっ、作者は計画なしにこの記事を書き始めてしまって焦ってるんだな。続いてpyAPISUBP.DIOを使ってDIOライブラリ対応モジュールを使ってデジタル出力をする例。
.. code-block:: python
:linenos:
import pyAPIUSBP
import time
import sys
# Initialize
try:
dio = pyAPIUSBP.DIO.DIO('DIO000')
except:
print 'DIO000 is not found.'
sys.exit()
if sys.platform == 'win32':
timefunc = time.clock
else:
timefunc = time.time
startTime = timefunc()
# Output 255 and 0 alternately for 10 seconds.
while timefunc()-startTime<10:
dio.outputByte(0,0)
print 0
time.sleep(1)
dio.outputByte(0,255)
print 255
time.sleep(1)
**B:** ほとんどアナログ入力の場合と同じですね。えーと、最後は一秒間255を出力して、一秒間0を出力してを繰り返すのか。
**A:** 255や0を出力するとはどういう意味かわかるかね?
**B:** へへん、僕を甘く見ないでください。出力に使っているメソッドの名前にByteとついていて、255とは2進数で0b11111111なんだから、8bitの全チャンネルを1にするということでしょう。で0を出力するとは全チャンネルを0にする、と。
**A:** 御名答。いや、さすが作者が焦っているととたんに有能になるBくんだけはある。
**B:** いやあ、そんなに褒められても。
**A:** ちなみにDIOライブラリに対応しているモジュールが接続されているか探して名前を返す関数はpyAIOUSBP.AIO.queryDioDeviceNameです。AioがDioになってるだけですね。そして最後にコールバックを使う例。例題21-3で扱ったコールバックをpyAPIUSBPで書く方法です。
.. code-block:: python
:linenos:
import pyAPIUSBP
import time
import ctypes
from ctypes.wintypes import WPARAM, LPARAM
# Initialize
aio = pyAPIUSBP.AIO.AIO('AIO000')
# Define callback function
def showMessageId(id, message, wparam, lparam, param):
if message==pyAPIUSBP.AIO.AIOM_AIE_END:
print 'AIOM_AIE_END:', lparam
elif message==pyAPIUSBP.AIO.AIOM_AIE_DATA_NUM:
print 'AIOM_AIE_DATA_NUM:', lparam
else:
print message, lparam
return 0
# Get prototype of callback function
callbackPrototype = ctypes.WINFUNCTYPE(ctypes.c_long,
ctypes.c_short,
ctypes.c_short,
ctypes.c_int,
ctypes.c_int,
ctypes.c_void_p)
# Get a pointer to the callback function
callback = callbackPrototype(showMessageId)
# Register callback function
aio.setAiCallBackProc(callback,
pyAPIUSBP.AIO.AIE_END|pyAPIUSBP.AIO.AIE_DATA_NUM, 0)
aio.setAiStopTimes(4000)
# Start analog input
aio.startAi()
time.sleep(5)
data = aio.getAiSamplingData(100)
print data
**B:** (んー、さすがのAさんもこれは解説するかな?)
**A:** 解説は例題21-3を見ていただくとして、相違点だけ補足。コールバックでは、メッセージの種類に応じて処理を分岐する例が示されています。まあ、メッセージを文字列で表示しているだけなんですが。
**B:** これってelseまで行っても結局print message, lparamってしてるんだから上の二つと同じなんじゃないんですか?
**A:** 実際にコールバック関数が受け取るメッセージはメッセージID、すなわちただの数値なんだから全然違うだろ。
**B:** あ、そうか。
**A:** で、34行目4000回サンプリングしたらサンプリングを停止するように設定して37行目でサンプリング開始。500件サンプリングしたところでリングバッファが一旦飽和してイベントAIOM_AIE_DATA_NUMが発生し、AIOM_AIE_DATA_NUM: 500と表示されます。その後、4000回サンプリングが終了するとAIOM_AIE_ENDイベントが発生し、AIOM_AIE_END: 4000と表示されます。最後に記録されたデータの内100件を41行目で取得しています。
**B:** さっぱりわかりません。
**A:** コールバックとはどういうことなのか、しっかり考えればわかるはず。これは宿題にしておきます。
**B:** げー。
**A:** というわけで、大急ぎのpyAPIUSBPの解説はこれまで。ふう、最後まで丁寧な口調をキープできたぞ。
**B:** エセ丁寧…
**A:** ん?なにか言ったか?
**B:** いえいえ、なんにも。
**A:** というわけで、9月の更新もそろそろ打ち止めかな。ではまた、何かのテーマでお会いしましょう。