2009年3月3日火曜日

NI DAQmx を Agilent の後に。Visa.dll その2

Agilent Connection Expert 15 が既にインストールされている PC に、 NI DAQmx 8.7.1 を後からインストールします。Windows XP。

NI DAQmx のインストール時に、 Agilent がインストールした visa32.dll を上書きします。でも、もとのファイルは、名前を替えて自動的にバックアップがコピーされます。PyVISA(win32) は Agivisa32.dll を使うので影響受けていません。

NI DAQ インストール後、つまり visa32.dll が NI版に上書きされても、Agilent の Connection Expert 15 は機能しました。私の環境では、GPIB バスにつながった測定器の認識等、みてる限り問題なく動いています。動いているようです。


NIの製品については詳しく無いのですが、 NI DAQmx のCDには、3つの大物がいるようです。
  • NI DAQmx 8.7.1
  • NI Labview Signal Express 2.5.1
  • NI Measurement & Automation Explorer 4.4.1
インストール時の注意は、特になく、ほとんどデフォルトの選択で進めて問題ありませんが、「自分でプログラムを作る」のは、デフォルトの選択肢ではありません。サンプルのソースを展開するには、インストールオプションで、「サンプル」を選んでおく必要があります。よく分かってないので「サンプル」と書かれたプログラム(項目?)に一通りチェックを追加しました。
  • VC++ 2003 Measurement Studio サンプル
  • VC++ 2005 Measurement Studio サンプル
  • labwindows CVI サポート サンプル <このへんが Python + ctypes には合うのかも
  • .NET Freamwork 1.1 サンプル
  • .NET Freamwork 2.0 サンプル
  • MS Visual C サンプル
  • MS Visual Basic サンプル
インストールすると、デスクトップに Measurement & Automation というプログラムのショートカットがありました。そこで NI USB-6009 を接続すると、認識され、テストパネルとか言うので個々の信号を制御できました。この段階で、「ハード的に」問題ないレベルにセットアップが出来た、ですね。


では、Python と ctypes を使って NI USB-6009 の制御を試します。
すでに賢い人が作ったコードがありますので、それをもらって自分用に改造します。
SciPy の Cookbook にいいのがありますので Google検索 もらいます。
Numpy 、Python のバージョンに合う、自分のOS用のをインストールします。

Idle で実行して、コンソールで data と入力すると電圧値がアレイに入っています。びっくりです。プログラムでは、Numpy をインポートしてます。これは、アレイデータの初期化に使っているだけです。この人、多分 Matlab 使いですね。


NI DAQmx のAPI は、Help ファイルというかたちで、PC にインストールされます。
無い場合は、インストーラでなにか適当な場所にチェックしてみる。とか。

Cookbook のソースは、アナログディファレンシャル入力、デフォルト int32(-1)なので、それをシングルエンド(10****とか)に、と改造しようとしましたが、その int32(10****) の数字をさがしたり、USB-6009固有の、この設定はできないとか、ひっかかりました。
  • Measurement & Automation のテストパネルを使いましょう。
  • API の Help ファイルを見つけましょう。Docs? Documents? の下。
  • NI のディベロッパースフォーラムにも情報がたくさんあります。
  • 人に聞く時や、セミナー等では、「Pythonで」と言わない方がいいと思います。
慣れた人なら、NI DAQmx インストール時に言語は、日本語ではなく英語のほうがいいかもしれません。最近の Matlab もそうですが、全てのドキュメントが日本語に訳されていれば、問題ないのでしょうが、「英語のドキュメント日本語化されたメニューの項目」の対応がとりにくい等、いろいろ問題があります。ディベロッパーゾーンで見つかる情報も、やはり英語ですし。

先のシングルエンドの設定にも 2種類あって、USB-6009 に出来る方と、出来ない方、基準シングルエンドとか基準無しシングルエンド、とか、そういう日本語の表示と、API 説明文の中の略語の対応を、考えないといけない、とか。ちょっと、つまづくネタが増えます。

USB-6009 のドキュメントを探すときには、USB-6008 のドキュメントのほうが充実してますので、こちらも同時に検索するといいと思います。

コードは、その、いまのところ本当に「ちょっと」改造しただけなので、公開するのが恥ずかしいのでやめときます。





2009年2月27日金曜日

Python Tuner

Python で beep を制御する。だけ。
440Hz を 0.5秒 (500mSec)
デモなので 0.5 秒と短い時間ですが、楽器のチューニングするときには、もっと時間を長めに。winsound のマニュアルには、もっといろいろなことが書いてあります。

# Win32(XP)
# Python 2.5

import winsound
winsound.Beep(440,500)

2009年2月17日火曜日

ctypes(その2) MBT.dll を Python から

問題は、ポインタやハンドラ渡しの変数をどう扱うかです。

いろいろ試してみて、なんとかこれでいいのではないか?と思われる方法です。MBT.dll の公開されている API (Google検索)を見てください 。
  1. ctypes のインポート
  2. PLC との通信に必要な変数の設定
  3. ハンドラ、ポインタの型 を c_long() に  < この辺が大事みたいです。
  4. def connect() 関数 < socket が ハンドラ です
  5. def disconnect()関数
  6. def read()関数 < ReadData が ポインタ です
  7. def write()関数 < WriteData が ポインタ です
  8. 各関数を順番に実行します
  9. ## は VBA で使う時のサンプル

  • 変数をあらかじめ c_long() 等宣言する
  • 関数では byref()で渡す
  • 中身は、 ***.value で扱う
ソフトウェア用語を使ったうまい説明はできません、用語を知りませんので。
 <<16、>>16 は 16 bit のデータを Python 32 bit、 で受けた(?)ので、ずらしてます。
SwapDWord() は、インテル系 CPU の変なバイトオーダーに合わせる為の関数です。 

16 bit の 整数型、だけしかうまく動いてません。また時間があったらがんばります。


# $Id$
# Win32
# Python 2.5 (ctypes)
# MBT.dll by Wago (Modbus over TCP)
# copy MBT.dll to c:\windws\system32
# SYSTEM 750-841 Wago PLC
#
# data = int (16 bit) only
#

from ctypes import *

PLCAddress = '192.168.1.2' # PLC adress or name
Port = 502 # Modbus TCP port, need to be enabled
BaseAddress = 0x3000 # PLC, IEC 61131-3 Addresses %MW0..
length = 1 # data length

socket = c_long() # handler
ReadData = c_long() # pointer
WriteData = c_long() # pointer

def connect():
state_a = windll.MBT.MBTInit()
state_b = windll.MBT.MBTConnect(PLCAddress, Port, 1, 3000, byref(socket))

print ('PLC address = ',PLCAddress)
print ('Socket = ',socket.value)
print ('MBT Init state = ',state_a)
print ('MBT Connect state = ',state_b)


def disconnect():
state_a = windll.MBT.MBTDisconnect(socket)
state_b = windll.MBT.MBTExit()

print ('MBT Disconnect state = ',state_a)
print ('MBT Exit state = ',state_b)


def read(OffsetAddress, c=None):
ReadAddress = BaseAddress + OffsetAddress
state_a = windll.MBT.MBTReadRegisters(socket,4,ReadAddress,length,byref(ReadData),0,0)
if state_a != 0 :
print (' **** PLC read error')
c = windll.MBT.MBTSwapDWord(ReadData.value) >> 16

print ('MBT Read resisters state = ', state_a )
print ('Read address = ', ReadAddress )
print ('Read data = ',c )

return c



def write(OffsetAddress, data, c=None):
WriteAddress = BaseAddress + OffsetAddress
WriteData.value = windll.MBT.MBTSwapDWord(data << 16)
state_a = windll.MBT.MBTWriteRegisters(socket,WriteAddress,length,byref(WriteData),0,0)
if state_a != 0 :
print (' **** PLC write error')

print ('MBT Write resisters state = ', state_a )
print ('Write add = ', WriteAddress )
print ('Write data = ', WriteData.value )

## read back
c = read(OffsetAddress)
return c


connect()
write(0,1)
data = read(0)
print(data)
disconnect()





## -----------------------------------------------
## 'VB source
##Public Const MODBUSTCP_TABLE_OUTPUT_REGISTER = 4
##Public Const MODBUSTCP_TABLE_INPUT_REGISTER = 3
##Public Const MODBUSTCP_TABLE_OUTPUT_COIL = 0
##Public Const MODBUSTCP_TABLE_INPUT_COIL = 1
##Public Const MODBUSTCP_TABLE_EXCEPTION_STATUS = 7
##
##Public Declare Function MBTInit Lib "MBT" () As Long
##Public Declare Function MBTExit Lib "MBT" () As Long
##Public Declare Function MBTConnect Lib "MBT" (ByVal szHostAddress As String, ByVal port As Integer, ByVal useTCPorUDP As Long, ByVal requestTimeout As Long, hSocket As Long) As Long
##Public Declare Function MBTDisconnect Lib "MBT" (ByVal hSocket As Long) As Long
##Public Declare Function MBTReadRegisters Lib "MBT" (ByVal hSocket As Long, ByVal tableType As Byte, ByVal dataStartAddress As Integer, ByVal numWords As Integer, pReadBuffer As Any, ByVal fpReadCompletedCallback As Long, ByVal callbackContext As Long) As Long
##Public Declare Function MBTReadCoils Lib "MBT" (ByVal hSocket As Long, ByVal tableType As Byte, ByVal dataStartAddress As Integer, ByVal numBits As Integer, pReadBuffer As Any, ByVal fpReadCompletedCallback As Long, ByVal callbackContext As Long) As Long
##Public Declare Function MBTReadExceptionStatus Lib "MBT" (ByVal hSocket As Long, pExceptionStatus As Byte, ByVal fpReadCompletedCallback As Long, ByVal callbackContext As Long) As Long
##Public Declare Function MBTWriteRegisters Lib "MBT" (ByVal hSocket As Long, ByVal dataStartAddress As Integer, ByVal numWords As Integer, pWriteBuffer As Any, ByVal fpWriteCompletedCallback As Long, ByVal callbackContext As Long) As Long
##Public Declare Function MBTWriteCoils Lib "MBT" (ByVal hSocket As Long, ByVal dataStartAddress As Integer, ByVal numBits As Integer, pWriteBuffer As Any, ByVal fpWriteCompletedCallback As Long, ByVal callbackContext As Long) As Long
##Public Declare Function MBTSwapWord Lib "MBT" (ByVal wData As Integer) As Integer
##Public Declare Function MBTSwapDWord Lib "MBT" (ByVal dwData As Long) As Long
##
##Private Socket As Long






2009年2月16日月曜日

Wago PLC System 750

ctypes(その2)の記事(code)の準備。近いうちに ctypes を使ったもう少しややこしい .dll の使い方の例を書きます。その .dll は、 MBT.dll という Wago の PLC のインターフェース用の .dll です。その前に ワゴ の PLC がどういうものなのか説明します。Wago PLC および、その開発環境CodeSys(通信用の MBT.dllも)は、残念ですがフリーではありません。

ネットワークの先に、I/O 機器を接続して、Python からそのデータを読み書き出来ると便利です。方法はいろいろありますが、その I/O 機器をマイコン+ネットワーク等と始めるとシステムが出来るのが何ヶ月先になるか分かりません。また市場で安く、信頼性のあるものを買って済ませることができれば、ありがたいです。Wago のリモート I/O でそういった使い方ができました。産業用の Modbus というプロトコルを MBT.dll 経由で TCP/IP に乗せて使います。

問題は、ハードは比較的安いのですが、開発環境が高価です、残念です。ターゲット側の開発にも、それなりのスキル(ST言語等)が必要です。が、ネットワーク I/O システム全体を(マイコン+TCP/IP 等で)発注して、アセンブリ言語ベースのマイコン ネットワークシステム をフルスクラッチするよりは、ある程度の標準化(分割発注も)ができるので、資産の使い捨てにならないのが救いです。何より、これで監視・制御ソフトは Python で自分で作れます。

ネット I/O の親(CPU)は、カプラと呼ばれます、ネット無しでも制御プログラムや多少面倒な変換ぐらい組み込めます。それどころか本来、CPU単品で1mSec 毎の制御をすべて自前でこなせるぐらいの能力があります。このカプラに必要に応じて、I/O のモジュールを、レゴ ブロックの感覚で拡張します。

Wago 750 シリーズ のカプラ(CPU)は、ARMナンチャラ、OSは、リアルタイム組み込み Linux、これだけで何が出来るか大体想像できると思います。Web server、Mail、FTP、TCP/IPやりたい放題です。旧来のお客様のために、TCP/IP、UDP に加え Modbus I/F もあります、というか、こちらがメインです。

そこで、前出のMBT.dll Modbus over TCP/IP (.dll)の登場です。Modbus プロトコル を TCP/IP に乗せて、PCとやりとりします。Python にそういうモジュールがあればいいのですが、あるのかもしれませんが私にとって手軽ではないので、PC と Wago PLC 間の通信に、MBT.DLL を使います。


システム構築に必要な物
  1. 産業用、DC24V電源
  2. PLC: Wago system 750-841 等 Ethernet カプラ(親)
  3. I/O module:Wago system 750- シリーズ AI、AO、DI、DO 等 
  4. ネットワーク用 ケーブル、スイッチ等
  5. WindowsPC (+Python + NIC)
  6. PLC 開発用ソフトウェア:WAGO-IO-PRO32、WAGO-IO-PRO CAA等(Codesys)(有償)
  7. MBT.DLL (有償)
分かりやすく?するために昔の VxWorks system との雑な対応表を書きます、と

PC:PC:PC
Development Env:Tornado:CodeSys
DevLang:c:ST or ladder
BSP:BSP(I do not know):sometarget.exe(forgot!) and MBT.dll
Target Master:MVME-xxx:Wago system750-xxx
Target Bus:VMEBackPlane:(Include with Wago system)
Target I/O:VME I/O Boards: Wago I/O modules
Target OS:VxWorks:some embedded Linux 



2009年2月14日土曜日

CSVファイルの読み書き(2)

標準CSVモジュール を使った、CSV ファイルの読み書き(その2)。 以前の記事を書いた時は、標準の CSV モジュールの使い方が、よく理解できていませんでした。今回は、標準の CSV モジュールを使った ファイル IOです。 

CSV ファイルの数値データを list にします。以降のプログラムで
 List[0][0]
といった形でデータを呼び、処理が出来るようにします。
  1. CSV モジュールのインポート
  2. 文字列データを数値(float)に変換する関数を準備。リスト、サンプルデータの初期化。
  3. サンプルデータを、sample.csv というファイルに書き込みます。FW0。
  4. sample.csv を開きます。FR0。文字列データを datastr のリストに、数値化したデータを datanum のリストに保存します。
  5. 読み出したデータのチェック。
  6. 変数 row に残っている文字列のリスト を、1 行づつ 4 回書き込みます。FW1。
  7. datastr の文字列のリスト を ファイルに書き込みます。FW2。
  8. datanum の数値のリスト をファイルに書き込みます。FW3。
2.のところで
  def s2f(x): return float(x)
と関数を定義しているのは、
4.で文字列のリストを数値化する
datanum.append(map(s2f,row))
map()
を利用するには、処理が関数でないといけないからです。





## $Id$
## win32
## Python2.5
## csv file io (for numeric data)

import csv

def s2f(x): return float(x)
datastr = []
datanum = []
datasample = [[1.0,2.0],[3.0,4.0]]

fw0 = open('sample.csv','wb')
writer0 = csv.writer(fw0)
writer0.writerows(datasample)
fw0.close()

fr0 = open('sample.csv', 'rb')
reader0 = csv.reader(fr0)
for row in reader0:
datastr.append(row)
datanum.append(map(s2f,row))
fr0.close()

print (reader0.line_num)
r = 1
c = 1
print ('string data [',r,'][',c,'] = ',datastr[r][c])
print ('numeric data [',r,'][',c,'] = ',datanum[r][c])

print(row) # data for row write test, form last row
fw1 = open('csvout1.csv','wb')
writer1 = csv.writer(fw1)
writer1.writerow(row)
writer1.writerow(row)
writer1.writerow(row)
writer1.writerow(row)
fw1.close()

fw2 = open('csvout2.csv','wb')
writer2 = csv.writer(fw2)
writer2.writerows(datanum)
fw2.close()

fw3 = open('csvout3.csv','wb')
writer3 = csv.writer(fw3)
writer3.writerows(datastr)
fw3.close()



2009年1月7日水曜日

Py3K にとりあえず対応

深刻なエラーを回避します。当面エラー無しで動くようにしないと話になりませんので。
print と raw_input() が変わっているようですので、先ずそこだけでも、の対策です。

print
print を print() に変更します。
カッコで囲みます。
変更後も 2.5 でも動きます。

raw_input()
raw_input() を input() に変更します。
変更後は、2.x では動かなくなります。