Pythonボックスの考え方 (Pepper TechFes技術セッション)


#ChoregrapheとPython

ここまでのチュートリアルで、Choregrapheに用意されているさまざまなボックスについて見てきました。ボックスだけでもさまざまなアプリケーションを実現できますが、Pythonを用いてAPIに直接アクセスすることで、さらに多くのことを実現することができます。

[Pepper Tech Festival 2014](http://www.softbank.jp/robot/special/techfes/) の技術セッションでは、Pythonを利用する方法の一つとして、Pythonボックスについて紹介しました。このチュートリアルでは、Pythonボックスの考え方について概要を説明します。

なお、Choregrapheにおいて使用するPythonのバージョンは2.7です。Pythonの基本的なチュートリアルについては、 http://docs.python.jp/2/tutorial/ などを参照してください。また、APIドキュメントの参照方法は [バンパーセンサーのイベント検出#ドキュメントの確認](http://qiita.com/Atelier-Akihabara/items/d87553893fe2caa26d67#%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE%E7%A2%BA%E8%AA%8D) などを参考にしてください。

#Pythonボックス

ここでは、Choregrapheで空のPythonボックスを作ってみて、その中を見ながらPythonボックスを説明をしていきます。

1. フローダイアグラム上で右クリックし、 **[ボックスの新規作成]メニューの[Python...]を選択**します
    ![new-python-box.png](https://qiita-image-store.s3.amazonaws.com/0/54394/dcea45bd-80f4-20de-4123-09b7ac0e89cb.png)

2. 名前として **Test を入力 [A]**し、**[OK]ボタンをクリック [B]**します
    ![new-python-dialog.png](https://qiita-image-store.s3.amazonaws.com/0/54394/9be99c62-dca7-50d5-7e71-111fe3b25f21.png)

    すると、Pythonボックスが作成されます。
    ![python-box.png](https://qiita-image-store.s3.amazonaws.com/0/54394/3bad7b8a-d26a-9d5b-5506-31d2ccfd9e92.png)


作成された Testボックスをダブルクリックしてみてください。スクリプトエディタが開き、以下のようなコードが表示されます。

```py
class MyClass(GeneratedClass):
    def __init__(self):
        GeneratedClass.__init__(self)

    def onLoad(self):
        #put initialization code here
        pass

    def onUnload(self):
        #put clean-up code here
        pass

    def onInput_onStart(self):
        #self.onStopped() #activate the output of the box
        pass

    def onInput_onStop(self):
        self.onUnload() #it is recommended to reuse the clean-up as the box is stopped
        self.onStopped() #activate the output of the box
```

Pythonボックスの振る舞いは `MyClass` クラスによって定義されます。`GeneratedClass` クラスはビヘイビアの実行時に自動的に生成され、ボックスの定義にしたがって追加のビルトイン関数を提供します。

##ボックスに定義されるメソッド

###入力

`MyClass`クラスではボックスの入力に対応するメソッドを定義する必要があります。これらの名前は **onInput_<入力名>** と定義され、入力がおこなわれたときに呼び出されます。

引数は[入力の種類](http://qiita.com/Atelier-Akihabara/items/7a898f5e4d878b1ad889#3-2)により変化します。たとえば、単純イベント(Bang)である *myInput* という名前の入力を作成した場合は、

```py
    def onInput_myInput(self):
        pass
```

となり、数値(Number)や文字列(String)、動的(Dynamic)の場合は、

```py
    def onInput_myNumberInput(self, p):
        pass

    def onInput_myStringInput(self, p):
        pass

    def onInput_myDynamicInput(self, p):
        pass
```

と、引数を持つ形になります。Dynamicの場合、Bangのように（値を渡されずに）呼び出される場合もあるので、以下のように定義することができます。

```py
    def onInput_myDynamicInput(self, p = None):
        pass
```

デフォルトでは *onStart* 入力と *onStop* 入力が定義されるので、ここでは `onInput_onStart`, `onInput_onStop` という2つのメソッドが生成されます。

###ロードとアンロード

`MyClass`クラスには `onLoad`と`onUnload`メソッドが定義されており、これらはボックスがロードされたり、アンロードされたりした際に呼び出されます。

```py
    def onLoad(self):
        #put initialization code here
        pass

    def onUnload(self):
        #put clean-up code here
        pass
```

なお、ボックスが停止したのちに再初期化をおこなうため、ボックスの停止(onStop入力など)に相当するメソッドで `onUnload` を呼び出すことが推奨されています。

##ボックスから提供されるビルトイン関数

`MyClass`は`GeneratedClass`を継承しているので、スクリプト中で`GeneratedClass`が提供するビルトイン関数を利用することができます。

###出力

ボックスの各出力に対応するメソッドが **<出力名>** という名前で定義されます。
このメソッドを呼び出すことで、ボックスの出力をおこなうことができます。

入力と同様に、引数は[出力の種類](http://qiita.com/Atelier-Akihabara/items/7a898f5e4d878b1ad889#3-2)により変化します。単純イベント(Bang)である *myBangOutput* という出力を定義した場合は、

```py
    def onInput_onStart(self):
        self.myBangOutput()
```

などと引数なしで呼び出し、数値(Number)や文字列(String)、動的(Dynamic)の場合は、引数を与えて呼び出すことで、出力先に値を渡すことができます。

```py
    def onInput_onStart(self):
        self.myNumberOutput(1000)
```

###パラメータ

ボックスに指定されたパラメータを利用することもできます。以下のいずれかの方法があります。

1. `getParameter` メソッドを利用する方法
    *param1* という名前のパラメータを取得する場合は、以下のように `getParameter` メソッドを呼び出します。
    
    ```py
        value = self.getParameter("param1")
    ```

2. `setParameter` メソッドをオーバーライドする方法
    `MyClass`クラスで`setParameter`メソッドをオーバーライドすることで、パラメータが変更された際の処理を記述することもできます。
    
    ```py
    def setParameter(self, parameterName, newValue):
        if(parameterName == "param1"):
            self.param1 = newValue
    ```

###ログ

Pythonボックスからログを出力することもできます。出力したログはログビューアで参照することができます。

| メソッド | 機能 |
|---------|-----|
| self.log("my message") |	infoレベルでメッセージ出力 | 
| self.logger.fatal("my message") |	fatalレベルでメッセージ出力 |
| self.logger.error("my message") |	errorレベルでメッセージ出力 | 
| self.logger.warning("my message") |	warningレベルでメッセージ出力 |
| self.logger.info("my message") |	infoレベルでメッセージ出力 |
| self.logger.debug("my message")  |	debugレベルでメッセージ出力 |



なお、print文ではログビューアに出力することはできません。これらのメソッドを利用する必要があります。


このように、Pythonボックスを作ることで、ボックス中に直接Pythonコードを記述することができます。このコードの中でAPIを直接呼び出すことで、ボックスライブラリではカバーしていないこともおこなえるようになります。今後、これらPythonボックスの考え方について、チュートリアルにて具体例を通じてみていく予定です。