Прежде чем начинать разрабатывать свои модули для IoT Edge откомпилируем и развернем модуль датчика температуры и влажности разработанный Microsoft. С ним проводили эксперименты ранее.
В Интернете есть два варианта модуля:
- https://github.com/Azure/iotedge/tree/master/edge-modules/SimulatedTemperatureSensor
- https://github.com/Azure/iot-edge-v1/tree/master/v2/samples/azureiotedge-simulated-temperature-sensor
Код первого примера на момент написания статьи менялся 21 день назад, т.е. более свежий. Код находится в папке относящейся к коду сервиса IoT Edge.
Второй код более сложный, но читабельнее, хотя изменения по нему проводились более 9 месяцев назад. На него есть ссылка с Microsoft Market:
С него есть ссылка на сайт Microsoft по сборке модуля https://docs.microsoft.com/en-us/azure/iot-edge/how-to-develop-csharp-module.
На странице https://docs.microsoft.com/en-us/azure/iot-edge/tutorial-csharp-module приведена информация по разработке модуля. Куски кода там из второго примера. Так что в статье используем эти исходники.
В части 4 повествования я вынес находки, которые будут полезны при отладке модуля. В идеале, не понадобится.
Termo sensor IoT Edge module
Прежде чем разрабатывать что-то своё, модифицируем Microsoft модуль Termo Sensor.
С созданием контейнера Azure все понятно. На облачном ресурсе выделяется виртуальное хранилище в котром лежат образы модулей, пригодные для развертывании на IoT Edge устройствах. Образ модуля в любом месте где есть Интернет можно накатить на устройство из облачного репозитория.
Есть два варианта собрать из исходников с GitHub рабочий модуль:
- Добавить в исходники с Git недостающий файл .env и поправить deployment.template.json.
- Создать новый проект IoT модуля и скопировать в него исходники.
Я разберу второй подход, поскольку структура папок исходников отличается от варианта, формируемого при создании проекта «с чистого листа». Например, нет файла modules.json. Вместо него папка Docker. Нет файла deployment.template.json и т.п. В общем, в таком случае быстрее скопировать *.cs файлы.
Первый способ будет продемонстрирован в следующей статье при сборке другого модуля.
- В Visual Studio Code создаем с чистого листа IoT Edge модуль, используя последовательность из документации и мои рекомендации ниже и из tips & tricks.
- На Azure portal, путь для создания своего контейнера: Create a resource > Containers >
AzureContainer Registry. - Имя для контейнера общее для всех участников, соотвественно, нужно придумать что-то достаточно уникальное. Например, я выбрал название WarlibRegistry, поэтому путь к серверу: warlibregistry.azurecr.io.
- Ключи доступа к созданному container repository можно найти на Azure portal: All resources -> [WarlibRegistry] -> Access Keys. Если Admin users в Enable, то отобразятся имя пользователя и пароль.
- При выполнении команды Visual Studio Code для создания нового модуля View -> Command Palette.. -> New IoT Edge Solution:
- Запрос на выбора папки — это корневая папка на локальном диске в котором будет создан solution.
- Solution name — в выбранной папке будет создана подпапка с заданным именем решения. Например, iot_edge_engine_simulator. Допускаются только символы нижнего подчеркивания.
- Module name — имя модуля. Например, iot_edge_engine_simulator. Допускаются только символы нижнего подчеркивания.
- Docker image repository — репозиторий Azure/Docker. Например, warlibregistry.azurecr.io/iot-edge-engine-simulator.
- Скачиваем с Git Hub https://github.com/Azure/iot-edge-v1 исходники модуля. Команда:
git clone https://github.com/Azure/iot-edge-v1.git
- Копируем все *.cs файлы из папки …\iot-edge-v1\v2\samples\azureiotedge-simulated-temperature-sensor\*.cs в папку вновь созданного модуля.
- В Visual Studio Code отобразятся все добавленные *.cs файлы.
- Правим (копируем нижеприведенный вариант) deployment.template.json в созданном модуле, убрав лишнее. В результате получится:
{ "modulesContent": { "$edgeAgent": { "properties.desired": { "schemaVersion": "1.0", "runtime": { "type": "docker", "settings": { "minDockerVersion": "v1.25", "loggingOptions": "", "registryCredentials": { "warlibregistry": { "username": "$CONTAINER_REGISTRY_USERNAME_warlibregistry", "password": "$CONTAINER_REGISTRY_PASSWORD_warlibregistry", "address": "warlibregistry.azurecr.io" } } } }, "systemModules": { "edgeAgent": { "type": "docker", "settings": { "image": "mcr.microsoft.com/azureiotedge-agent:1.0", "createOptions": "{}" } }, "edgeHub": { "type": "docker", "status": "running", "restartPolicy": "always", "settings": { "image": "mcr.microsoft.com/azureiotedge-hub:1.0", "createOptions": "" } } }, "modules": { "iot_edge_engine_simulator": { "version": "1.0", "type": "docker", "status": "running", "restartPolicy": "always", "settings": { "image": "${MODULES.iot_edge_engine_simulator.amd64}", "createOptions": "{}" } } } } }, "$edgeHub": { "properties.desired": { "schemaVersion": "1.0", "routes": { "route": "FROM /* INTO $upstream" }, "storeAndForwardConfiguration": { "timeToLiveSecs": 7200 } } } } }
- В командной строке или терминале Visual Studio Code авторизуем docker для доступа к Azure Container Registry:
docker login -u <ACR username> -p <ACR password> <ACR login server>
- В Program.cs нужно внести ряд изменений, чтобы сборка произошла корректно:
- В *.csproj указан <LangVersion>7.1</LangVersion>. В файле проект с «чистого листа» этого указания нет, поэтому при сборке возникнет ошибка метода Main.
- Для компиляции нужно либо добавить версию,
<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.1</TargetFramework> <LangVersion>7.3</LangVersion> </PropertyGroup>
либо изменить исходник Program.cs следующим образом:
//Andrey Fedorov Lang version 7.1 needed if //Andrey Fedorov static async Task Main(string[] args) static void Main(string[] args) { // The Edge runtime gives us the connection string we need -- it is injected as an environment variable var connectionString = Environment.GetEnvironmentVariable("EdgeHubConnectionString"); // Cert verification is not yet fully functional when using Windows OS for the container var bypassCertVerification = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); bypassCertVerification = true; //Andrey Fedorov new line if (!bypassCertVerification) InstallCert(); //await Init(connectionString, bypassCertVerification); //Andrey Fedorov Lang version 7.1 needed Init(connectionString, bypassCertVerification).Wait(); //Andrey Fedorov change // Wait until the app unloads or is cancelled var cts = new CancellationTokenSource(); AssemblyLoadContext.Default.Unloading += (ctx) => cts.Cancel(); Console.CancelKeyPress += (sender, cpe) => cts.Cancel(); //await WhenCancelled(cts.Token); //Andrey Fedorov Lang version 7.1 needed // Wait until the app unloads or is cancelled WhenCancelled(cts.Token).Wait(); //Andrey Fedorov My addition }
- Пока в исходниках убрал проверку сертификата, указав bypassCertVerification = true.
- Заменил DeviceClient на ModuleClient. В исходниках Microsoft используется старая сборка 1.6 preview DeviceClient. Возможно, тогда не было класса ModuleClient. Для разработки модулей используется ModuleClient, а для разработки устройств с прямым доступом к IoT Hub — DeviceClient.
- Добавил проверку на Connection string, поскольку при запуске исходника от Microsoft передается пустая строка. В «модуле с чистого листа» Connection string не используется.
// Open a connection to the Edge runtime //var ioTHubModuleClient = DeviceClient.CreateFromConnectionString(connectionString, settings); //Andrey Fedorov ModuleClient ioTHubModuleClient = null; //Andrey Fedorov if (string.IsNullOrEmpty(connectionString)) //Andrey Fedorov { ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings); //Andrey Fedorov } else //Andrey Fedorov ioTHubModuleClient = ModuleClient.CreateFromConnectionString(connectionString, settings);
- В Visual Studio Code встаем на deployment.template.json, нажимаем правую клавишу мыши и выбираем Build and Push IoT Edge solution. Запустится процесс сборки модуля, упаковки в контейнер Docker и отправки его в репозиторий Azure.
- Если сборка прошла удачно, то в Azure portal в Containers repository появится образ с именем указанным в deployment.json.
- В IoT Hub:
- Создаем новый IoT Edge device (All resources -> [Ваш IoT Hub] -> IoT Edge -> Add an IoT Edge Device -> Задаем имя устройства, больше ничего менять не нужно.
- Выбираем созданное устройство.
- Добавляем модуль, выбрав пункт Set Module.
- ОБЯЗАТЕЛЬНО заполняем параметры «Container Registry Settings» (см. лайфаки).
- В разделе «Deployment modules» -> Add -> IoT Edge module:
- В «Name» задаем имя модуля. Например, у меня engineSimulator.
- В «Image URI» указываем путь к образу в репозтитории. Например, у меня warlibregistry.azurecr.io/iot-edge-engine-simulator:0.0.1-amd64.
- Другие настройки можно оставить без изменений.
- Жмем кнопку Next для заполнения роутинга. В deployment.json прописан простой путь
"route": "FROM /* INTO $upstream"
- Затем снова Next и Submit.
- Модуль для устройства создан. Можно развертывать его на устройстве.
- На ПК, выполняющем функции IoT Edge устройства:
- В PowerShell запущенном под Администратором выполняем команду:
. {Invoke-WebRequest -useb aka.ms/iotedge-win} | Invoke-Expression; ` Install-SecurityDaemon -Manual -ContainerOs Linux
- Установка и развертывание IoT Edge runtime может занять несколько минут в зависимости от скорости Интернет.
- После успешного развертывания ждем немного, поскольку модули появляются не сразу. Проверяем, что все модули «приехали» командой:
iotedge list
Если собранный модуль в списке, то уже хорошо.
- Запускаем команду
iotedge logs [engineSimulator] -f
чтобы посмотреть какие данные выдает в консоль собранный модуль.
- Если в консоли появляются строки с данными по температуре и влажности, развертывание прошло успешно, все работает.
- Если в консоль валятся сообщения об ошибках — используем команды из раздела tips & tricks для выявления причин.
- В PowerShell запущенном под Администратором выполняем команду:
- Если в All resources -> [Iot Hub] -> IoT Edge -> [Your IoT device] -> [Your IoT module] -> Module Identity Twin вписать в секцию properties -> desired:
... "properties": { "desired": { "SendData": true, "SendInterval": 7, ...
то на «родном» модуле из контейнера Microsoft на это не будет никакой реакции. В модуле, который откомпилирован из исходников с github после перезапуска iotedge появятся строчки:
Updating desired properties { "SendData": true, "SendInterval": 7, "$version": 2 } Value for SendData = True Value for SendInterval = 7
Это говорит о том, что код исходника полноценнее реализует функционал IoT Hub/Edge, чем готовый модуль Microsoft.
- Если в All resources -> [Iot Hub] -> IoT Edge -> [Your IoT device] -> [Your IoT module] -> Direct method -> Methiod name -> Ввести «reset» без кавычек -> Нажать «Invoke method», то в логе на IoT Edge device появляется запись:
Received reset command via direct method invocation Resetting temperature sensor...
После чего записи начинается с единицы. Т.е. функционал вызова direct метода с клиента отрабатывает нормально.
Рабочие исходники.