Статьи

Управляйте своим кондиционером с Raspberry Pi и группой Microsoft – часть 2

вступление

В этом посте я объясню, как устройство Windows 10, к которому подключен Band, и Raspberry PI2 «общаются» друг с другом. Как уже было показано в предыдущем посте (и заголовке этого поста), это делается с помощью очереди Azure Service Bus. Код для этого находится в двух из пяти проектов в демонстрационном решении :

  • TemperatureReader.Shared содержит объекты данных, перемещаемые по очереди служебной шины Azure, а также все глобальные определения.
  • TemperatureReader.ServiceBus содержит фактический код для кода служебной шины.

Начиная

Прежде чем вы сможете получить работающий код, вам необходимо определить служебную шину Azure, потому что вы не можете использовать мою — я хотел бы оставить ее для демонстраций самостоятельно и сохранить разумность моего счета Azure, спасибо :). Если вы посмотрите в TemperatureReader. Общий проект в демонстрационном решении вы найдете класс «Настройки», который, помимо прочего, имеет следующее довольно длинное и довольно загадочное определение:

public static readonly string TemperatureBusConnectionString =

"Endpoint=sb://yournamespacehere.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=XXXXXXXXXXXXXXXXXYOURKEYHEREXXXXXXXXXXXXXXXX";

Чтобы иметь возможность использовать очереди в служебной шине, сначала необходимо запросить пространство имен . В настоящее время это поддерживается только «старым» порталом Azure , поэтому вам нужно перейти туда, чтобы создать его:

Название изображения

Название изображения

Вам необходимо выбрать вкладку «Сервисная шина» ниже, затем нажать кнопку «Создать». Затем вы получите всплывающее окно, как показано справа. Создайте красивое имя пространства имен (Azure скажет вам, доступно оно или нет), затем выберите регион. Поскольку я живу в Нидерландах, я обычно выбираю «Западную Европу», но не стесняюсь выбирать иначе. Остальные настройки можно оставить как есть — хотя вы можете рассмотреть возможность установки уровня сообщений на «базовый». Нажмите на галочку, и вы получите изображение ниже.

Название изображения

Ваше пространство имен шины службы Azure создано. Это этап 1 — теперь необходимо создать реальные очереди. Из кода в файле Settings.cs вы можете увидеть, что на этой служебной шине должно быть две очереди:

public static readonly string TemperatureQueue = "temperaturedatabus";

public static readonly int TemperatureQueueTtl = 10;



public static readonly string FanSwitchQueue = "fanswitchcommandbus";

public static readonly int FanSwitchQueueTtl = 10;

Первый предназначен для отправки данных о температуре с PI2 на устройство Windows 10 (и, в конечном счете, в Band), а второй — для отправки команд на включение и выключение вентилятора. Я установил время жизни (TTL) для сообщений в каждой очереди на 10 секунд, что означает, что любые данные старше 10 секунд могут быть отброшены, если они не будут получены. Вы можете пойти дальше и щелкнуть стрелку, которую я выделил, используя красное поле рядом с «thisisademo», и настроить очередь вручную или просто позволить коду (см. Ниже) позаботиться об этом. Есть только одна вещь, которая вам понадобитсядля этого: выберите только что созданное пространство имен Azure Service Bus, а затем нажмите «Информация о соединении» внизу. При наведении курсора на текст, начинающийся с «Endpoint = sb: //», вы получаете синий значок «copy» справа и, если вы щелкнете по нему, вся строка подключения будет помещена в буфер обмена для вашего удобства. Затем вы можете вставить что в константе TemperatureBusConnectionString в Settings.cs

Название изображения

Базовый клиент очереди

В любом проекте, в котором вы хотите использовать Azure Service Bus, вам потребуется использовать пакет NuGet «WindowsAzure.Messaging.Managed» (при условии, что вы используете управляемый язык, такой как C # или VB.NET).

Название изображения

Тогда важно знать, что любой клиент (и под этим я имею в виду объект типа Microsoft.WindowsAzure.Messaging.Queue) может как помещать объекты в очередь, так и читать их — и делать это довольно глупо, как я выяснил, трудный путь. Объект Queue, который делает оба, в основном ест свои собственные сообщения — большую часть времени, но не всегда . Удачной отладки. Во всяком случае, я хотел клиента очереди, который

  • Я мог бы либо попросить прочитать из очереди или поместить данные в очередь, но не оба
  • мог только отправлять или получать данные указанного типа
  • автоматически создаст очередь на служебной шине, если она не существует

Я придумал это:

Если объект QueueClient создан, он принимает имя очереди, строку подключения к очереди (эту невозможную длинную строку в Settings.TemperaBpusConnectionString), время жизни сообщения и QueueMode (может быть либо Send, либо Listen). Обратите внимание, что у него есть параметр типа — это тип объекта, который вам разрешено помещать в очередь — или вы можете ожидать его выхода.

using System;

using System.Diagnostics;

using System.Threading.Tasks;

using Microsoft.WindowsAzure.Messaging;



namespace TemperatureReader.ServiceBus

{

  public class QueueClient<T> : IQueueClient<T>

  {

    private readonly string _queueName;

    private readonly string _connectionString;

    private readonly int _messageTimeToLive;

    private readonly QueueMode _queueMode;

    private Queue _queue;



    public QueueClient(string queueName, string connectionString, 

      QueueMode mode = QueueMode.Send, int messageTimeToLive = 10)

    {

      _queueName = queueName;

      _connectionString = connectionString;

      _queueMode = mode;

      _messageTimeToLive = messageTimeToLive;

    }



    public async virtual Task Start()

    {

      try

      {

        var settings = new QueueSettings 

          { DefaultMessageTimeToLive = TimeSpan.FromSeconds(_messageTimeToLive) };

        await Queue.CreateAsync(_queueName, _connectionString, settings);

        Debug.WriteLine($"Queue {_queueName} created");

      }

      catch (Exception)

      {

        Debug.WriteLine($"Queue {_queueName} already exists");

      }



      _queue = new Queue(_queueName, _connectionString);

      if (_queueMode == QueueMode.Listen)

      {

        _queue.OnMessage(message =>

        {

          var data = message.GetBody<T>();

          OnDataReceived?.Invoke(this, data);

        });

      }

    }



    public virtual async Task PostData(T tData)

    {

      if (this._queueMode == QueueMode.Send)

      {

        await _queue.SendAsync(tData);

      }

      else

      {

        throw new ArgumentException(

          "Cannot send data using a QueueMode.Listen client");

      }

    }



    public void Stop()

    {

      if (_queue != null)

      {

        _queue.Dispose();

        _queue = null;

      }

    }



    public event EventHandler<T> OnDataReceived;

  }

}

Чтобы начать, вам нужно будет позвонить в «Пуск». Это сначала пытается создать очередь. К сожалению, я не смог выяснить, как проверить, существует ли очередь в первую очередь, поэтому я прибег к довольно грубому методу проглатывания исключения в случае сбоя при создании очереди — и предполагаю, что исключение было вызвано уже существующей очередью. Затем он создаст реальный объект Queue (Microsoft.WindowsAzure.Messaging) — и, если это Listen QueueClient, он присоединит анонимный метод к методу OnMessage этой очереди. Этот метод выведет объект данных из очереди и передаст его любому, кто подписан на событие OnDataReceived самого QueueClient.

Метод post QueueClient в основном является типизированной оболочкой для метода Queue PostData, за исключением того, что он запрещает публикацию сообщений, если это клиент Listen, — чтобы предотвратить поглощение собственного сообщения.

Наконец, есть метод Stop, который просто удаляет очередь.

Использование конкретных реализаций

Как TemperatureQueueClient, так и FanSwitchQueueClient являются дочерними классами, обеспечивающими конкретные реализации клиента очереди. Например, TemperatureClient просто обрабатывает данные о температуре, и все, что вам нужно сделать, это предоставить конструктор переопределения, чтобы вам не приходилось устанавливать все эти свойства каждый раз, когда вы их используете:

using TemperatureReader.Shared;

namespace TemperatureReader.ServiceBus

{

  public class TemperatureQueueClient : QueueClient<TemperatureData>

  {

    public TemperatureQueueClient(QueueMode mode = QueueMode.Listen) :

      base(Settings.TemperatureQueue, 

        Settings.TemperatureBusConnectionString, mode, 

        Settings.TemperatureQueueTtl)

    {

    }

  }

}

Единственное, что осталось установить — это QueueMode, так как вы обычно хотите создать одного клиента прослушивания и одного клиента очереди отправки.

Если вы теперь посмотрите на MainPage_Loaded в MainPage.Xaml.cs в TemperatureReader (проект IoT), вы увидите TempeatureQueueClient, обрабатывающий данные температуры, настроенный как клиент очереди отправки, в то время как FanSwitchQueueClient является клиентом очереди прослушивания — что имеет смысл, поскольку Raspberry PI2 отправляет данные о температуре, но прислушивается к командам для включения или выключения вентилятора (в конечном счете, из группы Microsoft, где-то в мире).

private async void MainPage_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)

{

  // stuff omitted

  var poster = new TemperatureQueueClient(QueueMode.Send);

  var fanCommandListener = new FanSwitchQueueClient(QueueMode.Listen);

В клиентском приложении, предназначенном для устройства Windows 10, к которому подключен Band (обычно это телефон), все наоборот. В MainViewModel.CreateNew вы увидите:

public static MainViewModel CreateNew()

{

  var fanStatusPoster = new FanSwitchQueueClient(QueueMode.Send);

Указывает, что клиент очереди FanSwitchQueueClient отправляет сюда, а TempeatureQueueClient — встроенный в TemperatureListener — создается в конструкторе как клиент Listen :

public TemperatureListener() 

{ 

  _client = new TemperatureQueueClient(QueueMode.Listen); 

  _client.OnDataReceived += ProcessTemperatureData; 

}

Вывод

Клиент Azure Service Bus — это отличный способ для простых одноранговых соединений в реальном времени, которые могут допускать некоторую задержку или очередь между подключенными устройствами. Он очень прост в использовании, может быть частично создан из кода, защищен по умолчанию и не требует развертывания отдельного веб-сайта в Azure для размещения концентратора соединений — очередь размещается в самом Azure.

В следующий раз я покажу, как измерять температуру на Raspberry PI2.