IEEE1888 SDKをMacで使う: Pythonプログラミング篇
前回はJavaによるプログラム開発でしたが、今回は同様のプログラムを python で作ってみます。
いろいろと前置き
SOAPのためにどのライブラリを使うかが問題だったわけです。前回から随分と時間があいたのも、ここが引っかかったからです。最初は、PyPIに対応していて使いやすそうなものということで、sudsを使用する事にしたのですが、どうしても上手く動きません。
実際に使ってみるとfactoryを使っているにも関わらず、誤ったNameSpace指定でリクエストを送信(transportがdataRQと同じ"http://soap.fiap.org/"になる)してしまい、そもそもdatabindingのレベルでfaultを返されてしまうのです。ざっとドキュメントをみた限りでは、簡単に終わるなと思っていたのに、これです。bug report など見て対応すればよいのでしょうが、面倒なので、別のモノを探す事にしました。
PyPI対応で他に簡単に使えそうなものは、PySimpleSOAP、rpclibあたりでしょうか。
最初は、PySimpleSOAP を使おうかと思って試したのですが、SoapClient(wsdl=WSDL) で WSDL を読み込んで解析する段階で RuntimeError をはいてabort。使えません。
rpclibの方はと言えば、clientのサポートはsudsなどにお任せで、(多分)今回の目的には使えなそうです。(そもそも、インストールの段階でclangが必要だったりとか、もう。ちなみに、clangは、Xcodeをインストールしたうえで、Xcode > Preferences... > Downloads > Components > Command Line Tools で、コマンドラインツール一式をインストールできます。以前は何もしなくてもインストールされたはずですが、コマンドラインとか使わないんですかね皆さん)
そんなわけで、どうしたものかと思っているうちに忙しくなってしまい、放置していました。今回、しばらくぶりに余裕ができたので、sudsのドキュメントを眺めていたらPluginsの記述を見つけ、これならやれそうだと言う事で、試したら上手くいったのです。そうとわかって検索すれば、pluginを使ったメッセージの修正はわりと行われているようで、ある意味、bad knowhow として確立しているようです。
環境構築
まずは easy_install で pip インストール後に、pipを使ってお手軽にインストールします。これだけです。
$ sudo easy_install pip $ sudo pip install suds ....
ちなみに、suds-0.4がインストールされますが、(beta版ではありますが)0.4.1が配布されてますので、それを使うのもありです。その場合は、ソースをおとして、setup.py を動かせばOKです。
コード
前回と同等のプログラムですが、以下のようになります。WSDLを取得して、Clientオブジェクトを生成し、そのfactoryを使用してリクエストを組み立てます。処理本体は非常にシンプルで、コンパイルもいりませんから、必要に応じて適当に書き換えて利用すれば良いです。
問題のpluginを使った workaround は、class BugfixMessagePlugin の部分です。コメントにあるように、組み立てられた soap envelope の transport 要素に付けられた namespace を訂正しています。
#!/usr/bin/env python # -*- coding:utf-8 -*- import logging from suds.client import Client from suds.plugin import MessagePlugin from suds.sax.date import UTC ############################################################# # 設定データ WSDL_URL = 'http://192.168.11.12/axis2/services/FIAPStorage?wsdl' POINT_ID = 'http://test.kahnn/ps/point_int_100' POINT_VALUE = 302 POINT_TIME = UTC() ############################################################# # デバッグ用設定 #logging.basicConfig(level=logging.INFO) #logging.getLogger('suds.client').setLevel(logging.DEBUG) #logging.getLogger('suds.wsdl').setLevel(logging.DEBUG) #logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG) ############################################################# # バグ修正用Plugin設定 class BugfixMessagePlugin(MessagePlugin): def marshalled(self, context): #print 'PLUGIN (message): marshalled: ctx=%s' % context.__dict__ #print 'PRE MODIFY: ', context.envelope # transport要素の namespace が http://soap.fiap.org/ となってしまうのを # http://gutp.jp/fiap/2009/11/ へ修正する。 tp = context.envelope.childAtPath('Body/dataRQ/transport') tp.setPrefix(tp.findPrefix('http://gutp.jp/fiap/2009/11/')) #print 'POST MODIFY: ', context.envelope myplugins = ( BugfixMessagePlugin(),) ############################################################# # 処理本体 client = Client(WSDL_URL, cache=None, plugins=myplugins) #print client val = client.factory.create('{http://gutp.jp/fiap/2009/11/}value') val.value = POINT_VALUE val._time = POINT_TIME point = client.factory.create('{http://gutp.jp/fiap/2009/11/}point') point._id = POINT_ID point.value.append(val) body = client.factory.create('{http://gutp.jp/fiap/2009/11/}body') body.point.append(point) tp = client.factory.create('{http://gutp.jp/fiap/2009/11/}transport') tp.body = body #print tp try: result = client.service.data(tp) print(result) except Exception as e: print e.message
実行
それでは実行してみます。
$ python ./sample_fiapapp.py (transport){ header = (header){ OK = "" } }
例のごとく、「Sensors in this FIAPStorage」の画面で確認しますが、別に面白くもないので省略します。
ちょっとしたテストやユーティリティを作る際は、python のようなスクリプト言語の方が楽ですね。
(まあ、今回はそれ以前で引っかかりまくりだったのですけど。。。)
最後に
ここまで IEEE1888 SDK を使って遊んできたのですが、規格自体はシンプルで特別なことは何もないです。データを取り扱うための共通基盤は用意したので、あとは好きに使ってくれというところでしょうか。全ては使う側のアイディア次第なので、これからの応用に期待といったところですかね。