Windows 7 Sensor and Location platform - реализация поддержки собственного сенсора (Часть 3/3)

Я рассказал о том, как можно использовать Sensor and Location platform в своих приложениях. Мы увидели, что использование этого набора компонент может быть очень удобным для приложения и не требует много усилий. Также мы получаем возможность работы с множеством устройств в унифицированном виде. Единственной проблемой использования этой платформы остается наличие драйверов для Windows 7 и наличие оберток для Sensor API. Разработка драйвера для устройства – задача производителя, в первую очередь. А вот реализацию поддержки в Sensor API вполне можно сделать собственными силами. Здесь мы и займемся этим.

Как я уже говорил точкой входа является класс SensorManager. С помощью этого класса можно получить доступ к нужным сенсорам и поработать с ними. Этот класс имеет такие методы как получение списка всех сенсоров, получение сенсора по ID или по типу, запрос на использование сенсора, а также событие изменения количества сенсоров в системе.

Каждый сенсор имеет два основных типа идентификаторов – SensorId и TypeId. TypeId идентифицирует отдельный класс устройств. Например, по нему можно получить все датчики света в системе, или какие-то другие типы устройств. SensorId присваивается уникально каждому устройству. Например, если в системе три однотипных датчика движений, то каждый будет иметь уникальный идентификатор. Есть еще также CategoryId, который объединяет сенсоры в категории.

Каждый идентификатор представляет собой GUID. Они задаются производителями при разработке устройства и драйверов. Таким образом, можно получить конкретный сенсор только зная его ID. Каждый сенсор представлен классом Sensor. Он имеет общую информацию о сенсоре и методы, которые позволяют получить данные из обобщенных коллекций в нетипизированном виде. Понятно, что такое представление данных не очень удобно для наших приложений. Поэтому для каждого сенсора принято реализовывать класс-обертку в рамках Sensor API. Реализуется она путем наследования от общего класса Sensor. В демонстрационных примерах уже есть две такие реализации – для акселерометра и для датчика света. Однако, в устройстве, которое мы рассматривали ранее присутствуют также сенсорные кнопки, которые также можно использовать. Поэтому давайте реализуем такой класс для этого датчика.

Мы определим новый класс, который будет наследником класса Sensor. Для того, чтобы он распознавался в Sensor API его нужно пометить атрибутом SensorDescription, в котором указать TypeId для этого типа сенсоров. В базовом классе Sensor существуют две важные вещи для нас – свойство DataReport и событие DataReportChanged. Это свойство содержит данные от сенсора, а событие срабатывает при их изменении. Задача нашего класса – воспользоваться этими данными и доставить их пользователю нашего класса в удобном виде. Для этого создадим еще один небольшой класс, который будет заниматься разбором информации из DataReport.

Экспериментальным путем мы выясним, что при нажатии кнопки 1 генерируется код 1, при нажатии 2 – генерируется код 2, при нажатии 3 – генерируется код 4, а при нажатии 4 – генерируется код 8. Видно, что здесь используются просто двоичные разряды. Также генерируется код 0 в случае отпускания всех кнопок. Таким образом, мы можем написать следующий код.

[SensorDescription("545C8BA5-B143-4545-868F-CA7FD986B4F6")]
public class SwitchArraySensor : Sensor
{
    public class SwitchArraySensorData
    {
        private static Guid KeyStateProperyId = new Guid(@"38564a7c-f2f2-49bb-9b2b-ba60f66a58df");

        public SwitchArraySensorData(SensorReport report)
        {
            uint state = (uint) report.Values[KeyStateProperyId][0];
            Button1Pressed = (state & 0x01) != 0;
            Button2Pressed = (state & 0x02) != 0;
            Button3Pressed = (state & 0x04) != 0;
            Button4Pressed = (state & 0x08) != 0;
        }

        public bool Button1Pressed { get; private set; }
        public bool Button2Pressed { get; private set; }
        public bool Button3Pressed { get; private set; }
        public bool Button4Pressed { get; private set; }
    }

    public SwitchArraySensorData Current
    {
        get { return new SwitchArraySensorData(DataReport); }
    }

    public event EventHandler StateChanged;

    public SwitchArraySensor()
    {
        DataReportChanged += SwitchArraySensor_DataReportChanged;
    }

    void SwitchArraySensor_DataReportChanged(Sensor sender, EventArgs e)
    {
        if (StateChanged != null)
        {
            StateChanged.Invoke(sender, e);
        }
    }
}

Фактически, этот класс является оберткой в Sensor API для нужного нам сенсора. Для его использования я должен подписаться на событие StateChanged и получать информацию через свойство Current.

Для получения списка доступных сенсоров заданного типа можно использовать метод GetSensorsByTypeId класса SensorManager. При этом TypeId этих сенсоров будет определяться исходя из заданного атрибута SensorDescription. Теперь, используя эти сенсоры мы можем подписаться на нужные событие и получать данные в удобном для приложения виде. Например, можем отображать на форме состояние нажатия кнопок.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    var sensors = SensorManager.GetSensorsByTypeId<SwitchArraySensor>();
    foreach (SwitchArraySensor sensor in sensors)
    {
        switch (sensor.FriendlyName)
        {
            case "Left Switch Array Sensor":
                sensor.StateChanged += delegate(object leftSensor, EventArgs arg)
                {
                    var buttons = ((SwitchArraySensor)leftSensor).Current;
                    SwitchState(LeftButton1, buttons.Button1Pressed);
                    SwitchState(LeftButton2, buttons.Button2Pressed);
                    SwitchState(LeftButton3, buttons.Button3Pressed);
                    SwitchState(LeftButton4, buttons.Button4Pressed);
                };
                break;
            case "Right Switch Array Sensor":
                sensor.StateChanged += delegate(object rightSensor, EventArgs arg)
                {
                    var buttons = ((SwitchArraySensor)rightSensor).Current;
                    SwitchState(RightButton1, buttons.Button1Pressed);
                    SwitchState(RightButton2, buttons.Button2Pressed);
                    SwitchState(RightButton3, buttons.Button3Pressed);
                    SwitchState(RightButton4, buttons.Button4Pressed);
                };
                break;
        }
    }
}

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

Конечно, пример с реализацией подобного сенсора достаточно синтетический. Однако, он явно демонстрирует процесс подключения датчика к Sensor API.

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