Pepper アプリプロジェクト内に独自の Webサーバーを立てる

Pepper に外部から HTTP リクエストを投げ、これをトリガーに何か Pepper にさせたい。　そんなことを実現する方法を説明します。

#対象
この記事は、Pepper の開発環境、Choregraphe をご存知で、 Python のプログラムがある程度理解出来る方を対象としています。

#概要
　Pepper には標準で Webサーバー機能が入っており、http://<ロボットのIP>/apps/アプリID/ でアプリの html フォルダーに入っているリソースに PC などからアクセスすることができます。 ただしこれには次のような制限があります。

- 外部からアクセスする場合、ロボットの ID、パスワードを最初に入力しなければならない
- 静的なコンテンツへのアクセスのみ。リクエストを受けてアクションを起こしたり、動的なレスポンスを生成したりすることはできない。

HTTP リクエストを Pepper 側で受けて、何かアクションをさせたい。そういった場合、独自に Webサーバー的なものを用意する必要があります。どうするか、Python には標準で簡易的な Webサーバーを立てるモジュールが用意されているので、これを使ってみましょう。

（今回紹介する方法は、あくまでローカルネットワーク内での HTTP リクエストの受け方です。インターネットを経由した HTTP のリクエストを直接 Pepper が受け取るには何かそのための特別な仕掛けが必要です）


#サンプルコード

サンプルコードを作ってみました。Python Script ボックスを持ってきて、中のコードを編集します。

<img width="907" alt="スクリプトエディタ_と_MyWebServer_-_Choregraphe__バーチャルロボットに接続_.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/81650/4457c982-a28f-1071-b900-8f78b6d0bc07.jpeg">

以下がサンプルの　Python Script ボックスのコードです。

```python:
import BaseHTTPServer

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

    def onLoad(self):
        self.httpd = None

    def onUnload(self):
        if self.httpd is not None:
            self.httpd.shutdown()
            self.httpd.server_close()

    def onInput_onStart(self):

        #HTTP リクエストを受信するポート番号 http://ロボットのIP:ポート番号/ でリクエストを受信します
        PORT = 8000

        MyHandler.setParent(self)
        self.httpd = BaseHTTPServer.HTTPServer(('', PORT), MyHandler)
        self.httpd.allow_reuse_address = True
        self.httpd.serve_forever()

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

    # BaseHTTPServer.BaseHTTPRequestHandler を経由して HTTP GET リクエストがあった時呼ばれます
    # http オブジェクトの中に以下のドキュメントが示す内容が入っています    
    # http://docs.python.jp/2/library/basehttpserver.html#BaseHTTPServer.BaseHTTPRequestHandler
    def do_GET(self, http):
    
        #ブラウザーからのリクエストの場合、実際のリクエストとは別に favicon.ico へのリクエストが来る、これは無視
        if http.path.endswith('favicon.ico'):
            return;
        
        self.logger.info("request url:" + http.path)
        
        http.send_response(200)
        http.send_header("Content-type", "text/plain")
        http.end_headers()
        http.wfile.write("This is a test response")

class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    _parent = None

    def do_GET(self):
        MyHandler._parent.do_GET(self)
        
    @staticmethod
    def setParent(parent):
        MyHandler._parent = parent
        
```

アプリを実行したら同一 LAN にいる PC などのブラウザーから http://<ロボットIP>:8000/ でアクセスしてみてください。ブラウザーには "This is a test response" と表示されるはずです。

#サンプルコードの解説

HTTP サーバー機能は BaseHTTPServer モジュールを使って実現しています。
こちらの例では、8000 番ポートでリクエストを待っています。
onStart 入力が呼ばれると、HTTPサーバープロセスを立ち上がり、ボックスがアンロードされるまでリクエストを待ち続きます。（アンロードはアプリ終了時、またはボックスがダイアグラムボックスの中や Timeline ボックスの入っている場合、そのボックスから抜けたタイミングで行われます）

リクエストは BaseHTTPServer.HTTPServer のインスタンス生成時に与えられた MyHandler クラスのインスタンスによって行われるのですが、ボックス処理と連動させたかったので

```python:
        MyHandler.setParent(self)
```

の箇所で少し強引に、クラスにボックスのインスタンスを渡し、ボックスインスタンス側で処理できるようにしています。ボックスのメソッド

```python:
    def do_GET(self, http):
```
が HTTP の GET リクエストを受け取り、処理します。
ここで self はボックスのインスタンス、http はリクエストを受け取った BaseHTTPRequestHandler クラスのインスタンスです。 

```python:
        self.logger.info("request url:" + http.path)
```
でとりあえず リクエストURLのパス部分をログに出力、次に

```python:
        http.send_response(200)
        http.send_header("Content-type", "text/plain")
        http.end_headers()
        http.wfile.write("This is a test response")
```

で HTTP レスポンスを生成しています。 

BaseHTTPRequestHandler インスタンスの中にどのような情報が入っているか、詳細は次の URL を参考にしてください。

http://docs.python.jp/2/library/basehttpserver.html#BaseHTTPServer.BaseHTTPRequestHandler

今回の例は、HTTP レスポンス についてプログラムから生成する形を取っています。　もし基本的にはプロジェクト内のファイルを返したいということであれば、 BaseHTTPRequestHandler をベースに実装が加えられた　[SimpleHTTPServer モジュールの SimpleHTTPRequestHandler クラス](http://docs.python.jp/2/library/simplehttpserver.html) を使えばいいのではと思います。このあたりは用途に応じて色々トライしていただければと。

#最後に
HTTP リクエストを Pepper が動的に受け取ることができるようになると、Web API のクライアント的な立場でインターフェースを持っている様々な機器との連携がしやすくなりそうです。　
Pepper の新しい活用方法の助けになれば幸いです。