[Blog翻訳] アプリケーションでセンサーを使用する – マネージ (その 1)

みなさん、こんにちは。Windows 開発統括部の古内です。

さて、センサー シリーズの 3 回目、トリを務めますのは Developing for Windows Blog に 2010 年 2 月 3 日に投稿された 「Using Sensors in You Application – Managed Part 1」 の翻訳です。 昨日はネイティブ API の紹介でしたが、これはマネージ コードについての説明です。

ただし、昨日のネイティブ実装の記事と同様に、「その 1」 と銘打ってはいるものの 「Part 2」 はいまだ Developing for Windows Blog には投稿されていないので、センサー データの読み取りに関してはまだ触れられていません。


アプリケーションでセンサーを使用する – マネージ (その 1)

前回までの投稿では、「センサーで感知する – Windows 7 Sensor プラットフォームの使用」でWindows 7 Sensor and Location プラットフォームのアーキテクチャを説明し、その後「アプリケーションでセンサーを使用する – ネイティブ実装 (その 1)」で実際の使用について説明してきました。この投稿では、センサーを使用するためのマネージ API を紹介します。ネイティブ API の説明は、また今後の投稿で続ける予定です。

Sensor and Location プラットフォームの C++ および COM の例は既に見てきましたが、ここでは、マネージ コード開発者が、Windows API Code Pack for .NET Framework を使用して、このプラットフォームでセンサーを検出して操作する方法について説明します。

マネージ コードを使用したセンサーの検出

マネージ コードでも、ネイティブ コードの場合と同様のガイドラインに従います。まず最初にセンサーを検出し、次にセンサーの状態を確認し、必要に応じてアクセス許可を要求した後、センサーからデータを読み取ります。それでは、センサーの検出から始めましょう。

Windows API Code Pack におけるセンサーのメインの名前空間は Microsoft.WindowsAPICodePack.Sensors であり、Microsoft.WindowsAPICodePack.Sensors.dll アセンブリとして実装されています。この名前空間には、PC に接続されているセンサー デバイスを管理する SensorManager クラスが含まれています。このクラスは、ネイティブの ISensorManager インターフェイスのメソッドと同様のメソッドのセットを提供します。たとえば、GetSensorsByCategoryIdGetSensorsByTypeIdGetSensorsBySensorId などのメソッドがあります。3 番目の GetSensorsBySensorId は入力パラメーターとして、センサーのカテゴリ、種類、または単一センサー ID のいずれかを表す GUID を受け取ります。さらに、GetAllSensors というメソッドもあります。このメソッドは、次のコード スニペットに示されているように、種類やカテゴリに関係なくシステムに接続されているすべてのセンサーを返します。

 private void PrintAllSensors()
 {
     SensorList<Sensor> sensorList =  SensorManager.GetAllSensors();
     foreach (var sensor in sensorList)
     {
         StringBuilder sb = new  StringBuilder();
         sb.Append("Sensor Information:");
         sb.Append(Environment.NewLine);
         sb.Append(sensor.FriendlyName);
         sb.Append(Environment.NewLine);
         sb.Append(sensor.CategoryId);
         sb.Append(Environment.NewLine);
         sb.Append(sensor.State);
         sb.Append(Environment.NewLine);
  
         Console.WriteLine(sb.ToString());
     }
 }

このコード スニペットを私の開発用ローカル コンピューターで実行すると、次のような出力が生成され、ローカル コンピューターにインストールされているセンサーが表示されます。仮想ライトのみが Ready 状態であり、残りは AccessDenied と表示され、有効になっていないことが示されています。

 Sensor Information:
 Legacy GPS Driver
 bfa794e4-f964-4fdb-90f6-51056bfe4b44
 AccessDenied
  
 Sensor Information:
 Skyhook Wireless XPS Location Sensor
 bfa794e4-f964-4fdb-90f6-51056bfe4b44
 AccessDenied
  
 Sensor Information:
 Ambient Light Sensor
 17a665c0-9063-4216-b202-5c7a255e18ce
 Ready

 

Windows API Code Pack には、厳密に型指定されたセンサー クラス Sensor が含まれているので、簡単にセンサーの一覧を取得し、センサーのさまざまなプロパティを出力できます。ネイティブ API には Sensor インターフェイスがあり、このインターフェイスによってセンサーを操作しますが、ISensor インターフェイス以外では GUID を使用する必要があります。Windows API Code Pack には、Sensors.h にあるすべての GUID の一覧が用意されています。SensorPropertyKeys クラスと SensorCategories クラスには、Sensors.h ファイル内の同じ値に対応する GUID オブジェクトの読み取り専用パブリック プロパティが含まれています。ただし、これは .NET 開発者が慣れ親しんでよく使ったり好んで使ったりするプログラミング モデルではありません。このようになっているのは、ネイティブ センサー オブジェクトが厳密に型指定されておらず、より汎用的な GUID システムを使用してセンサーのデータにアクセスする必要があるからです。この場合、データ バインド、タイプ セーフ、プロパティなどの .NET が提供する優れた機能をすべて使用できるわけではありません。右の図に示されている Microsoft.WindowsAPICodePack.Sensors 名前空間には、プロパティにバインドできる厳密に型指定されたセンサー クラスがいくつか含まれています。たとえば、AmbientLightSensor には、パブリック プロパティ CurrentLuminousIntensity があり、これはセンサーによって検出された現在の光量 (明るさ) を表します。この名前空間には、ネイティブ インターフェイスをラップする相互運用層、すべてのメタデータ情報、および開発者が操作するオブジェクト モデルも含まれています。

Codepack_Sensor

Microsoft.WindowsAPICodePack.Sensors 名前空間では拡張機能モデルが提供されているので、厳密に型指定されたセンサーを各自で作成することができます。これをネイティブ API が提供する拡張機能と併用すれば、さまざまな種類のセンサーを作成してさまざまなデータ値を扱うことが可能になります。Sensor and Location プラットフォームの拡張機能モジュールの詳細については、Sensor and Location プラットフォームの Web サイト (https://msdn.microsoft.com/ja-jp/windows/hardware/gg463473) を参照してください。

厳密に型指定されたセンサー クラスによって、Windows API Code Pack では .NET ジェネリック バージョンの Get メソッドを定義できます。たとえば、GetSensorsByTypeId<S> のようになります。ここで、SSensor 基本クラスから派生した型です。プロトタイプは次のようになります。

public static SensorList<S> GetSensorsByTypeId<S>( ) where S: Sensor

この関数を使用する場合、目的のセンサーの種類 (この例では AmbientLightSensor) の特定の SensorList<> を事前に定義し、AmbientLightSensor センサーのみを返すようにセンサーのマネージャーに要求するメソッドを呼び出す必要があります。次のコード スニペットに、このプロセスを示します。

 // AmbientLightSensor 型の厳密に型指定された SensorList
 SensorList<AmbientLightSensor> alsList = null;
 try
 {
     alsList = SensorManager.GetSensorsByTypeId<AmbientLightSensor>();
 }
 catch (SensorPlatformException) 
 {
     // どのセンサー デバイスにもアクセスできない場合にエラーを処理
 }

SensorManager クラスには、SensorChanged と呼ばれるイベントが 1 つ含まれています。このイベントはネイティブの ISensorManager::OnSensorEnter イベントに相当します。ネイティブ コードとマネージ コードの実装で大きく異なる点の 1 つは、マネージ コードの実装では、新しいセンサー デバイスが PC に接続されたときにイベントを受信することに加えて、センサーが接続解除されたときにイベントを生成することもできる点です。したがって、SensorsChangedEventArgs (SensorManager.SensorChanged イベント ハンドラーに渡される引数) には、各センサーの状態の変化の種類を定義する SensorAvailabilityChange メンバーが含まれており、新しいセンサー デバイスが追加されたときには Addition、センサーが PC から取り外されたときには Removal になります。

SensorManager _SensorsChanged は、このアプリケーションで SensorsChanged イベントを処理する関数であり、次のようになります。

 void SensorManager_SensorsChanged( SensorsChangedEventArgs change )
 {
     // SensorsChanged イベントが UI 以外のスレッドで発生
     // UI の更新を処理するために匿名デリゲートを作成
     BeginInvoke( new MethodInvoker( delegate
     {
         PopulatePanel( );
     } ) );
 }

SensorsChanged イベントは、アプリケーションのメイン フォーム (UI) スレッド以外のスレッドからディスパッチされます。Windows フォームでは UI 以外のスレッドからアプリケーションの UI を更新することは許可されていないので、ウィンドウ ベースの UI を持つ Windows アプリケーションでは、(ここで説明したように同期的にセンサー データを読み取る方法で) 長時間の計算や I/O に拘束される通信を実行する場合、メインの UI 以外のスレッドを使用することが強く推奨されています。これを行うには、UI 以外のスレッドによる UI の更新を正しく処理できるように、フォームの基になっているハンドルが作成されたスレッドで BeginInvoke を使用して指定のデリゲートを非同期的に実行する必要があります。これは WPF アプリケーションにも当てはまります。PopulatePanel メソッドはすべての環境光センサーを反復処理し、センサーの状態を検証し、センサーのデータを読み取って、アプリケーションの UI を更新します。これについては今後の投稿で説明します。