IEEE1888 SDKをMacで使う: Pythonプログラミング篇

前回Javaによるプログラム開発でしたが、今回は同様のプログラムを python で作ってみます。

いろいろと前置き

SOAPのためにどのライブラリを使うかが問題だったわけです。前回から随分と時間があいたのも、ここが引っかかったからです。最初は、PyPIに対応していて使いやすそうなものということで、sudsを使用する事にしたのですが、どうしても上手く動きません。
実際に使ってみるとfactoryを使っているにも関わらず、誤ったNameSpace指定でリクエストを送信(transportがdataRQと同じ"http://soap.fiap.org/"になる)してしまい、そもそもdatabindingのレベルでfaultを返されてしまうのです。ざっとドキュメントをみた限りでは、簡単に終わるなと思っていたのに、これです。bug report など見て対応すればよいのでしょうが、面倒なので、別のモノを探す事にしました。

PyPI対応で他に簡単に使えそうなものは、PySimpleSOAPrpclibあたりでしょうか。
最初は、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 を使って遊んできたのですが、規格自体はシンプルで特別なことは何もないです。データを取り扱うための共通基盤は用意したので、あとは好きに使ってくれというところでしょうか。全ては使う側のアイディア次第なので、これからの応用に期待といったところですかね。