HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
d-shop
HimeraSearchDB

НОВОСТИ [Из песочницы] Создание и настройка Mesh-сети на собственном опыте, а также немного цифр и аналитики

Bonnie
Оффлайн
Регистрация
12.04.17
Сообщения
19.095
Реакции
107
Репутация
0
Все началось с того, что на очередном обсуждении дальнейшей судьбы проекта, над которым я тогда работал, кто-то сказал: «А давайте прикрутим меш-сети, ведь это круто, модно и молодёжно!». И именно с этого момента началась моя неравная борьба с меш-сетями, из которой мы с товарищем вышли победителями. Хоть и с небольшой оговоркой.


Итак, что у нас имелось на текущий момент, так это несколько умных устройств (на базе nrf52840), уже умевших общаться с телефоном по «блютусу», странная среда разработки и я, абсолютно непонимающий, что такое меш-сети. То, как я неделю колдовал с бубном, чтобы arm-овский компилятор съел NRF mesh SDK – это отдельная история. Но после поражения в этой битве и переход на GCC дела пошли быстрее.



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


На каждом узле (железке) меш-сети должна находиться модель. Модель – это сущность, которой провижионер присваивает уникальный адрес, и которая может, если она клиент – отправлять запросы, а если сервер, то отвечать на них. Для моей задачи требовалось написать такой код, что модель могла бы и принимать сообщения и отправлять их сама. В принципе, никто не запрещает вам на одной железке создать несколько моделей с разными адресами, одна из которых будет отвечать за передачу сообщений, а другая за прием. Единственное, о чем вам стоит помнить, что конфигурация узла происходит в полуручном режиме. То есть вы не можете написать в коде провижионера: if (state == Configure) {makegood();}. К сожалению – это ограничение Mesh SDK и нам нужно каждую сущность, которая «сидит» у нас на железке конфигурировать вручную, то есть, хардкодом. Благо есть примеры из SDK. Однако идущее в комплекте приложения под android уже становится бесполезным, так как оно тоже ожидает на вашем устройстве определенные сущности и не сможет с ними работать, если они отличаются от сущностей из примеров. Но стоит отметить, что сами примеры хорошие и вы можете без особого труда посмотреть, как происходит процесс провижионинга и конфигурирования.


Кстати, я говорил, что на вашем узле может располагаться несколько сущностей и по умолчанию, у вас уже будет находиться health-client/server, config-client/server. Health-client находится на провижионере и отвечает за опрос health-server на обычных узлах. Этот уровень абстракции позволяет провижионеру контролировать состояние сети, кто жив, а кто мертв. Между счетчиками постоянно будут летать сообщения с подобными опросами. Config-client также находится на провижионере и его задача просто последовательно выполнить те команды конфигурации, которые вы ему задали изначально. Никакой гибкости здесь не предполагается – что написано один раз, будет выполняться для каждого узла независимо от внешних параметров системы. Аналогично, config-server находится на стандартных узлах и его цель – отвечать на запросы клиентов.


Итак, когда основные моменты рассмотрены, давайте перейдем к архитектуре. Я в то время находился под сильным впечатлением от прочтения Code Complete, да и уровень сокомандников не позволял написать откровенный говнокод, и тогда я решил акцентировать внимание на подобии архитектуры, а не просто запихнуть весь код в один мега-класс. Ну, или сделал вид.

Вот что из этого получилось:


  • У нас есть класс BLEMesh, у которого есть методы Send и Receive, соответственно для приема и отправки сообщений по меш-сети. Также, есть метод SetGroup, с помощью которого можно выставить, к какой группе принадлежит узел и StartProvisioning, после которого, узел становился провижионером и начинал создавать сеть, последовательно добавляя в нее всех соседей.
  • Чтобы не засорять главный интерфейс реализацией, а уж тем более обращениями к SDK, был создан класс BLEMEshImpl. Инстанс этого класса являлся полем BLEMesh, который, в свою очередь, просто обращался к нему и вызывал соответствующие методы. Здесь были реализованы методы по инициализации и запуску всего стека BLE и BLE Mesh, а также проставлялось куча колбеков на разные случаи жизни. И также здесь же инициализировалась третья сущность – модель.
  • Модель в моем случае представляла из себя класс, отвечающий только за передачу и прием данных по меш-сети. При попытке отправить сообщение, модель проверяет, содержится ли в сообщении уникальный адрес или адрес группы и в зависимости от этого, вызывает разные функции из SDK. Если уникальный адрес не выставлен – сообщение отправляется в группу, то есть всем.

xdqb61rdsfxxcbah2igppsynm8u.png


Также, уже изначально была заложена возможность покрытия всего этого шедевра юнит-тестами, а каждый класс был унаследован от интерфейса с приставкой I. И обращение к этим классам шло исключительно через него. Это нужно было сделать, чтобы в последствии безболезненно замокать обращения к Mesh SDK.


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

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


Третий пункт плавно вытекает из нашей задачи: прикрутить BLE Mesh к уже работающему девайсу. А ведь на нем уже был написан код, позволяющий общаться с ним по блютус с телефона. Как же нам совместить два таких разных SDK? Хорошо, что в комплекте с SDK шел пример co-exist, позволивший немного упростить нашу задачу. Почему немного, а потому что она до сих пор не была нами решена. Поколдовав с настройками и приоритетами, мы пришли к следующему. Если устройство когда-то было спэйрино с телефоном, но на телефоне были удалены настройки, то повторный пейринг не проходит. Скорее всего это проблема конкретно нашей реализации, но пока это лечится только руками – отключение пересылки событий SoftDevice в меш стек как описано . Костыли наше все. Во всех остальных случаях нет никаких проблем с пейрингом устройства и приложения, установлением подключения и работы с Mesh в роли профижионера или профижиони. Может быть в дальнейшем получиться исправить эту проблему, и я добавлю заветный UPD.


Ну и настало время аналитик, которую мы делали для себя. Моим коллегой был собран следующий тестовый стенд: четыре узла, один из которых подключен проводом к ПК (через переходник USB-UART). Он будет являться провижионером в нашей сети, а также именно с него будут уходить все запросы в сеть. Также, у нас есть планшет, с помощью которого мы будем измерять помехи в работе меш-сети, при использовании обычного блютуса. Ну и на ПК используется модифицированная программа modpoll, которая реализует тестовые сценарии и выводит в файл результаты измерений. В программе реализовано измерение круговой задержки (RTT) — интервала времени между отправкой запроса и получением ответа. Есть ограничение для круговой задержки — все что больше 1000 мс считается потерей.

irepjg94jhv1cgnu4pgkuccjkww.png



Очевидно, что при совместном сосуществовании BLE и Mesh невозможно обойтись без потерь. В наше случае мы рассматривали простейший случай, когда провижионер опрашивает одного провижиони без пересылок. Для оценки выполнялось 100 запросов подряд.

В «1 case» ни одно устройство не подключено к приложению, в «2 case» провижиони подключен к приложению и имеет место некоторый трафик через BLE, а в «3 case» провижионер подключен к приложению. Первый график показывает распределение потерь, второй это распределение круговой задержки. Как мы видим из первого графика — работа провижионера на «два фронта» обходится в +5% к потерям. В случае с быстродействием получается, что, когда установлено соединение, снижается нагрузка на стек BLE (прекращается сканирование эфира) и обработка событий Mesh происходит быстрее.

kl5iyoijbhql66flofoyiqitgom.png


p_sl-01mg4tfj9kr_939njmgrya.png



Надеюсь, этот короткий обзор Mesh-сетей и их производительность был вам полезен и интересен. Еще увидимся!
 
Сверху Снизу