Руководство. Визуализация данных устройства Интернета вещей из Центр Интернета вещей с помощью службы Azure Web PubSub и Функции Azure
В этом руководстве вы узнаете, как использовать службу Azure Web PubSub и Функции Azure для создания бессерверного приложения с визуализацией данных в режиме реального времени из Центр Интернета вещей.
В этом руководстве описано следующее:
- Создание приложения визуализации бессерверных данных
- Совместная работа с входными и выходными привязками функции Web PubSub и Центром Интернета вещей Azure
- Локальный запуск примера функций
Внимание
Необработанные строка подключения отображаются в этой статье только для демонстрационных целей.
Строка подключения содержит сведения об авторизации, требуемые для доступа приложения к службе Azure Web PubSub. Ключ доступа в строке подключения аналогичен паролю привилегированного пользователя для службы. В рабочих средах всегда защищать ключи доступа. Используйте Azure Key Vault для безопасного управления ключами и защиты подключения.WebPubSubServiceClient
Старайтесь не распространять ключи доступа среди других пользователей, жестко программировать их или где-то сохранять в виде обычного текста в открытом доступе для других пользователей. Меняйте свои ключи постоянно, если предполагаете, что они могут быть подобраны.
Необходимые компоненты
Редактор кода, например Visual Studio Code.
Node.js версии 18.x или более поздней.
Примечание.
Дополнительные сведения о поддерживаемых версиях Node.js см. в документации по версиям среды выполнения Функций Azure.
Azure Functions Core Tools (желательно версии 3 или более поздней версии) для запуска приложений-функций Azure в локальной среде и их развертывания в Azure.
Azure CLI для управления ресурсами Azure.
Если у вас еще нет подписки Azure, создайте бесплатную учетную запись Azure, прежде чем начинать работу.
Создание Центра Интернета вещей
В этом разделе описано, как использовать Azure CLI для создания центра Интернета вещей и группы ресурсов. Группа ресурсов Azure является логическим контейнером, в котором происходит развертывание ресурсов Azure и управление ими. Центр Интернета вещей действует в качестве центра сообщений для двусторонней связи между приложением Интернета вещей и устройствами.
Если у вас уже есть центр Интернета вещей в подписке Azure, этот раздел можно пропустить.
Чтобы создать центр Интернета вещей и группу ресурсов, выполните следующие действия:
Запустите приложение CLI. Чтобы выполнить команды CLI в остальной части этой статьи, скопируйте синтаксис команды, вставьте его в приложение CLI, измените значения переменных и нажмите клавишу
Enter
.- При использовании Cloud Shell нажмите кнопку Попробовать в командах интерфейса командной строки, чтобы запустить Cloud Shell в разделенном окне браузера. Или можно открыть Cloud Shell в отдельной вкладке браузера.
- Если вы используете Azure CLI локально, запустите консольное приложение CLI и войдите в Azure CLI.
Запустите az extension add, чтобы установить или обновить расширение azure-iot до текущей версии.
az extension add --upgrade --name azure-iot
В приложении CLI выполните команду az group create, чтобы создать группу ресурсов. В следующей команде создается группа ресурсов с именем MyResourceGroup в расположении eastus.
Примечание.
При необходимости можно задать другое расположение. Чтобы отобразить доступные расположения, выполните команду
az account list-locations
. В рамках работы с этим кратким руководством используется eastus, как показано в примере команды.az group create --name MyResourceGroup --location eastus
Создайте Центр Интернета вещей с помощью команды az iot hub create. Создание Центра Интернета вещей может занять несколько минут.
YourIotHubName Замените этот заполнитель и окружающие фигурные скобки в указанной ниже команде именем своего центра Интернета вещей. Имя центра Интернета вещей должно быть уникальным по всему Azure. Используйте имя центра Интернета вещей при работе с оставшейся частью этого краткого руководства везде вместо заполнителя.
az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
Создание экземпляра Web PubSub
Если у вас уже есть экземпляр Web PubSub в подписке Azure, можно пропустить этот раздел.
Запустите az extension add to install or upgrade the webpubsub extension to the current version.
az extension add --upgrade --name webpubsub
Используйте команду azure CLI az webpubsub create , чтобы создать web PubSub в созданной группе ресурсов. Следующая команда создает ресурс Free Web PubSub в группе ресурсов myResourceGroup в EastUS:
Внимание
Каждый ресурс Web PubSub должен иметь уникальное имя. В следующих примерах замените <your-unique-resource-name> именем своей службы Web PubSub.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
В выходных данных команды будут показаны свойства созданного ресурса. Запишите значения двух указанных ниже свойств.
-
Имя ресурса: имя, которое вы ввели для указанного выше параметра
--name
. -
hostName: в примере имя узла
<your-unique-resource-name>.webpubsub.azure.com/
.
На данном этапе любые операции в этом новом ресурсе могут выполняться только с использованием вашей учетной записи Azure.
Создание и запуск функций в локальной среде
Создайте пустую папку для проекта и выполните следующую команду в новой папке.
func init --worker-runtime javascript --model V4
Создайте функцию
index
для чтения и размещения статической веб-страницы для клиентов.func new -n index -t HttpTrigger
Обновите
src/functions/index.js
следующий код, который служит HTML-содержимому в качестве статического сайта.const { app } = require('@azure/functions'); const { readFile } = require('fs/promises'); app.http('index', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: async (context) => { const content = await readFile('index.html', 'utf8', (err, data) => { if (err) { context.err(err) return } }); return { status: 200, headers: { 'Content-Type': 'text/html' }, body: content, }; } });
Создайте файл в корневой
index.html
папке.<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0/dist/Chart.min.js" type="text/javascript" charset="utf-8"></script> <script> document.addEventListener("DOMContentLoaded", async function (event) { const res = await fetch(`/api/negotiate?id=${1}`); const data = await res.json(); const webSocket = new WebSocket(data.url); class TrackedDevices { constructor() { // key as the deviceId, value as the temperature array this.devices = new Map(); this.maxLen = 50; this.timeData = new Array(this.maxLen); } // Find a device temperature based on its Id findDevice(deviceId) { return this.devices.get(deviceId); } addData(time, temperature, deviceId, dataSet, options) { let containsDeviceId = false; this.timeData.push(time); for (const [key, value] of this.devices) { if (key === deviceId) { containsDeviceId = true; value.push(temperature); } else { value.push(null); } } if (!containsDeviceId) { const data = getRandomDataSet(deviceId, 0); let temperatures = new Array(this.maxLen); temperatures.push(temperature); this.devices.set(deviceId, temperatures); data.data = temperatures; dataSet.push(data); } if (this.timeData.length > this.maxLen) { this.timeData.shift(); this.devices.forEach((value, key) => { value.shift(); }) } } getDevicesCount() { return this.devices.size; } } const trackedDevices = new TrackedDevices(); function getRandom(max) { return Math.floor((Math.random() * max) + 1) } function getRandomDataSet(id, axisId) { return getDataSet(id, axisId, getRandom(255), getRandom(255), getRandom(255)); } function getDataSet(id, axisId, r, g, b) { return { fill: false, label: id, yAxisID: axisId, borderColor: `rgba(${r}, ${g}, ${b}, 1)`, pointBoarderColor: `rgba(${r}, ${g}, ${b}, 1)`, backgroundColor: `rgba(${r}, ${g}, ${b}, 0.4)`, pointHoverBackgroundColor: `rgba(${r}, ${g}, ${b}, 1)`, pointHoverBorderColor: `rgba(${r}, ${g}, ${b}, 1)`, spanGaps: true, }; } function getYAxy(id, display) { return { id: id, type: "linear", scaleLabel: { labelString: display || id, display: true, }, position: "left", }; } // Define the chart axes const chartData = { datasets: [], }; // Temperature (ºC), id as 0 const chartOptions = { responsive: true, animation: { duration: 250 * 1.5, easing: 'linear' }, scales: { yAxes: [ getYAxy(0, "Temperature (ºC)"), ], }, }; // Get the context of the canvas element we want to select const ctx = document.getElementById("chart").getContext("2d"); chartData.labels = trackedDevices.timeData; const chart = new Chart(ctx, { type: "line", data: chartData, options: chartOptions, }); webSocket.onmessage = function onMessage(message) { try { const messageData = JSON.parse(message.data); console.log(messageData); // time and either temperature or humidity are required if (!messageData.MessageDate || !messageData.IotData.temperature) { return; } trackedDevices.addData(messageData.MessageDate, messageData.IotData.temperature, messageData.DeviceId, chartData.datasets, chartOptions.scales); const numDevices = trackedDevices.getDevicesCount(); document.getElementById("deviceCount").innerText = numDevices === 1 ? `${numDevices} device` : `${numDevices} devices`; chart.update(); } catch (err) { console.error(err); } }; }); </script> <style> body { font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; padding: 50px; margin: 0; text-align: center; } .flexHeader { display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: space-between; } #charts { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-around; align-content: stretch; } .chartContainer { flex: 1; flex-basis: 40%; min-width: 30%; max-width: 100%; } a { color: #00B7FF; } </style> <title>Temperature Real-time Data</title> </head> <body> <h1 class="flexHeader"> <span>Temperature Real-time Data</span> <span id="deviceCount">0 devices</span> </h1> <div id="charts"> <canvas id="chart"></canvas> </div> </body> </html>
Создайте функцию, используемую
negotiate
клиентами для получения URL-адреса подключения службы и маркера доступа.func new -n negotiate -t HttpTrigger
Обновите для
src/functions/negotiate.js
использованияWebPubSubConnection
, содержащего созданный маркер.const { app, input } = require('@azure/functions'); const connection = input.generic({ type: 'webPubSubConnection', name: 'connection', hub: '%hubName%' }); app.http('negotiate', { methods: ['GET', 'POST'], authLevel: 'anonymous', extraInputs: [connection], handler: async (request, context) => { return { body: JSON.stringify(context.extraInputs.get('connection')) }; }, });
messagehandler
Создайте функцию для создания уведомлений с помощью"IoT Hub (Event Hub)"
шаблона.Необработанные строка подключения отображаются в этой статье только для демонстрационных целей. В рабочих средах всегда защищать ключи доступа. Используйте Azure Key Vault для безопасного управления ключами и защиты подключения.
WebPubSubServiceClient
func new --template "Azure Event Hub trigger" --name messagehandler
Обновите
src/functions/messagehandler.js
, чтобы добавить выходную привязку Web PubSub со следующим кодом json. Мы используем переменную%hubName%
в качестве имени концентратора для центра IoT eventHubName и Центра Web PubSub.const { app, output } = require('@azure/functions'); const wpsAction = output.generic({ type: 'webPubSub', name: 'action', hub: '%hubName%' }); app.eventHub('messagehandler', { connection: 'IOTHUBConnectionString', eventHubName: '%hubName%', cardinality: 'many', extraOutputs: [wpsAction], handler: (messages, context) => { var actions = []; if (Array.isArray(messages)) { context.log(`Event hub function processed ${messages.length} messages`); for (const message of messages) { context.log('Event hub message:', message); actions.push({ actionName: "sendToAll", data: JSON.stringify({ IotData: message, MessageDate: message.date || new Date().toISOString(), DeviceId: message.deviceId, })}); } } else { context.log('Event hub function processed message:', messages); actions.push({ actionName: "sendToAll", data: JSON.stringify({ IotData: message, MessageDate: message.date || new Date().toISOString(), DeviceId: message.deviceId, })}); } context.extraOutputs.set(wpsAction, actions); } });
Обновите параметры функции.
Добавьте
hubName
параметр и замените{YourIoTHubName}
имя концентратора, которое вы использовали при создании Центр Интернета вещей.func settings add hubName "{YourIoTHubName}"
Получите строку подключения службы для Центр Интернета вещей.
az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
Задайте
IOTHubConnectionString
значение, заменив<iot-connection-string>
его значением.func settings add IOTHubConnectionString "<iot-connection-string>"
- Получите строку подключения для Web PubSub.
az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
Задайте
WebPubSubConnectionString
значение, заменив<webpubsub-connection-string>
его значением.func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
Примечание.
Azure Event Hub trigger
Триггер функции, используемый в примере, зависит от служба хранилища Azure, но при локальном выполнении функции можно использовать эмулятор локального хранилища. Если возникает ошибка, напримерThere was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.
, необходимо скачать и включить эмулятор хранения.выполнение функции в локальной среде;
Теперь вы можете выполнить локальную функцию с помощью приведенной ниже команды.
func start
Чтобы посетить статическую страницу локального узла, посетите:
https://localhost:7071/api/index
Запуск устройства для отправки данных
Регистрация устройства
Устройство должно быть зарегистрировано в центре Интернета вещей, прежде чем оно сможет подключиться. Если устройство уже зарегистрировано в центре Интернета вещей, этот раздел можно пропустить.
Чтобы создать удостоверение устройства, выполните команду az iot hub device-identity create в Azure Cloud Shell.
YourIoTHubName: замените этот заполнитель именем, выбранным для центра Интернета вещей.
az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
Выполните команду az PowerShell module iot hub device-identity-identity-string show в Azure Cloud Shell, чтобы получить устройство строка подключения для только что зарегистрированного устройства:
YourIoTHubName. Замените этот заполнитель именем вашего центра Интернета вещей.
az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
Запишите устройство строка подключения, которое выглядит следующим образом:
HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}
Для получения самых быстрых результатов имитируйте данные температуры с помощью симулятора Raspberry Pi Azure IoT Online. Вставьте строка подключения устройства и нажмите кнопку "Выполнить".
Если у вас есть физический датчик Raspberry Pi и BME280, можно измерить и сообщить о реальных значениях температуры и влажности, следуя руководству по подключению Raspberry Pi к Центр Интернета вещей Azure (Node.js).
Запуск веб-сайта визуализации
Откройте страницу индекса узла функции: http://localhost:7071/api/index
чтобы просмотреть панель мониторинга в режиме реального времени. Зарегистрируйте несколько устройств и вы увидите, что панель мониторинга обновляет несколько устройств в режиме реального времени. Откройте несколько браузеров, и вы увидите, что каждая страница обновляется в режиме реального времени.
Очистка ресурсов
Если вы планируете продолжить работу с последующими краткими руководствами и статьями, эти ресурсы можно не удалять.
Вы можете удалить ставшую ненужной группу ресурсов и все связанные с ней ресурсы с помощью Azure CLI, выполнив команду az group delete:
az group delete --name "myResourceGroup"