enebular AI models、AI node 事始め

# はじめに
メールアドレスの登録だけで無償で利用できる enebular (https://www.enebular.com/) ですが、今回有償版の機能を利用できる機会をいただいたので、その「事始め」を記事に残しておこうと思います。

# 無償版と有償版（エンタープライズプラン）との違い
有償版はProject単位で有効化されるようです。同じアカウントで作成したProjectでも無償版Projectではグレーアウトされているメニュー項目がありますが、有償版Projectでは全て選択できるようになります。

- 無償版Project
![2019-10-06 22.12.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/3eeac4d7-d0ef-a7ac-c157-a50ab1aafdaf.png)

- 有償版Project
![2019-10-06 22.15.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/de80a32e-716a-8b7f-655a-7d53c0b5d91f.png)

Devices、Licenses、Action Historyの機能は以前、<A HREF="https://qiita.com/kitazaki/items/84fc7ef8d14dcea3964b">enebular スターターキット</A>をいただいた際に試したことがあったので、今回はFilesとAI modelsを試してみることにします。

# enebular Files
ドキュメントには「Files では有償デバイスにファイルをデプロイすることが出来ます。」と記載されています。enebular-agent (ver 2.3.0)以上が必要です。
https://docs.enebular.com/ja/Files/Introduction.html

![2019-10-06 22.35.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/1defd03c-ce3d-c05a-b7fd-e35ace746b5f.png)
![2019-10-06 22.36.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/01354800-a062-3e7e-a640-9f54c85ad68f.png)

DeviceへDeploy操作をした後、Raspberry PiにログインするとDeployしたファイルが保存されていることを確認できます。

```sh
# enebularアカウントで確認
$ ls ~/enebular-runtime-agent/ports/awsiot/assets/
201911.png
```

こちらは特に迷うことはありませんでした。

# enebular AI models と AI node
ドキュメントには「AI Models とは、機械学習・ディープラーニングのモデルをエッジデバイスにデプロイできる機能です。」と記載されています。enebular-agent (ver 2.7.0)以上が必要です。
https://docs.enebular.com/ja/AIModels/Introduction.html

こちらは最初ドキュメントを読んでも良く分からず、ソースコードを見たり試行錯誤しながらようやくイメージが掴めました。

- 動作イメージ
![2019-10-06 23.52.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/a796a840-56e3-910e-65fb-c436e25a649c.png)

Node-REDフロー上のenebular AI nodeはHTTP REST経由でdocker上で動作しているAI modelへ問い合わせをします。enebular上で登録したAI modelはdocker hubからダウンロードしたdocker image (Python実行環境)で動作します。docker imageはあらかじめいくつか用意されています(GPUリソースを利用できるNVIDIA Jetson TX2用のDocker Imageも用意されている！）が、自分で用意することも可能です。

- enebular上から選択するDocker Image
<img width="877" alt="2019-09-30 1.11.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/af481eb6-6a1b-d33f-d58a-8341e28422f6.png">

- Docker Hub上に登録されているDocker Image
![2019-10-02 15.32.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/632ca522-edc5-0e8f-f541-ba08c04fc779.png)

今回はRaspberry Pi上で動作を確認することにしました。

## enebularへのAI modelの登録
まず簡単に動作を確認するため下記のtest2.pyファイルを用意しzipで圧縮します。Node-REDフロー上のenebular AI nodeからは、このhandler関数が呼ばれます。

```python:test2.py
def handler(img):
  return img
```

```sh
$ zip -r test2.zip test2.py
```

test2.zipファイルをenebularに登録します。
![2019-10-07 1.01.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/6deac7e7-b5bf-360e-abec-cd188d6e1596.png)

Docker Imageは「Rpi 3B image for Keras v2.2.4 with Tensorflow v1.14 and Python3」を選択します。
Languageは「Python3」を選択します。
Content Typeは「Json」を選択します。
Max Ram (in MB)は「128」とします。
Coresは「1」とします。
![2019-10-02 14.43.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/65219661-9405-9e5f-cd38-41b5b3d041fd.png)

Handler Function #1は「handler」とします。
![2019-10-02 14.43.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/182f1006-3960-d3bc-e0bb-0f0d697fc015.png)

## Node-REDフローからenebular AI nodeの実行
enebular AI nodeのプロパティでAI ModelとHandler Functionを選択します。
![2019-10-02 14.45.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/09c883b2-451f-6eb5-0e78-a1249472bcd6.png)

Node-REDフローのinjectノードのボタンを押してdebugノードにタイムスタンプが表示されれば正常に動作しています。
<img width="1423" alt="2019-10-02 14.37.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/78d1be3c-c894-2c6b-aa5b-2e3f12c6ab53.png">

Raspberry Piにログインして動作を確認します。

```sh
# enebularアカウントで確認
$ docker image ls
REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
enebulardevelopers/rpi-keras   latest              c41cfd6ab451        5 weeks ago         1.22GB

$ docker container ls
CONTAINER ID        IMAGE                                 COMMAND                  CREATED             STATUS              PORTS                      NAMES
cc4a2b5ec73a        enebulardevelopers/rpi-keras:latest   "/usr/bin/entry.sh /…"   About an hour ago   Up About an hour    0.0.0.0:49153->49153/tcp   beautiful_khorana
```

```sh
# enebularアカウントで確認
$ pwd
/home/enebular/enebular-runtime-agent/ports/awsiot/ai-models/.mount/U0HFL2-V
$ ls
__pycache__  test2.py  wrapper.py
```

```python:wrapper.py
from flask import Flask, abort, jsonify, request, Response
import json
import test2

app = Flask(__name__)

@app.route('/vCPrWwtz', methods=["POST"])
def handler1():
    r = request
    data = json.loads(r.data.decode("utf-8"))
    predict = test2.handler(data) 
    return Response(response=json.dumps(predict), status=200, mimetype="application/json")

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=49153, debug=True)
```