Windows 7 Sensor and Location platform - программирование сенсоров (Часть 2/3)

Недавно я писал о том, что такое Sensor and Location platform в Windows 7 и зачем она нужна. Сейчас мы поговорим о том, каким образом эту платформу можно использовать в своих приложениях.

Для того, чтобы проводить эксперименты не с виртуальными датчиками, а с чем-то более-менее приближенным к реальности, мы будем использовать устройство от Freescale semiconductor, построенное на базе микроконтроллера JMBADGE2008-B. Это устройство представляет собой небольшую плату, на которой также есть несколько датчиков – акселерометр, датчик света и кнопки.

Это устройство разработано специально для демонстрации возможностей Sensor and Location platform в Windows 7. Фактически, каждый может купить его. Таким образом, это устройство хорошо подходит для того, чтобы продемонстировать эту возможность Windows 7.

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

При такой организации задачи взаимодействия с внешними датчиками можно, но тяжело. Для этого каждое приложение должно взаимодействовать с тем API, которое предложит разработчик датчика и программного обеспечения, которое обслуживает этот датчик. Проблема встает особенно остро в случае, если приложение должно использовать множество однотипных датчиков от разных производителей. Как эту проблему предлагает решить Sensor&Location platform?

На уровне операционной системы присутствуют механизмы работы с датчиками. Существует стандартный унифицированный программный интерфейс для работы с датчиками – Sensor API. При этом все взаимодействия с датчиком происходят именно через Sensor API. Важно, что взаимодействия со всеми датчиками происходит в едином стиле. Теперь вам не нужно интегрироваться с native API через p/invoke.

Для того, чтобы работать с Sensor and Location API необходимо загрузить соответствующую библиотеку .NET Interop Sample Library. В ней существуют .NET-обертки для работы с Sensor API. В нем существует несколько классов, при помощи которых можно работать с датчиками.

Класс SensorManager является точкой входа. Через него можно получить информацию о сенсорах, а также поработать с ними. Например, при помощи метода GetSensorBySensorId<> можно получить доступ к интересующему нас датчику. Каждый датчик должен иметь класс-обертку, который наследуется от базового класса Sensor. В .NET Interop Sample Library уже существуют три таких реализации – AmbientLightSensor, Accelerometer3D, UnknownSensor.

Основная идея при работе с датчиками заключается в следующем. При изменении состояния датчика (подключен/отключен/активен/итд) генерируется событие StateChanged. Это событие необходимо для начала или прекращения работы с датчиками. После того, как связь с датчиком налажена, при получении новых данных генерируется событие DataReportChanged. Насколько часто будет генерироваться это событие зависит от реализации датчика и драйвера к нему. При обработке этого события можно считать состояние датчиков и каким-то образом изменить работу приложения. Для этих целей используется метод GetProperty. В параметрах этого метода передается идентификатор свойства, которое нужно считать от датчика. Как правило, детали вызовов этого метода скрываются в классах, которые реализуются для специфичного датчика.

Кроме того, каждый датчик имеет собственный идентификатор (GUID), по которому можно идентифицировать устройство. При реализации класса-обертки для датчика этот ID указывается при помощи атрибута. Таким образом, доступ к датчику можно получить как явно указав идентификатор этого датчика, так и сославшись на этот класс-обертку.

/// <summary>
/// Represents a generic ambient light sensor
/// </summary>
[SensorDescription( "97F115C8-599A-4153-8894-D2D12899918A" )]
public class AmbientLightSensor : Sensor
{

// .....
// .....
// .....

var sensors = SensorManager.GetSensorsByTypeId<AmbientLightSensor>();

Давайте попробуем реализовать несколько примеров работы с датчиками, которые доступны в устройстве от Freescale. Мы будем работать с двуми типами датчиков – акселерометром (позволяет измерять угол наклона устройства) и датчиком света (измеряет уровень освещенности в помещении).

Первое приложение, которое мы реализуем будет отображать уровень освещенности в виде горящей лампочки на форме. Для начала подпишемся на событие изменения состояния в Sensor API. Это необходимо для того, чтобы приложение начало работать, если датчик подключен на ходу. В обработчике этого события мы получим список всех датчиков нужного типа и подпишемся у них на событие DataReportChanged. В обработчике этого события мы будем считывать значение с датчика освещенности и записывать его в TextBox на форме. Т.к. событие генерируется в дополнительном потоке, также потребуется сделать вызов метода Dispatcher.Invoke, чтобы обработка шла в основном потоке и мы могли взаимодействовать с элементами на форме. Таким образом получим следующий код.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    SensorManager.SensorsChanged += SensorManagerSensorsChanged;
}

void SensorManagerSensorsChanged(SensorsChangedEventArgs change)
{
    Dispatcher.Invoke((System.Threading.ThreadStart)(UpdateSensorsList));
}

private void UpdateSensorsList()
{
    var sensors = SensorManager.GetSensorsByTypeId<AmbientLightSensor>();
    foreach (var sensor in sensors)
        sensor.DataReportChanged += delegate(Sensor sender, EventArgs e)
        {
            Dispatcher.Invoke((System.Threading.ThreadStart)(delegate
            {
                if (ActiveSensorsListBox.SelectedItem == sender)
                {
                    CurrentValue.Text =
                        ((AmbientLightSensor)sender).CurrentLuminousIntensity.Intensity.ToString();
                }
            }));
        };
}

Теперь в TextBox на форме выводится текущее значение освещенности. Теперь нетрудно реализовать какую-то визуализацию для этого. При помощи привязок в WPF будем отображать степень освещенности в виде лампочек. В итоге получим следующее приложение.

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

private void UpdateSensorsList()
{
    foreach (var sensor in SensorManager.GetSensorsByTypeId<Accelerometer3D>())
    {
        sensor.DataReportChanged += delegate(Sensor sender, EventArgs e)
        {
            Dispatcher.Invoke((System.Threading.ThreadStart)(delegate
            {
                    if (UseXCoordinate.IsChecked == true)
                        CurrentXValue.Text = ((Accelerometer3D)sender).CurrentAcceleration[Accelerometer3D.AccelerationAxis.X].ToString();
                    if (UseYCoordinate.IsChecked == true)
                        CurrentYValue.Text = ((Accelerometer3D)sender).CurrentAcceleration[Accelerometer3D.AccelerationAxis.Y].ToString();
                    if (UseZCoordinate.IsChecked == true)
                        CurrentZValue.Text = ((Accelerometer3D)sender).CurrentAcceleration[Accelerometer3D.AccelerationAxis.Z].ToString();
            }));
        };
    }
}

Как видно из этого примера, код для работы с датчиками изменился не очень сильно. Фактически, изменился только код получения данных от датчиков, а остальное осталось неизменным.

Что интересно, эти датчики могут использовать несколько приложений одновременно. Также в одном приложении можно использовать несколько датчиков. Давайте совместим приложение с вращением трехмерной модели с датчиком света. В этом случае кроме поворота модели будем показывать солнце. Если освещенность в помещении снижается, то и солнце будет скрываться. Чем больше освещения в помещении, тем солнце будет светить интенсивнее. Соответственно в этом приложении используется код из двух предыдущих примерах. Поэтому я не буду приводить код, а сразу покажу результат.

На этих примерах хорошо видно, что работа с датчиками в Windows 7 является очень простой. Однако, для этого необходимо иметь драйвер для Windows 7 и класс-обертку для Sensor&Location platform. Как правило, драйвера поставляются самим производителем аппаратной платформы, а вот класс-обертку можно реализовать самостоятельно. В следующий раз мы поговорим как раз на эту тему.