.NET Micro FrameworkとWindows AzureとWindows Store Appsでプチセンサークラウドを試してみる


.NET Micro Frameworkでセンサーで物理データが収集できて、Aureでクラウドが使えて、Windows 8 ストアアプリでネットワークからデータを参照できるとなると、センサークラウド試してみるしかないですよね。

デバイス側をやっている人達からすると、いきなりホンキのクラウドでやろうというのは、マインド的にハードルが高いかなと思うので、クラウド側はPC上のエミュレータ(Windows Azureはエミュレータがあるのでテストも楽ですね)を使い、センサーデバイスもハードウェアを持っていない人も試せるように.NET Micro FrameworkのEmulatorを使って説明します。

クラウド側の開発

先ず、Cloud側ですが、センサーデータのアップロードと蓄積データを取得するREST APIを提供するクラウドサービスを作成します。蓄積データはAzureストレージサービスのテーブルに格納します。言語はすべてC#で解説します。
作業を始める前に、Windows Azure開発に必要なSDKをインストールしてください。

Visual Studioで、メニューから、”新規作成”→”プロジェクト”を選択して、C#の”Windows Azure クラウドサービス”を作成します。本格的にはASP.NET系のWebロールを使うところですが、基本は一緒なので説明をシンプルにするため、”WCFサービス Web ロール”を一つ作成します。名前はそれっぽく変えてください。ここではSensorServiceとしておきます。

出来上がったSensorServiceプロジェクトのIService1.csとService1.svcは削除してください。

先ずは、Azure Storageのテーブルとテーブルにアクセスする仕組みを作成します。

SensorServiceプロジェクトにSensorDataTableクラスを追加します。

ソリューションエクスプローラーにできたSensorDataTable.csを以下の様に修正します。

    public class SensorDataTableManagement
    {
        public static readonly string TableName = "SensorDataTable";
        private CloudTableClient tableClient;

        public CloudTableClient Initialize(CloudStorageAccount accont)
        {
            tableClient = new CloudTableClient(accont.TableEndpoint.ToString(), accont.Credentials);
            tableClient.CreateTableIfNotExist(TableName);

            return tableClient;
        }

        public TableServiceContext GetTableContext()
        {
            TableServiceContext context = null;
            if (tableClient != null)
            {
                context = tableClient.GetDataServiceContext();
            }
            return context;
        }

    }

    public class SensorDataTable : TableServiceEntity
    {
        public SensorDataTable()
        {
            PartitionKey = DateTime.Now.ToString("yyyyMMdd-hhmmss");
            RowKey = string.Empty;
        }

        public String SensorId
        {
            get
            {
                return RowKey;
            }
            set
            {
                RowKey = value;
            }
        }

        public string SensorType { get; set; }
        public DateTime MeasuredTime { get; set; }
        public string MeasuredValue { get; set; }
    }
}

コードの中のSensorDataTableクラス(TableServiceEntityを継承)が、センサーで計測した値を格納するテーブルの定義です。センサーを識別するID(SensorId)、センサーの種別(SensorType)、計測値(MeasuredValue)、計測時間(MeasuredTime)をカラムとして持つテーブルです。SensorDataTableManagementクラスは、実際にAzure Storageサービスが管理するテーブルにアクセスするためのヘルパークラスです。

次に、SensorServiceプロジェクトを作成したときに自動的に作成されているWebRole.csを以下の様に修正します。

    public class WebRole : RoleEntryPoint
    {
        static SensorDataTableManagement sensorDataTableManagement;
        public override bool OnStart()
        {
            // AzureLocalStorageTraceListner を有効にするには、web.config の関連するセクションのコメントを解除します 
            DiagnosticMonitorConfiguration diagnosticConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();
            diagnosticConfig.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
            diagnosticConfig.Directories.DataSources.Add(AzureLocalStorageTraceListener.GetLogDirectory());

            // 構成の変更を処理する方法については、
            // MSDN トピック (http://go.microsoft.com/fwlink/?LinkId=166357) を参照してください。

            var connectionString = RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString");
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            sensorDataTableManagement = new SensorDataTableManagement();
            sensorDataTableManagement.Initialize(storageAccount);

            return base.OnStart();
        }

        public static SensorDataTableManagement SensorDataTableManagent
        {
            get
            {
                if (sensorDataTableManagement == null)
                {
                    var connectionString = RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString");
                    var storageAccount = CloudStorageAccount.Parse(connectionString);
                    sensorDataTableManagement = new SensorDataTableManagement();
                    sensorDataTableManagement.Initialize(storageAccount);
                }
                return sensorDataTableManagement;
            }
        }
    }

OnStart()メソッドは、サービスが起動されたときにコールされるメソッドです。前述のSensorDataTableManagementクラスのインスタンスを作成し初期化します。staticなSensorDataTableManagementプロパティは、RESTでアクセスされてコールされたメソッドの中でTableのコンテキストを取得するために用意しています。

次に、ネットワークからRESTでアクセスするための仕掛けを作っていきます。
SensorServiceプロジェクトにWCFサービスをSensorREST.svcという名前で追加します。

出来上がったSensorREST.svcファイルを”マークアップの表示”で開き、以下のように修正します。

<%@ ServiceHost Language="C#" Debug="true" Factory="System.ServiceModel.Activation.WebServiceHostFactory"  Service="SensorService.SensorREST" %>

次に、ISensorREST.csファイルを開き、ISensorRESTインターフェイスを以下の様に編集します。

    [ServiceContract]
    public interface ISensorREST
    {
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/sensors/")]
        List<string> GetSensors();

        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/sensors/{id}/")]
        List<SensorData> GetSensorData(string id);

        [OperationContract]
        [WebInvoke(RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, Method = "PUT", UriTemplate = "/sensor/{id}/")]
        SensorData AddSensorData(string id, SensorData data);
    }

インターフェイスの定義に出てくる[…]で囲まれたServiceContractやOperationContract、WebGetやWebInvokeの詳細は、http://msdn.microsoft.com で調べてください。以上の定義の結果、クラウドサービスのURLをhttp://sensorcloud とすると、ネットで

http://sensorcloud/sensors  にアクセスされると GetSensors()メソッドがコールされ、XMLで蓄積されたセンサーIDのリストを返す

http://sensorcloud/sensors/sensor-id  にアクセスされるとGetSensorData()がコールされ、指定のセンサーの計測値を返す

といったように、Web上のREST APIとのマッピングが定義されます。

次に、SensorService.svcを”コードの表示”で開きます。エディター上で、

public class SensorREST : ISensorREST

のISensorRESTを右クリックして”インターフェイスの実装”を選択します。すると、ISensorRESTで定義したメソッドの実装用メソッドが自動生成されます。それぞれのメソッドのロジックは以下の様にコーディングしてください。

GetSensors()メソッド:

        public List<string> GetSensors()
        {
            var sensors = new List<string>();
            try
            {
                var tableMgmg = WebRole.SensorDataTableManagent;
                var context = tableMgmg.GetTableContext();
                var query = context.CreateQuery<SensorDataTable>(SensorDataTableManagement.TableName);
                foreach (var item in query)
                {
                    sensors.Add(item.SensorId);
                }
            }
            catch (Exception ex)
            {
                var message = ex.Message;
            }
            return sensors;
        }

GetSensorData()メソッド:

        public List<SensorData> GetSensorData(string id)
        {
            var tableMgmt = WebRole.SensorDataTableManagent;
            var context = tableMgmt.GetTableContext();
            var query = context.CreateQuery<SensorDataTable>(SensorDataTableManagement.TableName).Where((i) => (i.SensorId == id));
            var sensorData = new List<SensorData>();
            foreach (var item in query)
            {
                sensorData.Add(new SensorData()
                {
                    SensorId = item.SensorId,
                    SensorType = item.SensorType,
                    MeasuredTime = item.MeasuredTime,
                    MeasuredValue = item.MeasuredValue
                });
            }
            return sensorData;
        }

AddSensorData()メソッド:

        public SensorData AddSensorData(string id, SensorData data)
        {
            var tableMgmt = WebRole.SensorDataTableManagent;
            var context = tableMgmt.GetTableContext();
            var sensorData = new SensorDataTable();
            sensorData.SensorId = data.SensorId;
            sensorData.SensorType = data.SensorType;
            sensorData.MeasuredTime = data.MeasuredTime;
            sensorData.MeasuredValue = data.MeasuredValue;
            context.AddObject(SensorDataTableManagement.TableName, sensorData);
            context.SaveChanges();
            return data;
        }

 以上で、コーディングは終了です。

次に、PC上のエミュレーターで動作するための、必要な設定をしていきます。まずは、SensorCloudプロジェクトの図に示すアイテムを右クリックしてプロパティを表示してください。

 

設定タブをクリックし、”設定の追加”をクリックし、図のように設定、名前、値の定義を追加します。

 

  • 設定:StorageConnectionString
  • 種類:接続文字列
  • 値:UseDevelopmentStorage=true

次に、SensorServiceプロジェクトのWeb.configを開き、一番最後に太字で示した定義を追加してください。

  </system.webServer>
  <connectionStrings>
    <add name="StorageConnectionString"
         connectionString="DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" />
  </connectionStrings>
</configuration>

定義の中のAccountNameとAccountKeyは、PC上でストレージサービスをエミュレートする時のアカウント情報です。これが無いとエミュレータ上でテーブルにデータを蓄積することはできないのでご注意。

以上で、クラウド側の作業は終了です。F5キーなどでデバッグ実行すると、エミュレータが起動しPC上でクラウドサービスが開始され、同一PC内からサービスにアクセスできるようになります。SensorCloudのPC内のエンドポイントを表示するInternet Explorerが起動されRESTアクセスを試せます。SensorREST.csのGetSensors()メソッドにブレークポイントを設定し、以下の図の様に表示されたURLの後ろに、SensorREST.svc/sensors を追加してアクセスしてみてください。先ほど説明したように、URLのパターンとメソッドが対応付けられているのがわかるでしょう。

※ 図は既にデータが蓄積された後の状態です。最初はもちろんデータは入っていないので、空のリストが表示されます。
あ、実行するにはVisual Studioを管理者権限で起動していないといけません。Visual Studioを管理者権限で起動しなおしてお試しください。

では次に、センサーデバイス側の説明に入ります。

センサーデバイス側の.NET Micro Framework

 センサーデバイスのエミュレーションとして.NET Micro Frameworkのアプリケーションを開発します。.NET Micro Framework開発を行うには、.NET Micro Framework SDKのインストールが必要です。私のブログ、http://blogs.msdn.com/b/hirosho/archive/2013/07/16/howtoinstallnetmfandgadgeteerdevenvinstallationonthistimew.aspx を参考にしてくださいね。

さて、準備が出来たら、.NET Micro FrameworkのWindows Applicationプロジェクトテンプレートを使ってプロジェクトを作成してください。

 

.NET Micro Frameworkエミュレータには実センサーはついていないので、乱数などを利用してそれっぽい値を発生させ、それをAzureのSensorREST.svcにアップロードすることにします。アップロードのトリガーは、エミュレータのボタンを押下とします。

プロジェクトには、System.HttpとMicrosoft.SPOT.Netの2つのアセンブリーを参照に追加してください。

出来上がったプロジェクトのProgram.csファイルに修正を加えます。

先ずは、Programクラスに

        private const string sensorId = "E2A3DC66-E50D-4C19-B340-B563301BCBEB";
        private const string sensorType = "79C02FC9-DA98-40FB-A98F-70236CB0F60B";

と、クラウドサービスに送るセンサーIDとセンサー種別を保持するメンバー変数を追加します。※ここではUUIDを使っていますが、文字列なので何でもいいです。

OnButtonUpメソッドに以下のロジックを追加します。

            var data = GetSensorData();
            string serviceUrl = "http://127.0.0.1:81/SensorREST.svc/sensor/" + sensorId + "/";

            var request = HttpWebRequest.Create(serviceUrl) as HttpWebRequest;
            request.Method = "PUT";

            var now = DateTime.Now;
            string mt = now.ToString("yyyy-MM-dd") + "T" + now.ToString("hh:mm:ss") + "." + now.Millisecond + "Z";

            string sendData = "<?xml version=\"1.0\"?>";
            sendData += "<SensorData xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/EnvSensorService\">";
            sendData += "<SensorId>" + sensorId + "</SensorId>";
            sendData += "<SensorType>" + sensorType + "</SensorType>";
            sendData += "<MeasuredTime>" + mt + "</MeasuredTime>";
            sendData += "<MeasuredValue>" + data.ToString() + "</MeasuredValue>";
            sendData += "</SensorData>";
            request.ContentType = "text/xml";
            var buf = System.Text.UTF8Encoding.UTF8.GetBytes(sendData);
            request.ContentLength = buf.Length;

            using (var reqstream = request.GetRequestStream())
            {
                reqstream.Write(buf, 0, buf.Length);
                reqstream.Close();
            }
            var response = request.GetResponse() as HttpWebResponse;
            if (response.StatusCode == HttpStatusCode.OK)
            {
                using (var resStream = response.GetResponseStream())
                {
                    StreamReader reader = new StreamReader(resStream);
                    var receiveData= reader.ReadToEnd();
                }
                Debug.Print("Succeeded");
            }

冒頭のGetSensorData()は、それっぽいdoubleの計測値を生成するメソッドです。適当に用意してくださいね。簡単に固定値を返すものでも構いません。ざっと眺めると、.NET FrameworkでWebアクセスプログラミングの経験がある人は当然として、何となく何をやっているか雰囲気わかりますよね。http://127.0.0.1:81/sensors/E2A3DC66-E50D-4C19-B340-B563301BCBEB/ に対し、PUTメソッドで、センサーID、センサー種別、計測データ、計測時間を基に組み立てたXMLデータを、送信する、という簡単なコードです。このアクセスにより、Azure側では、AddSensorData()メソッドがコールされ、送られてきたデータをテーブルに格納します。

以上終了。F5実行でエミュレータを起動し、どのボタンでもいいので押してみてください。

ここで紹介したコードは、FEZシリーズなどの実機でも利用可能なコードです。簡単のため認証や暗号化などは入れていませんが、センサーデバイスの場合は、データの改ざん、端末のなりすましあたりを排除する必要があるはずです。.NET Micro Frameworkの場合、暗号化用のHASHアルゴリズムやHTTPS通信用のライブラリーも用意されているので、それらを使えばそれほど難しくなく実装できるはずです。この辺りのトピックスについては、別の機会で紹介しようと思っています。

.NET Micro Frameworkのエミュレーターで動作が確認できれば、次は実機でやりたくなるのが人情ですね。しかし、そうなると、PCの外からのAzureエミュレータへのアクセスになり、PCのFirewallや各種設定を無効にするなどかなり面倒くさいです。ここまで来たら図に示すように、SensorCloudプロジェクトを本番のWindows Azure上での稼働用に設定を変えて、Windows Azure上にデプロイして、.NET Micro Framework エミュレーター+クラウドで動作を確認、

その後、実機にエミュレータ上で確認したロジックを組み込む(ライブラリ化しておくとGood)、という流れでのチェックが良いでしょう。

クラウドに蓄積されたデータは、REST形式で簡単にアクセスできます(勿論本番は何らかの認証など必要ですが)。Windows ストアアプリなどで蓄積されたセンサー計測値を活用する場合、通常のWebサービスへのアクセス方法で実現が可能です。

以上、長々と説明してきましたが、いかがでしょう?それほど複雑なものではないので、是非試してみてください。

 

Comments (0)

Skip to main content