EC2インスタンスにNode-Redで仮想IoTデバイスを構築

#まえがき

もともとIoTについては大学で概念のみ習って興味はあったのですが、どうやって自宅で、個人で触ってみることができるかちょっと思いつけず臆していました。
そんな折Node-Redの存在を知り、これだったら仮想IoT環境の構築、モックアプリの開発もできるのではないかと考えたのでやってみた次第です。

#シナリオ
とある病院の精神科病棟では時に興奮状態にあることも多い患者の病室へと入る際に患者が部屋のどこにいるか確認する方法が小窓しかなく、扉の横に隠れていた場合など職員が入室する際に怪我を負う事例の発生を抑制できずにいました。
しかし通常のカメラによる24時間の監視は患者の人権を守るという当病院のプライバシーポリシーに違反します。
そこで何かしらのソリューションを用いて肖像権などを侵害することなく病室の安全性を高めてほしいというのが病院からの要望です。

#ソリューション設計

赤外線カメラは低解像度のものなら気温の値を、配列で返します。（例　16X16の座標の場合長さ256の配列であり、今回は仮想データで可視性のため4X4）
その中で最も高い点の座標がすなわち、多くの場合人間の最も体温の高い部分である頭部あるいはその付近である体の中心になります。
カメラはすくなくとも病室に対して平行、垂直なビジョンの2つを用意することで三次元での座標を特定し患者が立っているか寝ているかまで確認することが可能になります。
赤外線カメラを搭載したマイクロコンピュータ上のエッジコンピューティングによって座標のみをサーバに渡すのであればデータはプライバシーを十分に守ることができます。
また、デバイスにマイクも搭載し、タイムスタンプ毎の音量のみを記録することで患者の話す内容を暴くことなく患者が興奮状態にあるかどうかを確認することができます。

![image15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/a899fc27-5404-5697-713b-c41851f58334.jpeg)

> 画像は初期段階の構想で少し違うところがあります。
> 申し訳ありません。



#ソリューションのスコープ

ソリューションのスコープはそのソリューションの機能とケイパビリティを定義するものです。
ケイパビリティに関しては数字はすべて仮のものです。

##ソリューションの機能

* グリッドで示した室内マップでの患者の位置を表示する。
* アクティビティの一覧を表示する。アクティビティは病室のID、患者の身体的状態、音声から推測される精神的状態、タイムスタンプからなる。
* 上記の機能は看護師等、当病院関係者のもつタブレットから確認することができる。
* 機密情報にアクセス権限を求める（未実装）
* データを医療目的でのみ応用できるようデータはそれだけでは個人情報を持たせないことで匿名性を保つ。
* 24時間情報を受け取れるようにすること。

##ソリューションのケイパビリティ

* 看護師が患者由来の負傷をする割合を10％減少させる。
* 看護師の夜間勤務の労働負荷を30％減少させる。
* 患者の自傷インシデントの発生率を5％減少させる。
* 全室モニタリング機能をタブレットとモニター室両方で可能にする。

#Node-Redでのシミュレーション

実際にマイクロコンピュータやセンサーを用意してテストする財力は今現在学生である自分には少し難しいため、EC2にインストールしておいたNode-Redでシミュレーションするにとどめます。
今回はランダムな値を生成するノード、別にフロントエンドに任せてもいいので不要なんですがMoment的なDateをフォーマットするノード、IBM Cloudantに渡すためのノードの3種類をインストールしました。

##Node-Redフロー図

![node-red-flows.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/9fe8d37d-23c4-0388-7f8c-ae767f40d0f5.png)

少し画像が小さくて申し訳ありませんが、上記のフローによって5つの必要なデータを持つJSONをIBM　Cloudantのデータベースへ送るように設定されています。
仮想データですのでインスタンスの起動後5秒毎に送信されるようになっています。
また事前に仮想データの整合性の確認のためにIBM Watson IoT Simulationを用いて物理インターフェースから論理インターフェースへデータの変換テストを行っています。

##Cloudantで受け取ったデータ

![cloudant-capture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/755e9e28-7e41-2e46-045b-f9d101706d6e.png)

アンダースコアのないidは病室のIDということにしてあります。
タイムスタンプは冗長ではありますが可読性の高いものとネイティブな日付の2つを用意しています。
temperatureの2つの値はそれぞれ上から横からの最も温度の高い座標の配列の位置になっています。（理由は上述）
音声は単位をデシベルとした場合の数値になっています。

#フロントエンドの作成

フロントエンドではReact.jsを用いてクライアントからcloudantのAPIを叩きます。
APIキーなどのクレデンシャルはビルドツールに用いているJenkinsに渡してあります。
言い訳になりますがリアルタイムでcloudantからデータを受け取って可視化することのみを目的としていますのでお粗末なデザインになっています。

##ランディングページ

![frontend-activity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/174ff909-9d48-eede-ebc4-0a569141ff72.png)

rootのページでは5秒おきに最新のデータを受けとり、テーブルに表示します。
座標によって患者のPhysical Stateの値が変動し、音声によってBehavioural Stateの値が変動するようになっています。

###APIコール
cloudantのAPIは多くの場合POSTで検索、ビュー条件をBodyから渡して結果を得ます。GETコールはすべての値を受け取るもので、データの用途によりますが今回のケースではあまり実用的ではありません。

```javascript
import axios from 'axios'
import { encode } from 'js-base64'

function App(){
  async function getData(){
    axios.post(`${CloudantURL}/${DatabaseName}/_find`, {
      selector: {
          nativeTS: {
              $gt: 0
           }
        },
      fields: {
          ["_id", "id", "timestamp", "nativeTS", "sound", "temperatureSide", "temperaturePlain"]
      },
      sort: [{nativeTS: "desc"}],
      limit: 100,
      skip: 0,
    },
   headers:{
        "Authorization": `Basic ${encode(CloudantAPI_KEY)}`,
   },
  ).then( res => {
      setState(res.data.docs)
    }).catch(err => {
      if(err){
        console.error(err)
      }
    })
 }
}
```

##病室毎のページ

![frontend-patient.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/b3792106-179b-088c-beef-8ca5fe8ccb2e.png)

拙いグリッドではありますが、扉からみた患者の頭の位置を表示するようになっています。
それぞれの値の変動はランディングページと同じです。
こちらも病室の最新のデータを5秒おきに受け取っています。

#あとがき

IoTは現実の問題を解決することにおいてとても強いポテンシャルを持っています。
今回こそ仮想インスタンスを用いて行いましたが、Node-RedをラズパイやArduinoにインストールすればフロントエンド一辺倒な勉強しかしてこなかった私のような人間には複雑なマイコン上のプログラムもローコードを用いて容易に設定することができます。
初学者として拙いものを作っただけですが、私のようにノーコードやローコードを触ってはみたいもののとっかかりが思いつかない人もいるのではないかと思いますので、そのような方への参考になれば幸いです。