Данные считываемые из регистров 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 - abbDriveProfile
Image 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 message
Received 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 : 262
Result: 26.2
Humidity : 393
Result: 39.3
signals processed: 2
Telemetry 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: 260
30003: 348
30002: 260
30003: 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.