Опыт создания облачного решения по мониторингу цифрового киоска на Azure IoT Central

О задаче

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

Обычное желание владельца инфокиосков – понимать, что именно происходит в данный момент, работает киоск или нет, в каком состоянии он находится. Например, есть ли вода в кофе-машине? Если есть, какой ее запас?

В нашем случае задача инфокиоска – как раз помочь подошедшему человеку выбрать товар из каталога и напечатать штрих-код для последующей оплаты этого товара на кассе.
Проблема заключается в том, что подобные решения часто не имеют системы оповещения об остановке работы по разным причинам. Например, у киоска иногда заканчивается чековая лента, отваливается связь, происходит какой-то сбой, и пока персонал магазина это заметит, потенциальные клиенты пройдут мимо (в лучшем случае), а то еще и высмеют в сетях нерадивого предпринимателя.

Когда мы обсуждали с заказчиком, какие критерии он хотел бы отслеживать, на первом месте оказалась именно проблема окончания бумаги в принтере, как самая частая причина временной остановки работы даже самого надежного устройства (знаем это по киоскам в известных заведениях общепита). Идеально, если в киоске всегда будет чековая лента, для чего персонал магазина всегда будет оповещаться о ее окончании. А еще лучше, если такое оповещение придет заранее, до того, как чековая лента закончится. Киоск всегда должен работать!

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

Такой системе мониторинга помимо очевидных («есть ли в принтере чековая лента?») можно будет «задать» достаточно интересные вопросы:

  • сколько чековой ленты тратится в день, в месяц данным киоском?
  • сколько времени киоск работал, и сколько нет? (фактически, «SLA киоска»)
  • какой остаток чековой ленты на данный момент?

Если мы пофантазируем и пойдем еще дальше, то такие вопросы можно будет задавать не только в разрезе конкретного киоска, а к группе киосков, например, по магазинам в конкретном регионе. В дальнейшем это позволит использовать эти киоски более эффективно, например, иметь требуемый запас чековой ленты или менять ее, не дожидаясь фактического окончания.

Размышляя, как это сделать централизованно, рано или поздно придем к уже немного набившему оскомину широко упоминаемому Интернету вещей. Инфокиоск – «вещь». Их много. Эти «вещи» централизованно передают телеметрию со своим состоянием в какой-либо сервис, где эти данные обрабатываются и могут быть визуализированы и проанализированы.

Но самое важное — это даже не данные (не очень они и интересуют обычно клиента, разве что только в плане отчетности), а автоматизированный механизм принятия решения — в какой момент нужно привлечь человека к решению проблемы отправкой уведомления на телефон, email или в систему HelpDesk. Хорошо работающее устройство должно «молчать», но испытывающее проблемы – сразу (а лучше заранее) сообщать о проблеме.

Перед глазами возникает идиллическая картина, как генеральный директор сети «рулит» всем оборудованием через интернет, сидя в кресле и покуривая сигару.

Теоретически все выглядит очень здорово, но практика, как правило, более сложна. И в этой статье мы хотели бы описать свой опыт, который получили, создавая подобное решение.

Чем нам поможет «Интернет вещей»?

Самой концепции «Интернета вещей» на данный момент около 20 лет. Помимо того, что эта концепция сейчас сама по себе достаточно модная, наблюдается ее реальное наполнение технологиями: развиваются облачные вычисления, распространяются беспроводные сети, расширяется предложение решений высокой степени готовности.

Для информационных технологий 20 лет – это достаточно большой возраст, и для многих задач Интернет вещей дает решения разной степени готовности, часто практически готовые к реальному применению. Большинство облачных провайдеров обладают такими готовыми решениями. В данной статье речь пойдет о платформах Microsoft, так как в Кварта Технологии мы лучше всего знакомы именно с решениями от Microsoft, как с наземными, так и облачными. Мы выбрали технологии Microsoft, потому что:

  • Microsoft предоставляет самый широкий выбор решений для IoT на разных уровнях облачных вычислений. Доступны решения как PaaS, так и SaaS;
  • Облачные решения Microsoft глубоко интегрированы в платформу Azure, позволяют легко подключать дополнительные сервисы (количество которых огромно!) и хорошо управляются. Для управления доступен и графический интерфейс, и командная строка;
  • Все решения хорошо документированы, доступны примеры реализации наземной и облачной части на разных языках программирования, причем примеры обычно формируются динамически именно под ваш сервис;
  • Сервисы масштабируемы. Не придется думать, что делать, если количество необходимых ресурсов резко возрастет;
  • Ценообразование на все сервисы понятно и прогнозируемо;
  • Вопросов с приобретением сервисов нет – в наличии множество партнеров (включая Кварту), помощь представительства, отлаженная логистика.

Почему мы говорим именно о готовых решениях? Ведь первая мысль, которая при этом возникает, это то, что за них надо платить по модели подписки, т.е. чем больше используем ресурсов, тем больше платим. При этом собственно «за что» платим – у разных сервисов определяется по-разному. Где-то это количество подключенных устройств, где-то объем телеметрии. Кроме того, решение как бы не принадлежит нам, а мы его «арендуем», что у многих разработчиков вызывает некоторые опасения.

Ответ на вопрос «почему взять готовое» очень простой.

  1. Мы хотим сделать решение быстро и, желательно, качественно. На первый взгляд, сочетание этих двух параметров кажется абсолютным бредом нереализуемым. Но если мы применим решение высокой степени готовности, которое уже до нас разработали, отладили, протестировали, мы значительно сэкономим время на разработку. В реальности это означает, что мы выпустим на рынок решение быстрее, чем наши конкуренты. Задумайтесь, вы же используете в работе готовые библиотеки, иногда платные? В разработке коммерческого продукта для микроконтроллеров, например, гораздо эффективнее купить высококачественный USB стек с поддержкой, чем разрабатывать свой.
  2. Если мы решим создавать решение полностью своими силами, капитальные затраты на него (CapEx, в данном случае затраты на разработку и отладку) могут стать настолько значительными, что эксплуатация решения никогда не даст выгоды по сравнению с готовым решением, где оплата идет только за подписку (т.е. наши операционные расходы, OpEx).
  3. В части конфигурации решения высокой степени готовности под нашу задачу поставщик решения, как правило, предоставляет соответствующие рекомендации. Microsoft, например, выпускает статьи с best practices, проводит т.н. Design Sessions (в т.ч. и вместе с Кварта Технологии), где подробно объясняет, как надо проектировать решение из готовых частей. Зачем идти по неправильному пути, если можно сразу делать правильно, чтобы решение заработало максимально быстро и эффективно?

Конечно, в отдельных ситуациях «сделать свое» вполне оправдано, например, в целях совместимости с существующей системой. Но в типовой задаче обработки телеметрии с устройств интернета вещей такой подход, на наш взгляд, несостоятелен и приведет к удорожанию решения: мы неоднократно видели решения, в которых компании изобретали велосипед «делали полностью свое». Архитектура решения часто получалась сделанной на костылях странной, тяжело отлаживаемой и далекой от обработки в масштабе, хотя бы близком к реальному времени (вообще это достаточно широкий термин, который здесь мы употребляем в его «бытовом» понимании). Программисты писали протоколы для обмена данными, которые уже написаны, дизайнеры рисовали графические панели, которые уже нарисованы, математики реализовывали аналитику, которая уже реализована.

И да, у нас есть подобные анти-примеры, но по политическим этическим соображениям мы не можем их описывать.

Конфигурация киоска и проблема регистрации устройств

У нашего клиента уже развернуты киоски на специализированной операционной системе Windows 10 IoT Enterprise LTSC (Long-term Servicing Channel). Выпуски данной редакции ОС поддерживаются в течение 10 лет после релиза, а также имеют специализированные возможности встраивания, такие, как фильтры записи на диск, фильтр клавиатуры, замена оболочки и т. д., да и стоят раза в 3 дешевле «настольных» аналогов.

Одним из условий использования данной редакции ОС является поставка ОС уже установленной на устройство, вместе с самим устройством и всеми необходимыми приложениями, т.е. ОС и приложения, работающие поверх нее, «встраиваются» в устройство (отсюда и название: «встраиваемые системы») и неотделимы от него.

Для подготовки образа Windows 10 IoT Enterprise LTSC можно использовать бесплатные инструменты Microsoft, интересующихся отсылаем к статье, а про историю развития средств разработки можно прочитать здесь. Для приложений, работающих поверх данной ОС, благодаря ее полной двоичной совместимости с классическими Windows, применяются традиционные средства разработки, такие, как Visual Studio.

Поскольку образ системы уже разработан, в «наземной» части мы были задействованы только в разработке приложения-клиента, подключающегося к облачному сервису Microsoft IoT Central. Речь здесь идет о небольшой программе, которая, собственно, и занимается сбором телеметрии и отправкой ее в облако.

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

Одним из требований является установка нашего приложения через один из сервисов удаленного доступа к рабочему столу, без необходимости присутствия человека рядом с киоском.

Облачная часть

Выбор у нас был всего из двух вариантов:

  • PaaS (платформа как сервис) – облачный провайдер предоставляет платформу, на которую мы должны развернуть наше решение, предварительно его разработав. Примером PaaS от Microsoft является IoT Hub – решение для централизованного приема/отправки данных с/на большое количество устройств. Поскольку провайдер предоставляет только платформу, в этом случае нам самим предстоит решить, как обрабатывать полученные данных, какие средства визуализации применять, например, использовать службы потоковой аналитики (Stream Analytics), хранения данных, визуализации при помощи веб-приложения (Web Application). Естественно, это веб-приложение еще нужно будет разработать;
  • SaaS (программное обеспечение как сервис) – облачный провайдер предоставляет готовое программное обеспечение с оплатой по мере его использования. Преимуществом данного варианта является то, что на облачной части мы получаем готовый продукт и не пишем ни одной строчки кода (ложка дегтя: «наземную» часть разрабатывать все-таки придется, но без этого, увы, никак). Недостатком является отсутствие гибкости, то есть поменять что-то в готовом облачном продукте «под себя» не получится. Примером SaaS является широко известный Microsoft 365, ранее имевший название Office 365. В случае Интернета вещей к службам SaaS относится IoT Central.

Конечно, мы выбрали IoT Central как максимально готовое решение, потому что для демонстрации того, как облако решает задачу мониторинга киоска, пилотный проект нам нужен был максимально быстро.

Что позволяет сделать SaaS-служба IoT Central?

  • Принять данные телеметрии от большого количества устройств безопасным способом, в зашифрованном виде;
  • Отправить команды на устройство также безопасным способом, отправить команды на группу устройств;
  • Управлять устройствами: добавлять устройства в службу, блокировать и удалять их, изменять набор данных телеметрии устройства;
  • Хранить телеметрию с устройств в течение заданного времени (но не более 30 дней);
  • Визуализировать данные несколькими способами: как в виде различных графиков, так и журналов состояний. Доступна также визуализация положения устройства на карте;
  • Настраивать внешний вид панелей визуализации;
  • Выполнять типовые аналитические операции с потоком данных;
  • Создавать уведомления различных типов, основываясь на значениях заданной телеметрии;
  • Осуществлять версионирование форматов данных, в которых устройство отправляет данные, таким образом всегда поддерживая обратную совместимость;
  • Экспортировать полученные данные во внешние источники для последующего хранения и обработки внешними средствами.

Таким образом, при выборе IoT Central нам не нужно думать о безопасности решения, написании кода для облачной части и кода для соединения с ней: доступен соответствующий SDK и примеры для нескольких языков. Нам не нужно также думать о масштабировании решения, когда количество подключенных устройств станет достаточно большим, о способах визуализации данных и реализации типовой аналитики. Все потенциальные проблемные места на облачной стороне уже устранены за нас. Стоимость решения может быть достаточно точно оценена заранее, до его реализации.

Оформляем подписку Azure (можно буквально за 5 минут получить пробную) и начинаем работу с IoT Central с ресурса www.azureiotcentral.com, где нажимаем кнопку «Создать» и выбираем не приложение из готовой подборки, а «пользовательское», т.к. хотим приключений максимально настроить решение под наши нужды.

Для тестов выбирайте ценовой план «Бесплатный» или «Стандартный 1». Мы пользуемся планом «Стандартный 2». «Сообщения», речь о которых идет в описании планов – это блоки данных объемом до 4 КБ каждый. Сообщения в IoT Central передаются в формате JSON, т.е. в виде текстовых блоков с определенной разметкой, что достаточно удобно при отладке «наземной» части. Если сообщение не умещается в 4 КБ, то оно будет автоматически разбито на необходимое количество блоков меньшего размера.

В поле «Шаблон приложения» нужно выбрать «Custom application», а НЕ «Custom Application (legacy)». Legacy-приложения нужны исключительно для совместимости, и они не поддерживают ряд функций, таких, как версионирование.

После создания решение IoT Central будет доступно по адресу вида имя.azureiotcentral.com.
В интерфейсе решения видим меню:

Начнем краткий обзор с администрирования. Создавший приложение IoT Central становится его администратором, т.е. имеет полный доступ к его настройке. Есть и другие роли, такие, как создатель, оператор, различающиеся уровнем доступа к настройке приложения. В данном случае роли перечислены в порядке уменьшения прав. Администратор может настраивать все, а оператор, грубо говоря, имеет доступ «только для чтения».

Такая система достаточно удобна: мы настроили решение для нашего клиента, и не хотим, чтобы он вмешивался в настройки, соответственно – даем его учетной записи роль оператора. Или, например, хотим, чтобы он мог добавлять шаблоны устройств, но не мог изменять что-либо в разделе «администрирование»: даем ему роль создателя.
Назначение ролей перечислено в самом интерфейсе:

Одним из ключевых понятий в IoT Central является понятие «шаблон устройства». Шаблоны устройств находятся в одноименном разделе меню. Шаблон – это мета-описание всех данных, которые передает/принимает устройство, вместе с форматом этих данных. Если IoT Central знает шаблон устройства, то знает, и на каком «языке» это устройство «говорит».

Шаблон устройства включает следующие секции:

  • Device capability model (DCM): модель возможностей устройства – содержит сведения о том, как устройство взаимодействует с IoT Central, все данные и их формат;
  • Cloud properties: облачные свойства, любые данные, относящиеся к устройству, которые будут храниться только на облачной стороне (например, какие-либо заметки);
  • Customizations: тонкие настройки «поверх» тех, что находятся в DCM, в основном, это относится к локализации шаблона;
  • Views: представления, внешний вид графической панели, на которой будут отображаться сведения об устройстве и данные.

DCM является самой важной частью и, в свою очередь, состоит из ряда интерфейсов, каждый из которых может включать:

  • Данные телеметрии, т.е. поток данных, упорядоченных во времени;
  • Свойства устройства, которые могут быть доступны и для чтения, и для записи. В отличие от телеметрии, свойства имеют только мгновенное значение и не являются потоком данных;
  • Команды, которые можно направить из облака в устройство (с ответом или подтверждением приема).

Шаблон некоторых готовых устройств может быть взят из галереи устройств. При разработке собственного устройства – может быть разработан непосредственно в интерфейсе IoT Central или загружен или JSON файла.

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

Наиболее удобным способом разработки шаблона является его создание непосредственно в интерфейсе IoT Central. Впоследствии подготовленный шаблон можно загрузить и сохранить резервную копию в формате JSON.

DCM нашего киоска включает следующие интерфейсы:

  • Device Information: общая информация об устройстве, его изготовитель, наименование и версия ОС и т.д.
  • Printer: информация о принтере, такая, как код ошибки (нулем обозначаем ее отсутствие), текстовое описание этого кода;
  • Paper: параметры чековой ленты – расход чековой ленты на один чек, остаток ленты в катушке на данный момент, длина ленты в новой катушке;
  • Performance: параметры, позволяющие косвенно судить о «здоровье» киоска: загрузка центрального процессора и его температура, доступная ОЗУ и место на жестком диске;
  • Commands: набор команд для отправки на устройство. Сюда входят команды перезагрузки, удаленного обновления ПО, установки параметров чековой ленты.

Наш шаблон также содержит представления (Views), которые создаются в графическом редакторе перетаскиванием плитки в рабочую область и выбору того, что на ней будет отображаться и в каком виде. На рисунке показано редактирование плитки, на которой отображается журнал состояния принтера:

Шаблоны поддерживают версионирование. Для того, чтобы изменить что-то в шаблоне, нужно создать его новую версию (одноименная кнопка «Версия»), которая наследует все настройки предыдущей, и становится доступной для редактирования. По окончании редактирования для последующего использования шаблон необходимо «опубликовать».

Создав и опубликовав шаблон, нажимаем кнопку «Управление тестовым устройством» и в появившемся окне выбираем параметры для нашей «наземной» части:

Получаем готовый код для тестирования, в котором уже присутствуют все идентификаторы для подключения клиента к нашей облачной службе.

Заходим на вкладку «Устройства» и регистрируем новое устройство, нажав кнопку «Создать».

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

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

Нажимаем кнопку «Создать» и видим это устройство в списке устройств. Копируем идентификатор устройства в наш скачанный код (см. SampleApp.cs).

Можем убедиться, что этот код уже содержит правильные ключи для доступа к сервису. Для этого зайдем в «Администрирование», затем «Подключение» устройства, и сверим параметры «Область идентификатора» и «Подписанный URL-адрес (SAS)» по ссылке «Посмотреть ключи» со значениями в коде SampleApp.cs:

static string scopeId = "...";
static string deviceId = "...";
static string key = "...";

Можем открыть проект в Visual Studio и запустить его. Сразу же начнется отправка данных в сервис. Тестовый проект отправляет заранее запрограммированные в коде данные, нам предстоит модифицировать этот код и отправить/получить реальные данные.

Здесь сразу же заметим следующие сложности:

  • Код содержит ключ общего доступа SAS (Shared Access Signature), что нормально для тестового проекта, но совершенно не подходит для реального окружения, так как небезопасно. Утечка этого ключа приведет к компрометации всего сервиса. В дальнейшем мы покажем, как решить этот вопрос и сделать «наземную» часть более безопасной;
  • Перед тем, как «увидеть» данные с устройства в облаке, мы вручную провели регистрацию («Provisioning») устройства, выбрав его шаблон. Такой способ регистрации не очень подходит, когда устройств или шаблонов много. В дальнейшем мы покажем, как автоматизировать регистрацию и выбор шаблона.

В данном тестовом проекте можно столкнуться с одной сложностью, если в системе установлена русская локаль. В этом случае String.Format() преобразует числа типа Double в строку для создания соответствующего JSON для отправки в IoT Central. В русской локали числа, преобразованные в строку, будут содержать запятую «,» вместо десятичной точки «.», в то время как IoT Central ожидает именно точку. В этом случае данные не будут приняты и в облаке не появятся. Для того, чтобы это исправить, во всех файлах telemetryStateEvent*.cs замените

String.Format(...)

На

String.Format(CultureInfo.InvariantCulture, ...)

Не забудьте добавить в заголовок файла

using System.Globalization;

Это приведет к тому, что при преобразовании в строку десятичной точкой будет именно точка, а не запятая, и данные будут нормально приняты облачной частью.

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

Конечно, в случае нашей реальной задачи мы сконфигурировали облачную часть не с первой попытки. До достижения результата, который удовлетворил и нас, и клиента, мы сделали в общей сложности 9 версий шаблона нашего киоска.

«Наземная» часть

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

Мы произвели оценку необходимого периода отсылки данных в нулевом приближении. Заданный лимит на одно устройство составляет 30 тысяч сообщений в месяц. В рамках каждого интерфейса может отправляться несколько сообщений в зависимости от типа данных: свойства и телеметрия отправляются отдельными сообщениями, т.е. на каждый интерфейс приходятся одно или два сообщения, а в нашем случае отправка всех данных состоит из 5 сообщений, одно из которых (общая информация об устройстве) можно исключить, т.к. не требуется постоянная его отправка. Итого за месяц в рамках лимита можно сделать 30 000 / 4 = 7500 отправок, а значит, на один день приходится 250, или примерно 10 отправок в час, одна отправка в 6 минут. Мы приняли интервал отправки равный 5 минутам. При расчетах следует учесть:

  • Выбранный лимит суммируется (примерно как багаж в аэропорте), т.е. если на два устройства общий лимит 60 тысяч сообщений, то одно может использовать 40, другое 20, и дополнительной оплаты не будет;
  • Превышение лимита оплачивается отдельно, актуальную цену за дополнительные 10 тысяч сообщений можно узнать по ссылке.

Не рекомендуется выбирать для решения слишком маленький интервал, т.к. это приведет к дополнительным затратам. Не рекомендуется также выбирать слишком большой интервал, так как при этом будет сложно понимать, что происходит с устройством, в сети оно или нет.

Также стоит отметить, что если какой-то параметр телеметрии может измениться значительно за выбранный интервал, результаты принятой телеметрии могут не отражать реальную картину: мы можем, например, банально «пропустить» скачок загрузки процессора. Исходя из этого, в каждом решении нужно либо динамически подстраивать интервал отсылки телеметрии, либо, например, усреднять данные за некоторый временной интервал или обрабатывать их каким-то иным образом.

Вне зависимости от того, производите вы или не производите усреднение данных, для определения частоты опроса (не отправки!) данных можно воспользоваться правилом, позаимствованным из цифровой обработки сигналов: выбирайте частоту опроса как минимум в два раза больше максимальной частоты изменения опрашиваемого параметра. Затем усредняйте данные (или ищите максимум, минимум – в зависимости от смысла параметра) и отправляйте их в облако. В некоторых ситуациях частота отправки телеметрии может совпадать с частотой опроса параметра.

Итак, что же мы дописали в тестовом приложении:

  • Сбор общей информации о системе и ее характеристик в текущий момент. Основным методом получения является использование WMI и PerformanceCounter. Пример получения наименования ОС:

public static string OSFriendlyName
{
get
{
string result = string.Empty;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem");
foreach (ManagementObject os in searcher.Get())
{
result = os["Caption"].ToString();
break;
}
return result;
}
}

Пример получения загрузки процессора:

PerformanceCounter cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

Далее:

cpuCounter.NextValue();

Также был написан дополнительный код, производящий усреднение.
Использование WMI в данном случае вынуждает нас запускать клиент с правами администратора.

  • Состояние принтера. Принтер, печатающий чеки, отображается в системе как обычный USB принтер. Соответственно, мы можем использовать его очередь печати для получения состояния:

PrintServer ps = new PrintServer();
pq = ps.GetPrintQueue(name);

Затем опрашиваем очередь печати на наличие проблем, используя свойства типа

pq.HasPaperProblem

  • Остаток чековой ленты, который рассчитывается при непрерывном мониторинге очереди печати.
  • Мы также реализовали обработку команд, отправляемых с IoT Central. Устройство можно удаленно перезагрузить и обновить наше приложение-клиент, но, поскольку оно не может обновить само себя, нам понадобилась внешняя служба, о которой мы расскажем далее.

Первоначальная регистрация устройств

К данному моменту мы уже отладили код приложения-клиента, отсылающего телеметрию и обрабатывающего команды с облака, но по-прежнему осталась проблема безопасности решения, так как все идентификаторы доступа, включая SAS, остались в коде. Кроме того, каждый инфокиоск должен иметь свой уникальный идентификатор, а значит, нам нужно его как-то сгенерировать.

Регистрация устройств в IoT Central происходит при помощи отдельной облачной службы – Device Provisioning Service (DPS), которая принимает от устройства три параметра:

  1. Идентификатор устройства (Device ID), произвольная комбинация букв и цифр, которая однозначно указывает на это устройство. Идентификатор не обязательно должен быть зарегистрирован в IoT Central до подключения устройства, об этом далее.
  2. Область идентификатора (Scope ID). По ней DPS найдет IoT Hub, поверх которого работает IoT Central, и передаст соответствующие URL нашему клиенту. Нет необходимости разбираться, как это происходит вплоть до строчек кода, так как в SDK соответствующие возможности уже реализованы.
  3. Ключ доступа для аутентификации устройства. Для удобства закодирован в Base64.

Передача соответствующих параметров в DPS в тестовом проекте происходит в коде примера (connection.cs) в следующих строках:

using (var security = new SecurityProviderSymmetricKey(deviceId, deviceKey, deviceKey))
using (var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly))
{
var provClient = ProvisioningDeviceClient.Create("global.azure-devices-provisioning.net", scopeId, security, transport);
DeviceRegistrationResult result = await provClient.RegisterAsync().ConfigureAwait(false);
...

Вместо ключа доступа можно (и рекомендуется) использовать сертификаты X.509, но для нашего пилотного проекта мы решили пойти более простым путем, оставив ключи доступа, оставив сертификаты для будущего использования.

Конечно, в целях безопасности ключ SAS, единый для всех устройств, необходимо было заменить на ключ, уникальный для каждого устройства. Такая возможность существует, и для получения уникального ключа устройства и сокрытия SAS необходимо выполнить алгоритм HMAC SHA256 для вычисления хеша от идентификатора устройства, «смешав» идентификатор и SAS. На языке C# это выглядит так:

var hmac = new HMACSHA256(Convert.FromBase64String(sas_key));
var sig = hmac.ComputeHash(Encoding.ASCII.GetBytes(device_id));

В переменной sig получим наш ключ, уникальный для данного устройства.

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

Именно DPAPI мы решили использовать для более безопасного хранения ключа устройства.

Также нам хотелось бы, чтобы регистрация устройства происходила автоматически при первом подключении устройства, без предварительного «заведения» устройства в IoT Central. Для этого требуется выполнение двух условий:

  1. В самом IoT Central в разделе «Администрирование» — «Подключение устройства» необходимо ползунок «Автоматическое утверждение» перевести в активное положение:


Но этого будет недостаточно: IoT Central добавит устройство в список устройств, но ему не будет задан шаблон устройства, данные с него невозможно будет просмотреть. Шаблон необходимо будет задать вручную после появления устройства в IoT Central, или назначить автоматически, как указано в п.2.

  1. Для автоматического назначения шаблона нужно найти в файле connection.cs строки

var provClient = ProvisioningDeviceClient.Create("global.azure-devices-provisioning.net", scopeId, security, transport);
DeviceRegistrationResult result = await provClient.RegisterAsync().ConfigureAwait(false);

И заменить их на код, добавляющий дополнительные данные в формате JSON для регистрации:

var data = new ProvisioningRegistrationAdditionalData();
data.JsonData = "{ \"iotcModelId\": \"URI\" }";
DeviceRegistrationResult result = await provClient.RegisterAsync(data).ConfigureAwait(false);

Здесь URI – идентификатор шаблона. Его можно узнать, загрузив шаблон с IoT Central в формате JSON и открыв его текстовым редактором. В строке

"@id": "URI"

и будет находиться искомый URI.


Существует еще один вариант автоматической регистрации: это так называемые Plug-and-Play устройства. Но это – отдельный SDK и тема для отдельной статьи. Интересующихся отсылаем к документации.

Ну и наконец, остается еще один нерешенный вопрос, касающийся безопасности решения. Откуда взять данные для регистрации в DPS? Если идентификатор устройства можно сгенерировать автоматически из MAC-адреса сетевой карты, например, то как получить ключ доступа и область идентификатора? Сделать инсталлятор решения и встроить все необходимое (включая SAS) в него? Это слишком рискованно, так как при желании из инсталлятора эти данные можно с относительной легкостью извлечь и устроить атаку на наш сервис.

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

На эту роль идеально подходят Azure Functions: можно написать простую функцию, которая сделает все описанное. Поддерживается также аутентификация при помощи служб Azure Active Directory, а все ключи можно хранить в Azure Key Vault. Для формирования списка учетных записей, которым разрешен вызов функции, по-хорошему, нужно разработать небольшой кабинет администратора, но для простоты и компактности решения можно ограничиться перечислением этих учетных записей прямо в коде функции. Таким образом, часть регистрации устройств принимает следующий вид:

Вызывать функцию необходимо сразу после установки клиентского приложения на устройство. Мы запросим у пользователя логин и пароль (соответствующие SDK доступны), сгенерируем идентификатор устройства (Device ID), передадим запрос в нашу функцию, и при успешной аутентификации и авторизации в ответ получим ключ устройства (Device Key) и область идентификатора (Scope ID).

Гарантированная работа приложения-клиента 24/7 и удаленное обновление

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

В таких требованиях напрашивается оформление приложения-клиента в виде службы Windows, но при этом оно все еще не сможет само себя обновить по команде с облака. Но если разработать внешнюю по отношению к приложению службу, то можно решить сразу несколько задач:

  1. Отслеживание приложения, перезапуск в случае каких-либо его сбоев.
  2. Работа в фоне, вход в систему не обязателен.
  3. Обновление приложения по команде с облака.

Если первая и вторая задачи решаются относительно просто и даже есть готовые решения, то третья потребовала от нас изобретения велосипеда разработки своего решения.

На данный момент нами реализована специализированная служба для запуска приложения-клиента, с возможность удаленного обновления клиента. Способ обновления следующий:

  1. Приложение-клиент получает с IoT Central команду обновления, в аргументе которой содержится ссылка на архив с обновлением в определенном формате. Архив должен быть предварительно подготовлен и доступен по заданному адресу.
  2. Приложение-клиент загружает данный архив, сохраняет его на файловой системе, устанавливает флаг «необходимо обновление» (в реестре или файле) и сохраняет путь к файлу и завершается.
  3. Служба определяет, что приложение завершилось. Если при этом флаг «необходимо обновление» установлен, сохраняет старую версию приложения в архивной директории, распаковывает архив и запускает новую версию приложения-клиента.
  4. Если новая версия не запустилась, производится ее удаление и откат на старую.
  5. Факт обновления определяется изменением номера версии клиента в IoT Central.

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

Расчет оставшейся длины чековой ленты

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

Все это в совокупности позволяет, производя непрерывный мониторинг очереди печати, рассчитывать остаток ленты. Соответствующие параметры также должны быть отосланы на облачную часть, а на облачной части предусмотрена команда задания длины ленты (установили новую катушку – задать исходную длины), команда задания расхода ленты на один чек (например, в случае изменения формата печати).

Отслеживание остатка чековой ленты позволило нам сообщать о проблеме не постфактум, когда бумага уже закончилась, а вырабатывать в IoT Central соответствующее предупреждение, порог которого можно выбрать. Например, отправить письмо ответственному менеджеру магазина, когда остаток чековой ленты менее пяти метров и ее уже пора заменить.

Сброс длины ленты на киоске

Итак, киоск рассчитывает остаток чековой ленты, и в IoT Central мы можем его увидеть. Но при замене ленты на новую катушку необходимо соответствующий параметр вернуть к исходному значению. Т.е. сотрудник, заменивший ленту, должен зайти на IoT Central, в списке устройств найти соответствующий киоск, и дать ему команду сброса ленты, что не очень удобно.

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

За такой механизм сброса отвечает отдельное приложение, выполняющееся с правами активного пользователя, а команда сброса передается в приложение-клиент через механизм IPC.

Заметим, у нас появился дополнительный параметр: пин-код. Соответственно, в шаблон устройства в IoT Central мы добавили команду установки нового пина, ведь люди имеют свойство его забывать.

Развертывание на киоске

«Наземная» часть нашего решения уже состоит из нескольких частей:

  • Само приложение-клиент IoT Central, осуществляющее сбор и отправку телеметрии, обработку команд;
  • Служба Windows, отслеживающая непрерывное выполнение клиента и обновляющая его в случае необходимости по команде с IoT Central;
  • Небольшое консольное приложение, вызываемое однократно при первоначальной настройке приложения-клиента, осуществляющее генерацию идентификатора устройства и получение ключа доступа и области идентификатора. Приложение предварительно запрашивает у пользователя логин и пароль. Аутентификация осуществляется при помощи служб Azure;
  • Приложение, по определенному действию на сенсорном экране при вводе правильного пин-кода сбрасывает длину чековой ленты на исходное значение. Это позволяет установить это значение на месте, сразу после замены ленты, без необходимости отправки команды с IoT Central.

Облачная часть помимо самого IoT Central включает приложение Azure Functions, которое после успешной аутентификации и авторизации пользователя выдает данные для регистрации устройства.

Осталось самое главное – инсталлятор наземной части. Для его реализации мы использовали один из готовых популярных пакетов. Инсталлятор делает следующее:

  1. Копирует необходимые файлы на файловую систему.
  2. Вызывает консольное приложение регистрации устройства. После успешного получения данных для регистрации устройства – инсталлятор удаляет файлы консольного приложения, чтобы предотвратить его повторное использование.
  3. Ставит в автозапуск утилиту сброса длины чековой ленты, и сразу же запускает ее.
  4. Регистрирует службу, отслеживающую непрерывное выполнение клиента, настраивает ее на запуск клиента, и сразу же ее запускает.

Таким образом, киоск после инсталляции сразу начинает работать без перезагрузки.

Деинсталляция осуществляется в обратной последовательности.

Итак, наше «наземное» решение приняло вид, схематично показанный на рисунке:

Визуализация данных

В IoT Central есть общая панель мониторинга, и панель мониторинга каждого устройства.

Общая панель «набирается» из плиток вручную. Вид нашей общей панели показан ниже:

Каждый киоск представлен «строкой» из трех плиток. Сразу видим, когда киоск последний раз был в сети и какое состояние принтера чековой ленты, ее остаток. Обратите внимание, что киоск в последней строке уже давно не был в сети.

При клике на самой левой плитке попадаем в интерфейс конкретного киоска, на вкладку «Об устройстве», где видим его системные параметры на данный момент.

На вкладке «Параметры» видим параметры чековой ленты и журнал состояний принтера:

Команды для отправки на устройство находятся в одноименной вкладке:

Как видим, весь интерфейс состоит из плиток. Каждая плитка может содержать разные данные, и именно мы заранее определяем, какие. Конфигурация панели мониторинга каждого устройства определяется его шаблонов.

Здесь мы сталкиваемся с ограничениями IoT Central, которые заключаются в том, что в разработке внешнего вида мы ограничены именно плитками, и можем выбрать формат данных на плитке из ограниченного количества вариантов, но не запрограммировать какой-то свой. Например, мы не можем раскрасить плитку в красный цвет по какому-то сочетанию условий, например, если с устройства давно не приходила телеметрия. Но эти ограничения – плата за высокую готовность решения. Если мы хотим тонкой настройки, нам необходимо разрабатывать наше решение на службах PaaS, это будет и сложнее, и дороже, и дольше, но у нас будет возможность тонкой настройки.

Что делать с полученными данными? Правила, аналитика

На наш взгляд, это самый главный вопрос всего интернета вещей. Мы вывели визуализацию данных на панель мониторинга и теперь оператор видит состояние всех устройств. Но не хотелось бы постоянно сидеть, наблюдая, не случилось ли что-то на нашей панели. Хотелось бы, чтобы система сама предупреждала, что что-то идет не так, и только при этом предупреждении оператор начинал на панели мониторинга разбираться, что именно не так.

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

  1. К каким устройствам оно относится, т.е. шаблон устройства.
  2. Условия срабатывания правила. Доступны разные варианты агрегации времени, выбор телеметрии, типа агрегирования (например, сумма или максимум), оператора (равно, больше и т.п.), и значения, с которым мы сравниваем данные.
  3. Действия при срабатывании правила. Самый простой вариант – уведомление по электронной почте. Но если вы хотите реализовать что-то по собственному сценарию, доступны веб-перехватчики (Azure Functions, Microsoft Flow, Azure Logic Apps, другие приложения) и группы действий Azure Monitor.

В примере на рисунке создано правило, срабатывающее для устройств шаблона «Template», когда загрузка процессора превышает 80%, а температура 45 градусов (одновременно).

При срабатывании правила на заранее определенный адрес электронной почты будет направлено сообщение, вид которого отображается здесь же, в редакторе правила:

Как видим, данное сообщение локализовано лишь частично.

Приведем еще один пример. Зная, что сообщение телеметрии «Загрузка процессора» приходит раз в 5 минут, реализуем правило «Соединение потеряно». Правило будет срабатывать, если количество соответствующих данных за прошедшие 15 минут было меньше двух:

Также в IoT Central на одноименной вкладке интерфейса доступна аналитика.

Аналитика позволяет выявлять исторические тренды и выяснять соотношение разной телеметрии с группы устройств.

В приведенном примере рассмотрена телеметрия «Загрузка процессора» в интервале двух дней по всем устройствам шаблона «Template», выбрана агрегатная функция «Максимальное значение». Так мы увидим график максимальной загрузки процессора за временной интервал с 31 мая по 2 июня. Можно построить несколько графиков, разделив их, например, по архитектуре процессора, выбрав соответствующее значение в поле «Разделять по».

На рисунке представлены реальные данные с реальных устройств. Так мы выяснили, что 1 июня примерно в 17.00 на киоске возникла проблема, которая привела к сильной загрузке процессора, и требует дальнейшего выяснения. 1 июня разработчикам также пришло сообщение на E-mail о возникшей проблеме, так как среди правил заранее было настроено правило «Загрузка процессора > 80%».

Узнать, какой именно киоск из группы вызвал данный выброс на телеметрии, можно из e-mail, если было настроено правило, либо на странице аналитики выбрав в поле «Разделять по» — «Идентификатор устройства».

Как можно управлять сразу большим количеством устройств?

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

Решение есть. На вкладке «Задания» можно создать задание на большое количество устройств:

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

Экспорт данных

IoT Central хранит принятые данные не более 30 дней. Если вы хотите сохранить данные для последующего использования, воспользуйтесь функцией экспорта. Варианты экспорта доступны в разделе «Экспорт» при нажатии кнопки «Создать»:

На рисунке приведен пример экспорта в хранилище BLOB-объектов:

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

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

При экспорте в BLOB-хранилище удобно создать функцию Azure, которая будет автоматически вызываться (т.н. «триггер», возможность встроена в Azure Functions и готова к применению: https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob) и получать каждый блоб при экспорте. В целях эксперимента на службах Azure мы реализовали данный вариант и, проанализировав данные, реализовали простейший веб-интерфейс, где киоскам «без проблем» соответствуют зеленые квадраты, киоскам «с проблемами» — красные (на рисунке), а при клике на названии киоска пользователь попадает в интерфейс IoT Central на панель мониторинга данного киоска.

Такое решение с дополнительным экспортом только ради визуализации – едва ли можно назвать оптимальным, однако оно показывает, что данные из IoT Central можно успешно обрабатывать внешними средствами, в т.ч., например, Power BI.

Выводы

Используя облачные службы в модели SaaS, мы в короткие сроки и без написания кода на стороне облака реализовали решение по отслеживанию состояния цифровых инфокиосков.

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

В данный момент мы продолжаем улучшать решение. Вот что, как мы считаем, можно улучшить:

  • Применять сертификаты X.509 для подключения устройств;
  • Более безопасное обновление приложения-клиента по команде с IoT Central;
  • Оптимизация интерфейсов DCM для отправки меньшего количества телеметрии;
  • Применение IoT Edge для кэширования данных при отсутствии интернет-соединения. IoT Edge – это управляемый сервис, который позволяет перенести часть нагрузок из облака на так называемый «край», т.е. границу наземной части решения. IoT Edge также имеет богатые возможности кэширования данных;
  • Собственная визуализация состояния киосков, хотя это, возможно, потребует переработки решения на SaaS.

Обязательно возникнет вопрос, во сколько обходится решение. И хотя ответ на этот вопрос в общем случае довольно объемный, будем краткими и приведем чисто практический результат: IoT Central вместе со всеми использованными облачными службами в тестовой эксплуатации обходится примерно в 50 рублей на одно устройство в месяц. Мы стремимся улучшить данный показатель путем оптимизации количества телеметрии.

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

Хотя наша статья имеет обзорный характер, мы старались раскрыть важные детали и тонкие места. Ждем ваших вопросов и отзывов.


Автор статьи — Сергей Антонович, ведущий инженер Кварта Технологии. Связаться с ним можно по адресу sergant (at) quarta.ru. Статья опубликована в блоге компании на Хабр.