Использование сенсоров в приложении: реализация в неуправляемом коде (часть 1)

После поверхностного обзора платформы Sensor and Location пришло время углубиться в API. Мы начнем с описания Win32 API для понимания потока данных, а затем рассмотрим управляемые API через Windows API Code Pack для .NET Framework.

Платформа Sensor and Location состоит из двух компонентов: сенсорного и компонента, созданного поверх него и отвечающего за местоположение. Для начала мы подробно рассмотрим сенсорную часть, чтобы к тому моменту, когда пойдет речь о местоположении, понимать, на чем она основана.

Sensor API (равно как и Location API) базируется на COM. Все необходимые API и определения GUID можно найти в файлах Sensorsapi.h и Sensors.h из Windows 7 SDK. Файл Sensors.h содержит массу GUID, которые определяют различные категории сенсоров, типов и данных. API и интерфейсы находятся в файле Sensorsapi.h. Более пристальный взгляд на файл Sensorsapi.h открывает три основных COM-интерфейса, о которых вам необходимо знать, работая с сенсорами:

  • ISensorManager является основным интерфейсом для всех сенсоров, подключенных к платформе. Менеджер позволяет получать список сенсоров, запрашивать разрешения для доступа к их данным и получать уведомления о событиях при обнаружении нового сенсора.
  • ISensor представляет собой текущий объект сенсора, с которым вы собираетесь работать. Через этот интерфейс можно задавать и получать свойства объекта, получать отчеты и регистрировать события, которые произошли во время работы сенсора.
  • ISensorDataReport – единственный интерфейс, который вы будете любить и ненавидеть, поскольку это единственный способ получения актуальных данных от сенсора, однако это обобщенная форма обобщенного отчета данных, которые необходимо охватить.

Чтобы интегрировать сенсоры в приложение, придется работать с этими тремя интерфейсами.

Интеграция сенсоров в приложение

Для интеграции сенсоров в приложение, вам необходимо выполнить следующие шаги:

1. Обнаружение сенсора – прежде, чем вы сможете обратиться к сенсору, необходимо обнаружить, какие сенсоры подключены к системе. Используйте интерфейс ISensorManager для получения списка сенсорных устройств, подключенных к платформе, и регистрации событий о подключении нового сенсора.

2. Получение сенсора и прав на него – как только вы обнаружили и выбрали сенсор, с которым желаете работать, необходимо убедиться, что вы имеете полномочия для доступа к нему. Если вы не имеете прав для доступа к данным сенсора (использовать его), то должны получить соответствующие права, чтобы приложение имело доступ к данным сенсора.

3. Взаимодействие с сенсором – после получения доступа к данным сенсора, можно начать работу с ним, читая и устанавливая свойства, а также регистрируя события.

Первый шаг, который необходимо сделать для каждого приложения, в котором требуется использовать сенсоры, – это обнаружить, какие сенсоры подключены к платформе, а затем получить доступ к одному из них. Чтобы разрешить приложению сделать это, вы должны использовать COM-интерфейс ISensorManager. Менедже

Обнаружение сенсоров

р сенсора содержит список доступных датчиков. Так как это COM-интерфейс, вам необходимо его инициализировать с помощью CoCreateInstance. В принципе, этот интерфейс можно рассматривать как корневой интерфейс для API сенсора. Следующий фрагмент кода создает экземпляр менеджера сенсора:

 // Объявляем указатель для получения указателя менеджера сенсора.
 // Вообще данный указатель определяется в определении класса прототипов.
 // CAmbientLightAwareSensorManagerEvents
 CComPtr<ISensorManager> m_spISensorManager;
 HRESULT hr;
 // Создаем менеджер сенсора
 hr = m_spISensorManager.CoCreateInstance(CLSID_SensorManager);

Интерфейс ISensorManager обеспечивает набор методов для обнаружения и получения доступных сенсоров. Он также выдает событие, используемое для получения уведомлений о доступности новых сенсоров. Вы можете спросить себя, как регистрировать событие, когда сенсор «покидает» систему. Ответ на этот вопрос можно отыскать в самом сенсоре. С указателем ISensorManager под рукой можно начать поиск сенсоров. У ISensorManager есть три метода, которые помогут найти подключенные датчики:

  • GetSensorsByID – первый метод возвращает определенный сенсор по его идентификатору (ID), который представляет собой обычный GUID. Это не самая используемая функция, поскольку GUID сенсора генерируется автоматически при первоначальном подключении к системе для обеспечения поддержки нескольких сенсоров одного и того же производителя и одинаковой модели.
  • GetSensorsByCategory – эта функция возвращает набор сенсоров, совместно использующих одну категорию. Например, может быть несколько сенсоров местоположения, включая GPS и триангуляцию Wi-Fi, включенных в платформу, и вы не хотите отказываться от их использования.
  • GetSensorsByType – эта функция возвращает набор сенсоров одного и того же типа.

Как можно заметить, имена функций показывают, что к компьютеру одновременно подключено множество сенсоров. Технически возможно, чтобы несколько сенсоров одной и той же категории были подключены к одному и тому же ПК. Например, на моем компьютере установлен один датчик освещения, установленный в корпус ПК, и еще один из состава Sensor Development Kit. Имеет смысл использовать световой датчик, встроенный в корпус, так как он обеспечивает самое точное определение условий освещения. Для приложения наилучшим способом обнаружения сенсоров является использование типа интерфейса и категорий функции Sensor Manager с последующим выбором из списка нужного сенсора.

В этом цикле статей мы будем использовать очень простое приложение Microsoft Foundation Classes (MFC), являющееся частью Windows 7 SDK. Следующий код взят из метода Initialize класса CAmbientLightAwareSensorManagerEvents. В этом классе реализованы уведомления ISensorManagerEvents при обнаружении нового датчика.

Метод Initialize вызывают во время процесса инициализации главного диалога (у нас есть только один):

 HRESULT CAmbientLightAwareSensorManagerEvents::Initialize()
 {
     HRESULT hr;
     // Создаем менеджер сенсора
     hr = m_spISensorManager.CoCreateInstance(CLSID_SensorManager);
     if (SUCCEEDED(hr))
     {
         hr = m_spISensorManager->SetEventSink(this);
         if (SUCCEEDED(hr))
         {
             // Ищем сенсоры внешнего освещения
             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)
                                      {
                                         // Читаем данные с сенсора и обновляем 
                                         // интерфейс приложения
                                         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 предназначенного для перехвата событий, но об этом я расскажу позже. Наконец, вы вызываете другой вспомогательный метод, GetSensorData, чтобы считать данные с сенсора и обновить значение освещенности в интерфейсе приложения. Мы расскажем о чтении данных сенсора в следующих статьях.

В течение всего времени выполнения приложения к компьютеру могут быть подключены новые сенсоры, таким образом, требуется механизм уведомления приложения при подключении новых сенсоров. Интерфейс ISensorManager содержит метод SetEventSink для задания класса реализующего обработчик событий, то есть делегат, который обрабатывает события. Эта функция получает интерфейс обратного вызова ISensorManagerEvents, получающий уведомления о подключении новое сенсора. Это событие выступает в качестве приемника, обрабатывающего OnSensorEnter – единственное событие, которое есть у интерфейса ISensorManagerEvents.

Если вы посмотрите на вышеприведенный пример, то увидите, что SetEventSink вызывается после успешного создания интерфейса ISensorManager и передает его в качестве входного параметра, а затем локальный класс 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, считывается тип сенсора с помощью метода GetType интерфейса ISensor. Поскольку мы работаем со светочувствительным приложением и интересуемся только световыми сенсорами, мы проверим, является ли недавно подключенный сенсор световым, определив его тип. Помните, что ISensorManager получает уведомление о любом типе сенсора, подключенного к ПК. Если сенсор является датчиком освещенности, тогда вы вызываете ту же вспомогательную функцию AddSensor, которую использовали в методе Initialize, устанавливая перехватчик для специфичных сенсоров. Не перепутайте с перехватчиком для ISensorManager. Последнее, что нужно сделать, – проверить, находится ли сенсор в состоянии готовности. Если да, вы просто читаете данные с сенсора и обновляете интерфейс приложения.

Но чтение данных с сенсора мы оставим для следующих статей.

За дополнительными ресурсами по платформе Windows 7 Sensor and Location обращайтесь к учебному центру на Channel 9.