IoT機器のデータをWeb API経由でDBに保存したり時系列でChart表示したりしてみた

# まえがき

こんにちは！KemoKemoです！٩( 'ω' )و

前回・前々回と2つの記事で、以下を実践して「温度センサー現在値」が扱えるようになりました。

* Raspberry PiとArduinoを使った温度センサーデータのWeb API化
* 温度センサーデータの描画

詳しくは以下をご覧ください。

> * [Gobotの招きにあひて、徒然なるままにArduinoとRaspberry PiでIoTっぽいことをやってみるなり](http://qiita.com/KemoKemo/items/10fb644f9d359c35646a)
> * [IoTのAPIサービスから取得した温度計情報をVue.jsとCanvas Gaugesでレンダリングしてみた](http://qiita.com/KemoKemo/items/f96338840770c2e425d0)

# 概要

![chart.png](https://qiita-image-store.s3.amazonaws.com/0/166225/ce5f9942-09a6-d7be-9e8f-5e6f25a044cc.png)

今回は、上図のように「温度を時系列でチャート表示するっ！」システムを構築してみます。以下3つのサービスを実装します。

1. センサー情報収集サービス
2. チャートデータ用DBのWeb APIサービス
3. チャート描画サービス

## センサー情報収集サービス (Collector Service)

![collecting.png](https://qiita-image-store.s3.amazonaws.com/0/166225/d613a304-e30f-bfdd-8861-e35deee415b3.png)

### やること

- 定期的に温度センサーの情報を取得して、現在時刻情報を付与する（この情報を`DataSet`とします）
- `DataSet`をチャートデータ用DB APIに`POST`する
- 収集する時間間隔を外部設定ファイル(`*.yml など`)で設定する

### 作ってみた

今回もgithubにソースコードあります٩( 'ω' )و

> [KemoKemo/IoT-DB-Sample/collector](https://github.com/KemoKemo/IoT-DB-Sample/tree/master/collector)

APIサービスのURIや収集時間の間隔は`collector.yml`を書き換えて調整します。

```collector.yml
# The unit of this parameter is minute
duration: 5

# The address of the sensor API
sensor: http://192.168.111.135:5000/api/v1/sensors

# The address of the DB API
db: http://localhost:9000/sensors
```

### デプロイ

Ubuntu Serverで`Systemd`を使ってサービス稼働するときの例です。

* `collector`という名前でビルド
* `/usr/local/bin/tempcol`ディレクトリに`collector.yml`と共に配置
* `/usr/local/bin/tempcol/collector`に実行権限を付与
* 以下の内容を`tempcol.service`として`/lib/systemd/system/`に配置
* `sudo systemctl enable tempcol.service`して再起動

```tempcol.service
[Unit]
Description=a collector service for the temperature

[Service]
Type=simple
WorkingDirectory=/usr/local/bin/tempcol
ExecStart=/usr/local/bin/tempcol/collector
ExecStop=/bin/kill -SIGTERM $MAINPID

[Install]
WantedBy=multi-user.target
```

## チャートデータ用DB API (DB API)

MongoDBをGo言語のAPIサービスでラップして、Web API経由でデータの保存や取り出しができるサービスにしてみました。

DBaaSみたいな構図も最初考えたのですが、マイクロサービスアーキテクチャ的には以下のような理由から現在の構成の方が良いだろうと考えました。

* DBが別途マイクロサービスで起動してればいい
* データの保存も取得もWeb API経由で簡単に済ませたい
* DBの種類によって利用者側のコードに依存があったら嫌だ

また、今回のように「API経由で渡されたJSONデータを保存」「ざっくり貯めてざっくり見る」という用途にはドキュメント型DBの`MongoDB`がもってこいだと判断しました。

### やること

* 日付情報付きのセンサー情報(`DataSet`)が`POST`で渡されたらDBに保存
* `GET`で要求されたら指定時間区間の`DataSet`配列(`ChartData`とよぶ)を返す

### 作ってみた

> [KemoKemo/IoT-DB-Sample/db-api](https://github.com/KemoKemo/IoT-DB-Sample/tree/master/db-api)

DB接続を伴うWeb APIの実装には、デコレーターパターンを用います。

* `mgo`ライブラリを使ってMongoDBに接続してセッションを取得
* これをデコレーターパターンで修飾したハンドラーに渡す
  - `withVars(withData(s, epGetSensors))`など

```go
s, err := mgo.Dial(*dbaddr)
if err != nil {
  log.Println("Failed to dial to db", err)
  return exitCodeFailed
}
defer s.Close()

r := gin.Default()
r.Use(cors.Default())
r.GET("/sensors", withVars(withData(s, epGetSensors)))
r.POST("/sensors", withVars(withData(s, epPostSensors)))
err = r.Run(*addr)
```

詳しくは、以下の書籍の「6章 REST形式でデータや機能を公開する」を参照してください。

> [Go言語によるWebアプリケーション開発](https://www.oreilly.co.jp/books/9784873117522/)

## チャート描画サービス (Chart Service)

![chart_service.png](https://qiita-image-store.s3.amazonaws.com/0/166225/c28238ee-b303-58a6-f1c3-17950e0db9fd.png)

### やること

チャート描画のためのデータはWeb APIで取得可能になっているので、以下のようにすれば良さそうです。

* `axios`を使って情報取得
* `vue-chartjs`を使ってチャート表示
  * 素の`Chart.js`を使う手もあるが、`vue.js`のリアクティブ性を使いたかった

### 作ってみた

> [KemoKemo/IoT-Chart-Sample](https://github.com/KemoKemo/IoT-Chart-Sample)

以下の素晴らしい記事を参照しながら進めました。この場をお借りしてお礼申し上げますm( _ _ )m

> * [Vue.js上でChart.jsを扱うにはvue-chartjsが便利](https://katsunory.github.io/2017/02/17/beginner-vue-chartjs/)
> * [vue-chartjsでグラフをリアクティブに表示する](https://katsunory.github.io/2017/02/22/vue-chartjs-reactive/)

# まとめ

* 温度の変化を時系列で確認できるようになった
* DBサービスをWeb APIで実現することにより、利用者側のサービスにDBアクセスコードが一切不要になった
* データ変換と収集を行うマイクロサービスを追加することで、既設のIoT機器側のAPIを一切変更することなく新たなサービスを追加することができた