IoT Kitハンズオンセミナーで実演している、センサーデータ収集とExcelによる表示の仕組みについて

2月のデベロッパーサミットのハンズオンで初披露し、3/15のIoTあるじゃん(https://www.facebook.com/groups/ioytjp/)コミュニティ設立イベントでのハンズオン、それと、最近地方で開催しているIoT Kitハンズオンセミナーで、もれなく使っている、参加者の皆さんが各自のGR SAKURA+センサーボードやGR PEACH+センサーボードでの実習で計測したデータを、集めて、ExcelのPower Mapでの表示に関する説明です。

この右側の赤の点線で囲んだ部分です。

この部分は大きく分けて二つのパーツから構成されています。一つ目は、受講者各自が作成したASP.NET WebアプリからSignalRで送信されたセンサーデータを受信しAzure StorageのTableに格納する部分、二つ目は、そのTableからExcel Power Queryでデータを取り出し、Excel PowerMapで表示する部分です。

1つ目は図の4番目のステップで、SignalRというプロトコルを使って、私が作って動かしているASP.NET Webアプリケーションにデータを送っています。作り方は、https://aka.ms/IoTKitHoL から公開中のハンズオントレーニングのStep 4 表示の、4.1節で解説している方法で作っています。手順書を見ながら以下の説明を読んでください。

SignalR?なんか難しそう…と思っている方、大丈夫です。簡単です。先ず、ハンズオントレーニングのStep1 接続の1.1節で説明された方法で、Azure上にWebアプリを作成します。出来上がったら、Step 4表示の4.1節で説明された方法で、SignalRのハブを”SensorHub”という名前で作成します。

で、ここでは、Azure Storageを使うので、NuGetパッケージで、Windows Azure Storeage SDKをインストールします。やり方は、ハンズオントレーニングのStep 3蓄積の1.2、1.3節に書いてあります。Azure上でのストレージアカウントの作成や、Web.configへのストレージへのConnectionString設定などは手順書を見て適宜やってください。

作成したSensorHubクラスに、以下の様なメソッドとクラスを追加します。

        public void Update(SensorReading data)
        {
            Clients.Others.NotifyTemperature(data);

            var storeCS = CloudConfigurationManager.GetSetting("StorageConnectionString");
            var storageAccount = CloudStorageAccount.Parse(storeCS);
            var tableClient = storageAccount.CreateCloudTableClient();
            var sensorReadingTable = tableClient.GetTableReference("SensorReading");
            sensorReadingTable.CreateIfNotExistsAsync().Wait();

            var query = new TableQuery<SensorReadingTable>().Where(
     TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, data.DeviceId));
            bool unstored = true;
            foreach (var stored in sensorReadingTable.ExecuteQuery(query))
            {
                // 登録されていたら新しい温度で更新
                stored.LastUpdatedTime = data.Timestamp;
                stored.SensorValue = data.Temperature.ToString();
                stored.SensorValueD = data.Temperature;
                stored.Count = stored.Count + 1;
                sensorReadingTable.ExecuteAsync(TableOperation.Replace(stored)).Wait();
                unstored = false;
            }

            if (unstored)
            {
                // 未登録なら、新しいレコードを追加
                var sr = new SensorReadingTable(data.DeviceId, DateTime.Now);
                sr.Timestamp = data.Timestamp;
                sr.SensorType = "Temperature";
                sr.SensorValue = data.Temperature.ToString();
                sr.SensorValueD = data.Temperature;
                sr.CreatedTime = data.Timestamp;
                sr.LastUpdatedTime = data.Timestamp;
                sr.Count = 1;
                sensorReadingTable.ExecuteAsync(TableOperation.Insert(sr)).Wait();
            }
        }

        public class SensorReading
        {
            public string DeviceId { get; set; }
            public DateTime Timestamp { get; set; }
            public double Temperature { get; set; }
        }

        public class SensorReadingTable : TableEntity
        {
            public SensorReadingTable(){

            }

            public SensorReadingTable(string deviceId, DateTime time)
            {
                PartitionKey = deviceId;
                RowKey = deviceId + time.Ticks.ToString();
            }
            public string SensorType { get; set; }
            public string SensorValue { get; set; }
            public double SensorValueD { get; set; }
            public DateTime CreatedTime { get; set; }
            public DateTime LastUpdatedTime { get; set; }
            public int Count { get; set; }
        }

これで、「4.SignalR送信用Webアプリ作成」で追加したコードが実行され、SignalRでセンサーデータが送信されたときに受信するHubのロジックが出来上がります。一見複雑そうですが、やっていることは簡単で、Updateメソッドの引数dataが受講者が作ったWebアプリから送られてくるデータ、それを受けて、テーブルを準備して、送られてきたDeviceIdに関するセンサーデータのレコードがあれば、最新情報で更新、なければ新規にレコードを追加、という処理です。

後は、Startup.csにあるConfigurationメソッドを

        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            app.MapSignalR();
        }

の様にapp.MapSignalR()を追加すれば実装終了。あとはVisual StudioでAzureに発行すれば準備完了。受講者のGR SAKURAやGR PEACH、FEZで各自のMobile Serviceにセンサーデータを送ると、このHubのUpdateメソッドがどんどんコールされ、各参加者毎の計測データが逐次蓄積されていきます。SensorReadingTableのプロパティ群(PartitionKey、RowKey、SensorType、SensorValue(文字列形式)、SensorValueD(数値形式)、CreatedTime、LastUpdatedTime、Count)がテーブルの各カラムになります。PartitionKeyにはDeviceIdが格納されます。

3/15に実施したGR PEACHを使ったハンズオンの場合は、https://1drv.ms/1xuYj1k からGR SAKURAを使ったハンズオン資料をダウンロードして、Mobile Service、Webアプリを作ってください。WebアプリのSignalRの転送先のURLを変えれば動きます。

修正が終わったらAzureに”発行”してください。
GR PEACHやGR SAKURAで温度を測ってクラウドに送信してください。温度を送信するとテーブルが出来上がります。逆にいうと、温度が送られない限りテーブルは作成されないので、発行したら、必ずボードをイーサネットに接続して温度センサーの値をクラウドに送ってくださいね。

次にExcelでの表示です。これはハンズオントレーニングのStep 5 分析の1.1、1.2で解説している手順がベースになります。Power Qeury、Power Mapのセットアップはこちらの手順書を参考にしてください。

Power QueryとPower Mapのアドインを入れたら、ExcelでHeatmapを作っていきます。

先ず、Excelを起動して、新しいシートを作成します。

リボンのPOWER QUERYタブをクリックします。

図の様に”Azureからインポート”をクリックし、”Microsoft Azureテーブルストレージから”を選択します。
ストレージアカウント名を入力して”OK"をクリックするとナビゲーターペインにテーブル名が表示されます。※今回は事情により”SensorReading2”がターゲットとしています。
インポートするテーブルをクリックすると、クエリエディターでAzureストレージに格納されているデータが表示されます。

Contentの右側の矢印ボタンをクリックするとSensorReadingTableで定義されたプロパティ名のデータが表示されます。クエリエディタを閉じればクラウド上のデータがダウンロードされて表示されます。

次に、POWER PIVOTタブをクリックして、

データモデルに追加をクリック。で、テーブルツールを閉じます。
各参加者の温度を棒グラフで表示する場所を次に定義します。

ここでは簡単のため4人の場合を想定しています。要は位置をPositionというシートにべた決めってことです。図は左上を原点(0,0)として、右方向がX軸の正方向、下方向がY軸の正方向です。01、02、03、04というラベルがついたセルの100×100ピクセルの画像を想定しています。DeviceIdと各座標を書いたら、マウスで選択して、POWER PIVOTの”データモデルを追加”をクリックします。

テーブルの作成ダイアログの”先頭行をテーブルの見出しとして使用する”にチェックを入れて”OK”をクリックします。
Power Pivot for Excelのページが表示されるので、”ダイアグラムビュー”をクリックします。

表示されたら、テーブルの名前を変えて、PositionのDeviceIdとSensorReadingのPartitionKeyをつなぎます。

これでPower Mapで表示するためのデータモデルが完成しました。POWER PIVOTのページを閉じて、

挿入タブをクリックし、”マップ”→”Power Mapの起動”を選択します。

図の様に、”シーンのオプション”をクリックして、表示する画像を選択します。

背景画像を設定し、”ピクセル領域”ボタンをクリックして”完了”をクリックして背景画像が設定されます。
位置情報は、

PositionのX,Yにチェックを入れて、ペインの下でX座標とY座標にマップします。

SensorValueD、Timestampにチェックを入れて完成です。

リボンの”データの更新”をクリックすると最新の情報で表示が更新されます。時系列でのアニメーションも可能です。

さて、クラウドからのデータのダウンロードですが、定期的に更新した場合には、以下の設定をしてください。