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

Мы рассмотрели архитектуру платформы Sensor and Location в Windows 7 (Я тебя чувствую: использование платформы Sensor в Windows 7 ) и Использование сенсоров в приложении: реализация в неуправляемом коде (ч.1) . В сегодняшней статье я расскажу об управляемом API для использования сенсоров. В следующих публикациях я продолжу рассказ о неуправляемых API.

До сих пор вы видели примеры использования платформы с помощью C++ и COM. Теперь давайте рассмотрим, как разработчики управляемого кода смогут использовать платформу с помощью Windows API Code Pack для .NET Framework.

Обнаружение сенсоров с помощью управляемого кода

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

Основное пространство имен для сенсоров в Windows API Code Pack – это Microsoft.WindowsAPICodePack.Sensors из сборки Microsoft.WindowsAPICodePack.Sensors.dll. Это пространство имен содержит класс SensorManager, управляющий сенсорными устройствами, подключенными к ПК. Этот класс предоставляет набор методов, схожих с методами из неуправляемого интерфейса ISensorManager. Эти методы включают GetSensorsByCategoryId, GetSensorsByTypeId и GetSensorsBySensorId, последний из которых получает в качестве входного параметра 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
 AccessDenied
 bfa794e4-f964-4fdb-90f6-51056bfe4b44
  
 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 присутствует список всех GUID, которые доступны в Sensors.h. Классы SensorPropertyKeys и SensorCategories содержат публичное свойство «только для чтения» для объектов GUID, которые соответствуют тем же самым значениям в файле Sensors.h. Однако, это не самая используемая и предпочтительная модель программирования, к которой привыкли .NET-разработчики, и в основном потому, что неуправляемые объекты сенсора в определены не строго и вы вынуждены использовать более универсальную систему GUID для обращения к данным сенсора. Это не позволяет использовать весь спектр возможностей .NET, например, привязку данных, безопасность типов и свойства. Таким образом, пространство имен Microsoft.WindowsAPICodePack.Sensors, структура которого приведена на изображении ниже, включает несколько строго типизированных классов сенсора, позволяющих осуществлять привязку к их свойствам. Например, AmbientLightSensor, у которого одно публичное свойство, CurrentLuminousIntensity, отображающее текущее количество света (яркость), измеренное сенсором. Пространство имен также включает уровень совместимости, который охватывает неуправляемый интерфейс, всю информацию о метаданных и объектную модель, с которой имеют дело разработчики.

clip_image001[4]

Обратите внимание, что пространство имен Microsoft.WindowsAPICodePack.Sensors предлагает модель расширения, позволяющую создавать любой строго типизированный сенсор. Вместе с возможностями расширения, предлагаемыми неуправляемыми API, вы можете создать любой тип сенсора, какой только захотите, с любыми значениями данных. Дополнительную информацию о модуле расширения платформы Sensor and Location вы можете найти на веб-узле платформы: https://www.microsoft.com/whdc/device/sensors/ .

Со строго типизированным классом сенсора Windows API Code Pack может определить версию .NET Generics методов Get. Например, GetSensorsByTypeId<S>, где S – производный тип от базового класса Sensor. Прототип выглядит следующим образом:

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

При использовании данной функции вы должны предопределить SensorList <> желаемого типа сенсора, (в нашем примере AmbientLightSensor), а затем вызвать метод, запрашивающий у менеджера сенсора возврат сенсоров типа AmbientLightSensor. Следующий фрагмент кода иллюстрирует этот процесс:

 // Строготипизированный SensorList типа AmbientLightSensor
 SensorList<AmbientLightSensor> alsList = null;
 try
 {
     alsList = SensorManager.GetSensorsByTypeId<AmbientLightSensor>();
 }
 catch (SensorPlatformException) 
 {
     // Обработка ошибки, когда отсутствует доступ к сенсорному устройству 
 }

Класс SensorManager содержит одно событие, называемое SensorChanged, эквивалентное неуправляемому событию ISensorManager:: OnSensorEnter. Единственным различием между реализациями на неуправляемом и управляемом коде является то, что реализация на управляемом коде в дополнение к получению события при подключении нового устройства генерирует событие при отключении сенсора. Таким образом, SensorsChangedEventArgs, параметры которого переданы обработчику события SensorManager.SensorChanged, включает SensorAvailabilityChange, определяющий тип изменения для каждого сенсора, который может принимать значение Addition для вновь подключенных сенсоров и Removal при отключении сенсоров от ПК.

SensorManager_SensorsChanged представляет собой функцию, которая обрабатывает событие SensorsChanged в нашем приложении и это выглядит следующим образом:

 void SensorManager_SensorsChanged( SensorsChangedEventArgs change )
 {
     // Событие SensorsChanged происходит в потоке, отличном от потока приложения.
     // Анонимный делегат осуществляет обновление интерфейса приложения.
     BeginInvoke( new MethodInvoker( delegate
     {
         PopulatePanel( );
     } ) );
 }

Событие SensorsChanged обрабатывается в ином потоке, отличном от основного потока приложения (UI). Windows Forms не позволяют обновлять интерфейс приложения из таких потоков. Поэтому для любого Windows-приложения с оконным интерфейсом мы настоятельно рекомендуем использовать поток, отличный от основного потока UI, для выполнения сложных вычислений или любого процесса ввода/вывода (также как в нашем примере синхронного чтения данных сенсора). Для того чтобы правильно осуществлять обработку обновлений для не-UI потоков, следует использовать BeginInvoke, позволяющий выполнить определенный делегат асинхронно в потоке, в котором был создан основной обработчик формы. Это также верно для любого приложения WPF. Метод PopulatePanel выполняет итерации через все сенсоры и обновляет интерфейс приложения по мере проверки состояния сенсора и чтения его данных. Об этом мы расскажем в следующих статьях.