GadgeteerとAzureで作る簡易温度計 - Part 2

さて、続き。

Part 1は、https://blogs.msdn.com/b/hirosho/archive/2014/07/25/gadgeteer-azure-part-1.aspx こちら。

  1. Azure Mobile Serviceを作成
  2. Gadgeteerのアプリ開発
  3. Excel Power Queryで可視化
  4. Excel Power Viewで分析
  5. Azure Web Siteを作成
  6. Azure Mobile Serviceを改造 - Web Siteへのデータ転送
  7. Store アプリ開発
  8. Azure Mobile Serviceを改造 - Push 通知の追加

の4番目は別の投稿でじっくり説明するとして、5から始めます。

5. Azure Web Siteを作成

ここでは、AzureのWeb Siteを使って、Mobile Serviceにアップロードされたデータを転送するためのWeb APIと、それをSignalRで通知するためのHubを作成します。

Visual Studio 2013を起動し、メニューの”ファイル”→”新規作成”→”プロジェクト”を選択して、Visual C#のWebカテゴリーのASP.NET Webアプリケーションテンプレートを使って、プロジェクトを作成します。MVCを選択し、Web APIにチェックを入れて作成します。

そのまま、Azure Web Site上に作れてしまうので、指示に従ってWebサイトをAzure Web Site上に作ってしまいます。

SignalRのハブを作成します。SignalRは、インターネット上で、計測した温度をStoreアプリに通知するために利用します。
ソリューションエクスプローラーで、プロジェクトの”Models”フォルダーに”SensorReading.cs”という名前でクラスを追加します。これは、モバイルサービスからのデータ転送、SignalRによる同報通信の際にデータを格納する為のクラスです。以下のようにコーディングします。

    public class SensorReading
    {
        public string SensorId { get; set; }
        public string SensorType { get; set; }
        public string Timestamp { get; set; }
        public double MeasuredValue { get; set; }
    }

次にソリューションエクスプローラーで、プロジェクトに”Hubs”というフォルダーを追加します。(プロジェクトを右クリックし、”追加”→”新しいフォルダー”を選択”)そして、そのフォルダーに、”SignalRハブクラス”というアイテムテンプレートを使って、SensorValueHub.csという名前でクラスを作成(”Hubs”フォルダーを右クリックし”追加”→”新しい項目”を選択”)します。

以下のようにコードを編集します。

    public class SensorValueHub : Hub
    {
        public void Update(Models.SensorReading model)
        {
            Clients.Others.Update(model);
        }
    }

SignalRを初期化するコードを追加します。ソリューションエクスプローラーで、”Startup.cs”というファイルを開き、以下のようにコードを編集します。

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

次に、モバイルサービスからSignalRのハブに転送するためのWeb APIを追加します。

ソリューションエクスプローラーで、”Controllers”フォルダーを右クリックし、”追加”→”コントローラ”を選択し、表示されたダイアログで”WEB API 2 コントローラ - 空”を選択し、”PostSensorReadingController.cs”という名前のクラスを作成します。コードは以下の通りです。

    public class PostSensorReadingController : ApiController
    {
        public void Post(Models.SensorReading model)
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<Hubs.SensorHub>();
            context.Clients.All.Update(model);
        }
    }

これで、AzureのWeb Site側のコードは作成完了です。プロジェクトを右クリックし、”発行”を選択して、Web Siteに配置してください。

6. Azure Mobile Serviceを改造 - Web Siteへのデータ転送

前のステップで作成したWeb APIに対して、モバイルサービスにデータが挿入された時にアクセスするようモバイルサービスを改造します。サーバーエクスプローラーで、Windows Azure→モバイルサービスまでを開き、ステップ1で作成したモバイルサービスを探します。そのモバイルサービスの下のSensorReadingテーブルを更に開きます。直下にinsert.jsというファイルがあるので、それをダブルクリックして開きます。

insert.jsのコードは以下のように書きましょう。

function insert(item, user, request) {
    var httpRequest = require('request');
    var date = new Date();

    var postData = {
        'SensorId': item.SensorId,
        'SensorType': item.SensorType,
        'MeasuredValue': item.MeasuredValue,
        'Timestamp': date.toISOString()
    };

    httpRequest.post({
        uri: 'https://your-website.azurewebsites.net/api/PostSensorReading',
        headers: { 'Content-Type': 'applicaiton/json' },
        body: JSON.stringify(postData)
    }, function (err, res, body) {
        if (err) {
            console.log("Post Failed");
        }
    });

    request.execute();

}

your-websiteの部分を、作成したWeb SiteのURLに合わせて変えてください
httpRequestのpostメソッドをコールすることにより、前のステップで作成したWeb APIにJSON形式でデータが送られます。Web Site側では、PostSensorReadingControllerのPostメソッドがコールされます。コードを書き終わったら保存するのを忘れずに。

 7. Storeアプリ開発

さて、.NET Gadgeteerでセンサー計測→モバイルサービスへのInsert→Web APIでJSON受信→SignalRハブで同報送信という流れで送信されたデータを受信して表示するStoreアプリを開発します。
Microsoft Azureの管理ポータルで、ステップ1で作ったモバイルサービスのページを開き、プラットフォームの選択で”Windows ストア”を選択、”新しいWINDOWS ストアアプリを作成する”をクリックします。

3の”ダウンロード”ボタンをクリックしてStoreアプリをダウンロードします。
Visual Studio 2013を起動し、XAML/C#でストアアプリプロジェクトを一つ作成します。プロジェクトテンプレートは空のアプリで良いです。

作成されたプロジェクトをソリューションエクスプローラーで右クリックし、NuGetパッケージの管理を選択します。右上の検索窓に”SignalR”と入力

Microsoft ASP.NET SignalR .NET Clientを選択して、”インストール”ボタンをクリックします。

次に、SensorReadingPage.xamlという名前で、基本ページを追加します。このページに、図の様にGridを一つ追加し、データのラベル用にTextBockを4つ、データの表示用にTextBlockを4つ追加します。

SensorReadingPage.xaml.csを開き、SensorReadingPageクラスに以下のコードを追加します。Azure Web サイトからSignalRによる通信を受信するためのロジックです。HubConnectionコンストラクターの引数のURLは、各自のWeb サイトのURLに変えてください。

        IHubProxy sensorReadingHub;
        private async Task MakeConnectionAsync()
        {
            Exception lastEx = null;
            try
            {
                var hubConnection = new HubConnection("https://your-website.azurewebsites.net");
                sensorReadingHub = hubConnection.CreateHubProxy("sensorreadinghub");
                await hubConnection.Start();
                var updateSubscribe = sensorReadingHub.Subscribe("Update");
                updateSubscribe.Received += updateSubscribe_Received;
            }
            catch (Exception ex)
            {
                lastEx = ex;
            }
            if (lastEx != null)
            {
                var dialog = new MessageDialog(lastEx.Message);
                await dialog.ShowAsync();
            }
        }

        // SignalRのメッセージを受信した際にコールされるメソッド
        async void updateSubscribe_Received(IList<Newtonsoft.Json.Linq.JToken> obj)
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                foreach (var packet in obj)
                {
                    var sensorType = packet.Value<string>("sensortype");
                    if (sensorType == "Temperature")
                    {
                        tbMesauredValue.Text = packet.Value<double>("measuredvalue").ToString();
                        tbSensorId.Text = packet.Value<string>("sensorid");
                        tbSensorType.Text = sensorType;
                        tbTimestamp.Text = packet.Value<string>("timestamp");
                    }
                }
            });
        }

 そして、navigationHelper_LoadStateメソッドを以下の様に書き換えます。

        private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            if (sensorHub == null)
            {
                await MakeConnectionAsync();
            }
        }

更に、App.xaml.csの85行目の”MainPage”というクラス名を以下のように書き換えます。

                rootFrame.Navigate(typeof(SensorReadingPage), e.Arguments);

これで、SensorReadingPageがアプリ起動時に表示されます。
今作成したストアアプリを起動して、Gadgeteerも起動すると、センサーが計測した温度が表示されます。

8. Azure Mobile Serviceを改造 - Push 通知の追加

最後は、GadgeteerがAzureモバイルサービスにデータをアップロードした時に、温度が30度以上ならPush通知を送信するように改造します。
Push通知を受信するためには、アプリケーションをストアに登録(あくまでも登録です。公開ではありません)する必要があります。プロジェクトを右クリックし、”ストア”→”アプリケーションをストアと関連付ける”を選択し、一連の操作でダッシュボード上のアプリと関連付けを済ませます。この作業には、Windowsストアへの開発者登録が必要です。まだ開発者登録していない方はこの際に、登録してしまいましょう。
次に、Push通知を受けるために、ソリューションエクスプローラーで、ストアアプリプロジェクトを右クリックし、”追加”→”プッシュ通知”を選択して、一連のダイアログに従いストアアプリプロジェクトにプッシュ通知機能を追加します。
この作業を実施すると、Storeアプリ側にはPush通知を受信するためのコードが自動的に埋め込まれ、クラウド側のモバイルサービスにもPush通知の送信に必要な機能が自動的に組み込まれます。

最後に、Azure モバイルサービスのテーブルのスクリプト、Insert.jsを以下のように書き換えます。

function insert(item, user, request) {
    var httpRequest = require('request');
    var date = new Date();
    var postItem = {
        'SensorId': item.SensorId,
        'SensorType': item.SensorType,
        'MeasuredValue': item.MeasuredValue,
        'Timestamp': timestampValue
    };
    httpRequest.post({
        uri: 'https://eg-sensor-management.azurewebsites.net/api/PostValue',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(postItem)
    }, function (err, res, body) {
        if (err) {
            console.log("Temp - Post Failed");
        }
        else {
            //          console.log("Temp - Post Succeeded");
        }
    });
    if (item.sensortype == "Temperature") {
        if (item.MeasuredValue > 30) {
            sendNotifications("Too Hot! - " + item.SensorId + ":" + item.MeasuredValue);
        }
    }
    request.execute();

    function sendNotifications(message) {
        var payload = '<?xml version="1.0" encoding="utf-8"?><toast><visual><binding template="ToastText01">' +
            '<text id="1">' + message + '</text></binding></visual></toast>';
        push.wns.send(null,
            payload,
            'wns/toast', {
                success: function (pushResponse) {
                    console.log("Sent push:", pushResponse);
                }
            });
    }
}

太字で示したコードが、追加するコードです。Push通知のチャネルを管理しているchannelテーブルから登録済みのチャネルを取り出し、送信しています。

はい、これでお終い。後は、GadgeteerをVisual Studioで起動して温度センサーを手とかで優しく温めて、30度以上にすると、Push通知が送信されます。