Данные считываемые из регистров Modbus перед передачей в облако для анализа и визуализации нередко требуют предварительной обработки. Например, в случае с термодатчиком предыдущих статьях необходимо разделить полученные значения с датчика на 10.
Архтитектура модулей в IoT Edge Runtime построена на микросервисной архитектуре. Каждый модуль, формально, ничего не знает о том, что данные на его вход поступают с предыдущего модуля. Он делает обработку входящего потока данных по некоторому алгоритму («черный ящик») и передает его на выход.
При этом сам модуль не подозревает, принимает ли модифицированный им поток данных следующий модуль или они уходят сразу в облако IoT Hub. Т.е. внутренние алгоритмы работы каждого такого «черного ящика», формально, не должны зависеть от предыдущего модуля.

Настройки маршрутизации заданные в конфигурации определяют последовательность обработки данных модулями. В идеале, если обеспечена работа модулей, как «черных ящиков», они должны с легкостью допускать изменение порядка. Но, понятно, что это в идеале. По факту-же каждый модуль должен представлять, что за данные к нему приходят и в каком формате, чтобы производить обработку.
Сборка модулей ABB Modbus
Для проверки как отрабатывает цепочка, воспользуемся модулем разработанным Maxim Khlupnov: https://github.com/MaxKhlupnov/SmartHive.AbbEdge. Соберем проект из исходников:
- Создаем клон исходников с GitHub.
- В папку ABBEdge копируем файл .env и .gitignore из проекта созданного с «чистого листа» (см. статью «Часть 6»).
- Вписываем в файл deployment.template.json информацию для авторизации в Azure Containers Registry. Можно затереть секции «docker» и «smarthive», поскольку мы будем создавать образ модуля в нашем собственном Azure Containers Registry. В registryCredentials останется только строчки с именем вашего registry. В моем случае warlibregistry с адресом warlibregistry.azurecr.io:
"registryCredentials": {"warlibregistry": {"username":"$CONTAINER_REGISTRY_USERNAME_warlibregistry","password":"$CONTAINER_REGISTRY_PASSWORD_warlibregistry","address":"warlibregistry.azurecr.io"}} - Заменяем в секции modules -> modbus адрес образа с
"image":"microsoft/azureiotedge-modbus-tcp:GA-preview-amd64"на адрес из статьи «Часть 6» или адрес репозитория Microsoft (mcr.microsoft.com/azureiotedge/modbus:1.0):
"image":"warlibregistry.azurecr.io/iotedgemodbus:0.0.1-amd64" - Заходим в подпапку: SmartHive.AbbEdge\ABBEdge\modules\abbDriveProfile\
- Находим файл modules.json и заменяем там
"repository":"smarthive.azurecr.io/abb-drive-profile"адрес на
"repository":"warlibregistry.azurecr.io/abb-drive-profile" - Можно обновить файлы проекта до текущего TargetFramework. Для этого в файлах проекта *.csproj в соответствующих папках меняем framework с netcoreapp2.0 на последний на сегодня: netcoreapp2.1. Аналогичную операцию надо сделать и в Dockerfile.* Для Dockerfile.* недостаточно заменить цифру 2.0 на 2.1, поскольку названия framework после цифры отличаются. Проще скопировать все файлы из проекта «с чистого листа», заменив ENTRYPOINT на верный.
- Аналогичную замену делаем и в файле SmartHive.AbbEdge\AbbEdge\modules\abbRemoteMonitoringGateway\module.json.
- В Visual Studio Code открываем Workspace: File -> Open workspace… -> AbbEdge.code-workspace (в корне папки).
- Кликаем правой клавишей мыши на deployment.template.json и запускаем «Build and Push IoT Edge Solution».
- Если build & push прошел без ошибок, то в Azure Containers Registry появятся образы abbremotemonitoringgateway и abb-drive-profile.
Настройки Modbus термодатчика
- Создаем новое тестовое IoT Edge устройство. Например, с именем TermoSensorWithConverter.
- Добавляем modbus модуль как описано в статье: «Часть 5«. Даем имя модулю modbus.
- Добавляем модуль ABBDriveProfile как описано в статье Максима. Путь правим на свой (в моем случае «warlibregistry.azurecr.io»):
Name - abbDriveProfileImage URI - warlibregistry.azurecr.io/abb-drive-profile:0.0.1-amd64 - В properties.desired секция SignalConfig для термодатчика будет выглядеть так:
{"properties.desired": {"SignalConfigs": {"Temp": {"ValueFormula":"Temp / 10.0","ValueType":"Double","ValueUnit":"oC"},"Humidity": {"ValueFormula":"Humidity / 10.0","ValueType":"Double","ValueUnit":"%"}}}} - Для визуализации сконвертированных в правильный вид данных достаточно модуля modbus и abbDriveProfile. Маршрут для передачи данных в IoT Hub будет выглядеть следующим образом:
{"routes": {"modbusToAbbAcsEdgeProfile":"FROM /messages/modules/modbus/outputs/modbusOutput INTO BrokeredEndpoint(\"/modules/abbDriveProfile/inputs/driveProfileInput\")","abbDriveProfileToIoTHub":"FROM /messages/modules/abbDriveProfile/outputs/driveProfileOutput INTO $upstream"}} - Завершим добавление модулей для IoT Edge устройства TermoSensorWithConverter.
- На локальном ПК развернем IoT Edge Runtime передав в качестве параметра connection string от TermoSensorWithConverter.
- Посмотрим логи abbDriveProfile командой «iotedge logs abbDriveProfile -f»:
Drive profile module - Received messageReceived message: 64, Body: [{"PublishTimestamp":"2018-11-19 09:01:40","Content":[{"HwId":"TermoSensor-0a:01:01:01:01:01","Data":[{"CorrelationId":"DefaultCorrelationId","SourceTimestamp":"2018-11-19 09:01:32","Values":[{"DisplayName":"Humidity","Address":"30003","Value":"393"},{"DisplayName":"Temp","Address":"30002","Value":"262"}]}]}]}]Temp : 262Result: 26.2Humidity : 393Result: 39.3signals processed: 2Telemetry message succesfully sent: [{"HwId":"TermoSensor-0a:01:01:01:01:01","SourceTimestamp":"2018-11-19 09:01:32","Name":"Temp","Value":"26.2","ValueType":"Double","ValueUnit":"oC"},{"HwId":"TermoSensor-0a:01:01:01:01:01","SourceTimestamp":"2018-11-19 09:01:32","Name":"Humidity","Value":"39.3","ValueType":"Double","ValueUnit":"%"}] - Видно, что модуль abbDriveProfile корректно принимает информацию с Modbus, производит необходимые вычисления и отправляет в IoT Hub преобразованные данные в JSON.
- Отметим, что если посмотреть командой «iotedge logs modbus -f» логи с модуля modbus, то там идет построчный вывод информации о номере регистра modbus и температуре:
30002: 26030003: 34830002: 26030003: 348Модуль-же abbDriveProfile приниает на вход JSON с детальной информацией о том, какой датчик отправил информацию, символическое имя регистра, значение. Видно, что информация в логах отличается от информации, которая фактически транслируется на вход:
{"PublishTimestamp":"2018-11-19 09:01:40","Content":[{"HwId":"TermoSensor-0a:01:01:01:01:01","Data":[{"CorrelationId":"DefaultCorrelationId","SourceTimestamp":"2018-11-19 09:01:32","Values":[{"DisplayName":"Humidity","Address":"30003","Value":"393"},{"DisplayName":"Temp","Address":"30002","Value":"262"}]}]}]}
Тестирование
Чтобы посмотреть, что реально принимает IoT Hub от устройства воспользуемся консольным приложением из статьи. Пример работы приложения в документации на сайте Microsoft.
Отмечу, что если идет отправка в IoT Hub информаци с нескольких IoT Edge устройств, то все эти потоки данных смешиваются и отображаются в приложении по мере поступления.
Для анализа логов по каждому устройству есть приложение из инструментария работы с IoT Hub: DeviceExplorer. Но в логах этого приложения отображается только JSON, который был передан устройством без служебной информации. Поэтому его не удобно использовать, например, при написании QUERY для Stream Analytics Job.