IoT Edge を触ってみた

Azure の IoT Edge を触ってみた。たまたま社内のハッカソンがあった。私は社内のハッカソンの時は普段触っていない技術を触るようにしている。

<img width="1385" alt="Screen Shot 2017-10-17 at 10.34.54 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/3470/edc9dc6e-f2f8-1467-c273-8d6ce89978f8.png">


本当は、ML をやりたかったが、他にやっている人がいたので、今回は、IoT Edge を担当することになった。IoT Edge は、下記のビデオがわかりやすいが、Intelligent edge と呼ばれるサービスで、サーバーで動いているAzure Functions や、Stream Analytics そしてマシンラーニングを、ローカル（Edge) で動かせるとのことだ。

* [Introducing IoT Edge](https://azure.microsoft.com/en-us/campaigns/iot-edge/)

//build で見た時は相当衝撃だったので、今のうちに IoT Edge に触れておきたい。（ちなみに、現状では残念ながら上記の機能を持った IoT Edge 2.0 はリリースされていない様子。無念）ま、ともかく、今日学んだことをメモしておく。

# IoT Edge のアーキテクチャ

試しに、Ubuntu 14.04 のマシンを Edge と見立ててサンプルが動く環境を作って見た。とてもスムースだった。

![image2.png](https://qiita-image-store.s3.amazonaws.com/0/3470/aee4941c-af37-6de6-2b75-11a324f66390.png)

Edge 側のアーキテクチャはとてもシンプルだ。（ver1.x) ポイントは、各モジュールが、Broker を経由してデータをやりとりしていること。また、ブローカーを通じて、IoT Hub の方とも通信する。
　ちなみに、Azure Functions も現在のバージョンでも、IoT Hubをサポートしているので、Broker を経由して、メッセージを投げると、IoT Hub にメッセージが送られて、そこからさらに、Azure Functions をキックするということが可能だ。

# Edge のインストール

こちらもほぼ苦労したところがない。手順のまま。尚環境は、手元に、マシンがないので、Ubuntu 14.04 LTE で代用した。

* [Explore Azure IoT Edge architecture on Linux](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-linux-iot-edge-get-started)

## 関連ライブラリのインストール

```
sudo apt-get update
sudo apt-get install curl build-essential libcurl4-openssl-dev git cmake pkg-config libssl-dev uuid-dev valgrind libglib2.0-dev libtool autoconf
```

## iot-edge ファイルの取得とビルド

```
git clone https://github.com/Azure/iot-edge.git
cd iot-edge
tools/build.sh --disable-natibe-remote-modules
```

ちなみに、`samples/hello_world/src/hello_world_lin.json` は次のようになっている。このような設定ファイルを見ていると、ブローカーのイメージがうっさらわかるかもしれない。これは、`logger` `hello_world` の二つの構成になっていて、`hello_world`の出力を`logger` に書き込んでいる。 

_hello_world_lin.json_

```json
{
    "modules" :
    [
        {
            "name" : "logger",
            "loader": {
            "name": "native",
            "entrypoint": {
                "module.path": "./modules/logger/liblogger.so"
            }
            },
            "args" : {"filename":"log.txt"}
        },
        {
            "name" : "hello_world",
            "loader": {
            "name": "native",
            "entrypoint": {
                "module.path": "./modules/hello_world/libhello_world.so"
            }
            },
            "args" : null
        }
    ],
    "links":
    [
        {
            "source": "hello_world",
            "sink": "logger"
        }
    ]
}
```

バイナリの`hello_world_sample` を実行する。

```
./samples/hello_world/hello_world_sample ../samples/hello_world/src/hello_world_lin.json
```

出力はそのまま。マニュアル通りだった。

```
[{
    "time": "Mon Apr 11 13:42:50 2016",
    "content": "Log started"
}, {
    "time": "Mon Apr 11 13:42:50 2016",
    "properties": {
        "helloWorld": "from Azure IoT Gateway SDK simple sample!"
    },
    "content": "aGVsbG8gd29ybGQ="
}, {
    "time": "Mon Apr 11 13:42:55 2016",
    "properties": {
        "helloWorld": "from Azure IoT Gateway SDK simple sample!"
    },
    "content": "aGVsbG8gd29ybGQ="
}, {
    "time": "Mon Apr 11 13:43:00 2016",
    "properties": {
        "helloWorld": "from Azure IoT Gateway SDK simple sample!"
    },
    "content": "aGVsbG8gd29ybGQ="
}, {
    "time": "Mon Apr 11 13:45:00 2016",
    "content": "Log stopped"
}]
```

# IoT Hub と接続する。

次はこちらのチュートリアル。こちらもほぼそのまんま。

* [Use Azure IoT Edge to send device-to-cloud messages with a simulated device (Linux)](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-linux-iot-edge-simulated-device)

`samples/simulated_device_cloud_upload_sample/src/simulated_device_cloud_upload_lin.json` を埋める。自分の

```json
{
    "modules": [
        {
            "name": "IotHub",
          "loader": {
            "name": "native",
            "entrypoint": {
              "module.path": "./modules/iothub/libiothub.so"
            }
            },
            "args": {
              "IoTHubName": "removeiot",
              "IoTHubSuffix": "azure-devices.net",
              "Transport": "HTTP"
            }
          },
        {
            "name": "mapping",
          "loader": {
            "name": "native",
            "entrypoint": {
              "module.path": "./modules/identitymap/libidentity_map.so"
            }
            },
            "args": [
              {
                "macAddress": "01:01:01:01:01:01",
                "deviceId": "<<insert here deviceId>>",
                "deviceKey": "<<insert here deviceKey>>"
              }
            ]
          },
        {
            "name": "BLE1",
          "loader": {
            "name": "native",
            "entrypoint": {
              "module.path": "./modules/simulated_device/libsimulated_device.so"
            }
            },
            "args": {
              "macAddress": "01:01:01:01:01:01"
            }
          },
        {
            "name": "Logger",
          "loader": {
            "name": "native",
            "entrypoint": {
              "module.path": "./modules/logger/liblogger.so"
            }
            },
            "args": {
              "filename": "deviceCloudUploadGatewaylog.log"
            }
          }
    ],
    "links": [
        {
            "source": "*",
            "sink": "Logger"
        },
        {
            "source": "BLE1",
            "sink": "mapping"
        },

        {
            "source": "mapping",
            "sink": "IotHub"
        }
    ]
}
```

ちなみに、device id は、IoT Hub の方で、生成する。Device Explorer で作成できる。

<img width="1124" alt="Screen Shot 2017-10-17 at 11.30.02 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/3470/294aada6-5684-e1a1-0a5e-004adcb3bddb.png">

これで実行

```
./samples/simulated_device_cloud_upload/simulated_device_cloud_upload_sample ../samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_lin.json
```

ただ、これだと結果がわからないので、Azure Functions に繋いでみる

# Azure Functions を生成してみる。

実は、現在の、Azure Functions では、IoT Hub に直接バインドすることができた。私の好きな、TypeScript + IoT Hub のバインディングスを使って繋いで見た。

特に解説はないが、最初`message` の持っているプロパティがわからなかったので、`Object.getOwnPropertyNames` で調べて見た。そして、ダンプして見た。

```ts
export function run(context: any, IoTHubMessages: any[]): void {
    context.log(`TypeScript eventhub trigger function called for message array ${IoTHubMessages}`);
   

    IoTHubMessages.forEach(message => {
        let names = Object.getOwnPropertyNames(message);
        context.log("properyName: " +names);
        context.log("temperature = " + message.temperature);
        
        context.log(`Processed message ${message}`);
    });

    context.done();
};
```

_function.json_

```json
{
  "bindings": [
    {
      "type": "eventHubTrigger",
      "name": "IoTHubMessages",
      "direction": "in",
      "path": "samples-workitems",
      "connection": "removeiot_events_IOTHUB",
      "cardinality": "many",
      "consumerGroup": "$Default"
    }
  ],
  "disabled": false
}
```
IoT Edge からしっかりデータがきているのがわかる。

```
2017-10-17T14:35:08  Welcome, you are now connected to log-streaming service.
2017-10-17T14:36:05.464 Function started (Id=1dd2fa3e-83e0-456c-974c-6a2eec896dae)
2017-10-17T14:36:05.464 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:05.464 properyName: temperature
2017-10-17T14:36:05.464 temperature = 10
2017-10-17T14:36:05.464 Processed message [object Object]
2017-10-17T14:36:05.464 Function completed (Success, Id=1dd2fa3e-83e0-456c-974c-6a2eec896dae, Duration=7ms)
2017-10-17T14:36:07.051 Function started (Id=68e2efd1-54bb-4fe3-b275-15371b004a31)
2017-10-17T14:36:07.051 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:07.051 properyName: temperature
2017-10-17T14:36:07.051 temperature = 11
2017-10-17T14:36:07.051 Processed message [object Object]
2017-10-17T14:36:07.051 Function completed (Success, Id=68e2efd1-54bb-4fe3-b275-15371b004a31, Duration=1ms)
2017-10-17T14:36:09.050 Function started (Id=6da7743e-1070-4049-bc58-30c94901bdc4)
2017-10-17T14:36:09.050 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:09.050 properyName: temperature
2017-10-17T14:36:09.050 temperature = 12
2017-10-17T14:36:09.050 Processed message [object Object]
2017-10-17T14:36:09.050 Function completed (Success, Id=6da7743e-1070-4049-bc58-30c94901bdc4, Duration=1ms)
2017-10-17T14:36:11.048 Function started (Id=5dea256e-c500-4c4f-a404-17cdf3c76852)
2017-10-17T14:36:11.048 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:11.048 properyName: temperature
2017-10-17T14:36:11.048 temperature = 13
2017-10-17T14:36:11.048 Processed message [object Object]
2017-10-17T14:36:11.048 Function completed (Success, Id=5dea256e-c500-4c4f-a404-17cdf3c76852, Duration=0ms)
2017-10-17T14:36:13.057 Function started (Id=f82e8fc7-c171-4646-abbd-f1868bf64be5)
2017-10-17T14:36:13.057 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:13.057 properyName: temperature
2017-10-17T14:36:13.057 temperature = 14
2017-10-17T14:36:13.057 Processed message [object Object]
2017-10-17T14:36:13.057 Function completed (Success, Id=f82e8fc7-c171-4646-abbd-f1868bf64be5, Duration=0ms)
2017-10-17T14:36:15.101 Function started (Id=03da80a3-ebae-4019-96fd-d85c329553a3)
2017-10-17T14:36:15.101 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:15.101 properyName: temperature
2017-10-17T14:36:15.101 temperature = 15
2017-10-17T14:36:15.101 Processed message [object Object]
2017-10-17T14:36:15.101 Function completed (Success, Id=03da80a3-ebae-4019-96fd-d85c329553a3, Duration=1ms)
2017-10-17T14:36:17.064 Function started (Id=ed63f63b-9e58-4ed3-9fb2-a02cd59cdd4d)
2017-10-17T14:36:17.064 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:17.064 properyName: temperature
2017-10-17T14:36:17.064 temperature = 16
2017-10-17T14:36:17.064 Processed message [object Object]
2017-10-17T14:36:17.064 Function completed (Success, Id=ed63f63b-9e58-4ed3-9fb2-a02cd59cdd4d, Duration=1ms)
2017-10-17T14:36:19.064 Function started (Id=633e5861-d81d-4e1f-b3dd-9e50b135997e)
2017-10-17T14:36:19.064 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:19.064 properyName: temperature
2017-10-17T14:36:19.064 temperature = 17
2017-10-17T14:36:19.064 Processed message [object Object]
2017-10-17T14:36:19.064 Function completed (Success, Id=633e5861-d81d-4e1f-b3dd-9e50b135997e, Duration=0ms)
2017-10-17T14:36:21.062 Function started (Id=49cc53ae-7e9a-4344-a114-b9b6a380915f)
2017-10-17T14:36:21.062 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:21.062 properyName: temperature
2017-10-17T14:36:21.062 temperature = 18
2017-10-17T14:36:21.062 Processed message [object Object]
2017-10-17T14:36:21.062 Function completed (Success, Id=49cc53ae-7e9a-4344-a114-b9b6a380915f, Duration=1ms)
2017-10-17T14:36:23.048 Function started (Id=740a9a65-5554-4738-aaeb-670d49bff0d1)
2017-10-17T14:36:23.048 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:23.048 properyName: temperature
2017-10-17T14:36:23.048 temperature = 19
2017-10-17T14:36:23.048 Processed message [object Object]
2017-10-17T14:36:23.048 Function completed (Success, Id=740a9a65-5554-4738-aaeb-670d49bff0d1, Duration=1ms)
2017-10-17T14:36:25.057 Function started (Id=3bcd6cb6-7c29-4ca6-b0d8-f8271f24dda3)
2017-10-17T14:36:25.057 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:25.057 properyName: temperature
2017-10-17T14:36:25.057 temperature = 20
2017-10-17T14:36:25.057 Processed message [object Object]
2017-10-17T14:36:25.057 Function completed (Success, Id=3bcd6cb6-7c29-4ca6-b0d8-f8271f24dda3, Duration=1ms)
2017-10-17T14:36:27.060 Function started (Id=7240e6e5-f974-4490-9baf-f7550e09b6b1)
2017-10-17T14:36:27.060 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:27.060 properyName: temperature
2017-10-17T14:36:27.060 temperature = 21
2017-10-17T14:36:27.060 Processed message [object Object]
2017-10-17T14:36:27.060 Function completed (Success, Id=7240e6e5-f974-4490-9baf-f7550e09b6b1, Duration=1ms)
2017-10-17T14:36:29.069 Function started (Id=912b8bae-fc4c-47e2-bc73-0591316ef597)
2017-10-17T14:36:29.069 TypeScript eventhub trigger function called for message array [object Object]
2017-10-17T14:36:29.069 properyName: temperature
2017-10-17T14:36:29.069 temperature = 22
2017-10-17T14:36:29.069 Processed message [object Object]
2017-10-17T14:36:29.069 Function completed (Success, Id=912b8bae-fc4c-47e2-bc73-0591316ef597, 
```

ちなみに、Service Bus を経由して使う方法は昔はメジャーだったらしい。今は、Azure Functions 直接が主流ね。つまり下記のは古い。

* [How to use Azure Functions with IoT Hub message routing](https://azure.microsoft.com/en-us/blog/how-to-use-azure-functions-with-iot-hub-message-routing/)

# 終わりに

ちなみに、本当は、IoT Edge の Edge にコンテナダウンロードしたかったので、悔しいから、Functions をコンテナで動かせる Dockerfile 書いてみようかな。

あと、IoT Edge とくみあわせていい感じになりそうなサービスがあるので、次回試してみたい。ちなみにこのハックは日を空けて３日づつくので

* [Send events to a Time Series Insights environment using event hub](https://docs.microsoft.com/en-us/azure/time-series-insights/time-series-insights-send-events)
* [Create and provision a simulated device using IoT Hub Device Provisioning Service (preview)](https://docs.microsoft.com/en-us/azure/iot-dps/quick-create-simulated-device)


# Resource
* [Azure / iot-edge](https://github.com/azure/iot-edge)