[Blog翻訳] アプリケーションでセンサーを使用する - ネイティブ実装 (その 1)
みなさん、こんにちは。Windows 開発統括部の古内です。
昨日に引き続き、2010 年 2 月 2 日に Developing for Windows Blog へ投稿された 「Using Sensors in Your Application – Native Implementation, Part 1」 の翻訳をお届けします。昨日は Sensor プラットフォームの概要についてでしたが、今日の記事は Sensor API に関するより具体的な内容で、ソース コードのサンプルもあります。(なので、長いです...)
ちなみに 「その 1」 となっていますが、「Part 2」 はまだ英語 Blog サイトにも投稿されていません。(なので、「その 2」 があるかはわかりません...)
アプリケーションでセンサーを使用する – ネイティブ実装 (その 1)
「センサーで感知する – Windows 7 Sensor プラットフォームの使用」では Sensor and Location プラットフォームの概要を説明しました。今回は、API について詳しく見ていきましょう。データの流れを理解するために、まずは Win32 API の概要から説明します。その後、Windows API Code Pack for .NET Framework (英語) を利用したマネージ API について説明します。
Sensor and Location プラットフォームには、センサーに関する部分と、その上に構築されたロケーション (位置情報) に関する部分の 2 つのコンポーネントがあります。ここではセンサーの部分について詳しく説明します。この基本的な部分が理解できていれば、位置情報に関する部分についても理解が簡単になります。
Sensor API (および Location API) は COM ベースです。必要な API および GUID の定義はすべて、ローカルにインストールされている Windows 7 SDK の Sensorsapi.h ファイルと Sensors.h ファイルに含まれています。Sensors.h ファイルには、センサーのカテゴリ、種類、およびデータをあらゆる側面から定義する多数の GUID が含まれています。API とインターフェイスは Sensorsapi.h ファイルに含まれています。Sensorsapi.h ファイルを注意して見ると、次の 3 つの主要な COM インターフェイスが含まれていることがわかります。センサーを操作するときはこれらを意識する必要があります。
- ISensorManager は、プラットフォームに接続されるすべてのセンサーに対するメインのインターフェイスです。このセンサー マネージャーによって、センサーのリストを列挙したり、センサー データにアクセスするためのアクセス許可をユーザーに要求したり、新しいセンサーが使用可能になったときにイベントを受け取ったりすることができます。
- ISensor は、操作の対象となる実際のセンサー オブジェクトです。このインターフェイスを通じて、プロパティの設定や取得、データ レポートの取得、センサーが有効になっている期間中に発生するさまざまなイベントの登録を行うことができます。
- ISensorDataReport は、好き嫌いにかかわらず理解しておかなければならないでしょう。これは、センサーから実際のデータを読み取る唯一の手段となるインターフェイスです。汎用形式の汎用データ レポートとしてさまざまなデータに対応します。
センサーをアプリケーションに組み込むには、これら 3 つのインターフェイスを扱う必要があります。
アプリケーションへのセンサーの組み込み
アプリケーションにセンサーを組み込むときは、基本的には次の 3 つの手順を行います。
- センサーの検出 – センサーを取得するには、まず、システムに接続されているセンサーを検出する必要があります。ISensorManager インターフェイスを使用すれば、システムに接続されているセンサー デバイスの一覧を列挙したり、新しいセンサー デバイスが接続されたときに通知を行うイベントを登録したりすることができます。
- センサーの取得とセンサーのアクセス許可の要求 – センサーを検出し、操作対象のセンサーを決定したら、センサーにアクセスするためのアクセス許可があることを確認します。センサーのデータにアクセスする (使用する) ための許可がない場合は、アプリケーションからセンサーのデータにアクセスできるように、ユーザーにアクセス許可を要求します。
- センサーとのやりとり – センサーを取得し、そのデータに対するアクセスが許可されたら、センサーとの対話、プロパティの読み取りと設定、およびイベントの登録を開始できます。
センサーの検出
センサーを使用する各アプリケーションで最初に行う必要がある手順は、プラットフォームに接続されているセンサーを検出し、センサーへのアクセスを確立することです。アプリケーションでこれを行うには、ISensorManagerCOM インターフェイスを使用します。センサー マネージャーは、使用可能なセンサーの一覧を管理します。センサー マネージャーは COM インターフェイスであるため、CoCreateInstance を使用して初期化する必要があります。このインターフェイスは Sensor API のルート インターフェイスと考えることができます。次のコード スニペットは、センサー マネージャーのインスタンスを作成します。
// センサー マネージャー インターフェイスのポインターを受け取るスマート ポインターの宣言
// 元々、このスマート ポインターは、クラス プロトタイプ定義の
// CAmbientLightAwareSensorManagerEvents で定義されています
CComPtr<ISensorManager> m_spISensorManager;
HRESULT hr;
// センサー マネージャーの作成
hr = m_spISensorManager.CoCreateInstance(CLSID_SensorManager);
ISensorManager インターフェイスは、使用可能なセンサーを検出および取得するメソッドを提供します。また、新しいセンサーが使用可能になったときの通知の受信に使用するイベントも提供します。センサーがシステムから 「取り外された」 場合のイベントをどこに登録しておくべきか迷われるかもしれません。これはセンサー自体でわかります。ISensorManager ポインターがあれば、センサーの検索を開始できます。ISensorManager には、接続されているセンサーの検索に役立つ 3 つのメソッドがあります。
- GetSensorsByID – 最初のこのメソッドは、センサーの ID (GUID) によって特定のセンサーを返します。これが最も便利な関数というわけではありません。センサー GUID は、同じメーカーおよびモデルの複数のセンサーをサポートするために、センサーが最初にシステムに接続されたときに自動的に生成されます。
- GetSensorsByCategory – この関数は、同じカテゴリに属するセンサーのコレクションを返します。たとえば、GPS および Wi-Fi 三角測量など、複数の位置センサーがプラットフォームに接続されていて、どのセンサーも除外しない場合に使用できます。
- GetSensorsByType – この関数は、同じ種類に属するセンサーのコレクションを返します。
お気づきのように、すべての関数の名前は、複数のセンサーが同時にコンピューターに接続されていることを前提としています。同じカテゴリの複数のセンサーが 1 台の PC に接続されることも当然あります。たとえば、私のコンピューターには、PC の筐体に付属の光センサーと、Sensor Development Kit の一部としての光センサーがあります。PC から読み取った最も正確な照明の状態を示すのは筐体の光センサーなので、通常はこちらのセンサーが使用されなければなりません。アプリケーションでは、センサー マネージャー インターフェイスの種類またはカテゴリの関数を使用して各センサーを検出し、その一覧から操作したいセンサーを選択するという方法がベストです。
このシリーズ記事では、Windows 7 SDK に含まれている、非常にシンプルな Microsoft Foundation Classes (MFC) の光認識アプリケーションを使用します。次のコードは、CAmbientLightAwareSensorManagerEventsクラスの Initializeメソッドのコードです。このクラスは、新しいセンサーが使用可能になったときにプラットフォームから行われる ISensorManagerEvents インターフェイス通知を実装します。
Initialize メソッドは、メイン ダイアログの初期化プロセスの中で呼び出されます (1 つのみ)。
HRESULT CAmbientLightAwareSensorManagerEvents::Initialize()
{
HRESULT hr;
// センサー マネージャーの作成
hr = m_spISensorManager.CoCreateInstance(CLSID_SensorManager);
if (SUCCEEDED(hr))
{
hr = m_spISensorManager->SetEventSink(this);
if (SUCCEEDED(hr))
{
// Ambient Light Sensor (環境光センサー) をすべて検索
CComPtr<ISensorCollection> spSensors;
hr = m_spISensorManager->GetSensorsByType
(SENSOR_TYPE_AMBIENT_LIGHT, &spSensors);
if (SUCCEEDED(hr) && NULL != spSensors)
{
ULONG ulCount = 0;
hr = spSensors->GetCount(&ulCount);
if (SUCCEEDED(hr))
{
for(ULONG i=0; i < ulCount; i++)
{
CComPtr<ISensor> spSensor;
hr = spSensors->GetAt(i, &spSensor);
if (SUCCEEDED(hr))
{
// 特定のセンサーに対してイベント シンクを
// セットアップするヘルパー関数
hr = AddSensor(spSensor);
if (SUCCEEDED(hr))
{
// 現在のセンサーの状態を確認
SensorState state = SENSOR_STATE_READY;
hr = spSensor->GetState(&state);
if(SUCCEEDED(hr))
{
if(state == SENSOR_STATE_READY)
{
// センサーのデータを読み取って、
// アプリケーションの UI を更新
hr = m_pSensorEvents->GetSensorData
(spSensor);
}
}
}
}
}
}
}
}
return hr;
}
このコードでは、ISensorManagerインターフェイスを正常に取得した後、ISensorManager::GetSensorsByType の呼び出しで、SENSOR_TYPE_AMBIENT_LIGHT と、ISensorCollectionコレクションへのポインター spSensors を指定しています。SENSOR_TYPE_AMBIENT_LIGHT は、環境光センサー (ALS) のみを受信することを示します。同時に、電気カテゴリから種類が SENSOR_TYPE_VOLTAGE であるすべてのセンサーを指定したり、運動カテゴリの SENSOR_TYPE_ACCELEROMETER_3D を指定したりすることもできます。
GetSensorsByType 関数が成功すると、ISensorCollection に ALS の一覧が設定されます。次に、このセンサー コレクションを反復処理し、各センサーについて AddSensorヘルパー関数を呼び出します。これにより、センサーを m_pSensorEvents に登録してイベント シンク (デリゲート) が設定されます。これは、後で説明するイベント シンクに使用される SensorEvents実装クラスです。最後に、もう 1 つ別のヘルパー メソッド GetSensorData を呼び出して、センサーからのデータを読み取り、アプリケーションの UI のルクス (LUX) の値を更新します。センサー データの実際の読み取りについては、今後の投稿で説明します。
アプリケーションの実行中に新しい光センサーが PC に接続されることもあるので、新しいセンサー デバイスが接続されたときにアプリケーションに通知を行うためのメカニズムも必要です。ISensorManagerインターフェイスには、イベント シンク実装クラス (つまり、イベントを処理するデリゲート) を設定する SetEventSinkメソッドが含まれています。この関数は、新しいセンサー デバイスが接続されたときにイベント通知を受信する ISensorManagerEventsコールバック インターフェイスを受け取ります。このイベント シンクは、ISensorManagerEvents インターフェイスの唯一のイベントである OnSensorEnter を処理するリスナーとして機能します。
前のサンプルを見ると、ISensorManagerインターフェイスを正常に作成した直後に SetEventSinkを呼び出し、入力パラメーターとして "this" を渡した後、このローカル クラスの CAmbientLightAwareSensorManagerEvents で ISensorManagerEvents インターフェイスを実装していることがわかります。
したがって、ISensorManager::OnSensorEnterイベントの実装はこのクラスに次のようなコードで存在します。
HRESULT CAmbientLightAwareSensorManagerEvents::OnSensorEnter
(ISensor* pSensor, SensorState state)
{
HRESULT hr = S_OK;
if (NULL != pSensor)
{
SENSOR_TYPE_ID idType = GUID_NULL;
hr = pSensor->GetType(&idType);
if (SUCCEEDED(hr))
{
// 光センサーのみを対象
if (IsEqualIID(idType, SENSOR_TYPE_AMBIENT_LIGHT))
{
hr = AddSensor(pSensor);
if (SUCCEEDED(hr))
{
if (SENSOR_STATE_READY == state)
{
hr = m_pSensorEvents->GetSensorData(pSensor);
}
}
}
}
}
else
{
hr = E_POINTER;
}
return hr;
}
このコードで、OnSensorEnterメソッドが、新しく接続されたセンサー デバイスへのポインターと、センサーの状態ステータスを受け取っていることがわかります。センサー ポインターが有効 (null 以外) である場合、まず ISensorインターフェイスの GetTypeメソッドを呼び出してセンサーの種類を読み取ります。ここでは、光認識アプリケーションを使用しており、光センサーにのみ関心があるので、種類を確認することによって新しく接続されたセンサー デバイスが光センサーであるかどうかを確認します。ISensorManagerは、PC に接続されたすべての種類のセンサー デバイスの通知を受信することを忘れないでください。センサーが光センサーであった場合について、Initialize メソッドで使用したものと同じ AddSensor ヘルパー関数を呼び出してそのセンサーのイベント シンクを設定します (ISensorManager のイベント シンクと混同しないでください)。最後に行う処理は、センサーが準備完了状態であるかどうかの確認です。センサーの準備が完了している場合は、センサーのデータを読み取って、アプリケーションの UI を更新します。
センサー データの読み取りについては、また今後の投稿で説明することにします。
Channel 9 Learning Center (英語) にも Windows 7 Sensor and Location に関するトレーニング (英語) が用意されています。