Kurz: Vizualizace dat zařízení IoT ze služby IoT Hub pomocí služby Azure Web PubSub a Azure Functions
V tomto kurzu se naučíte používat službu Azure Web PubSub a Azure Functions k vytvoření bezserverové aplikace s vizualizací dat v reálném čase ze služby IoT Hub.
V tomto kurzu se naučíte:
- Vytvoření aplikace pro vizualizaci dat bez serveru
- Spolupráce se vstupními a výstupními vazbami funkce Web PubSub a Azure IoT Hubem
- Místní spuštění ukázkových funkcí
Důležité
Nezpracované připojovací řetězec se v tomto článku zobrazují jenom pro demonstrační účely.
Připojovací řetězec obsahuje informace o autorizaci vyžadované pro vaši aplikaci pro přístup ke službě Azure Web PubSub. Přístupový klíč uvnitř připojovací řetězec je podobný kořenovému heslu pro vaši službu. V produkčních prostředích vždy chraňte přístupové klíče. Pomocí služby Azure Key Vault můžete bezpečně spravovat a obměňovat klíče a zabezpečit připojení WebPubSubServiceClient
.
Vyhněte se distribuci přístupových klíčů ostatním uživatelům, jejich pevnému kódování nebo jejich uložení kdekoli ve formátu prostého textu, který je přístupný ostatním uživatelům. Otočte klíče, pokud se domníváte, že mohly být ohroženy.
Požadavky
Editor kódu, například Visual Studio Code
Node.js verze 18.x nebo vyšší.
Poznámka:
Další informace o podporovaných verzích Node.js najdete v dokumentaci k verzím modulu runtime služby Azure Functions.
Azure Functions Core Tools (upřednostňovaná verze 3 nebo vyšší) pro místní spouštění aplikací Funkcí Azure a jejich nasazení do Azure.
Azure CLI pro správu prostředků Azure.
Pokud ještě nemáte předplatné Azure, vytvořte si bezplatný účet Azure před tím, než začnete.
Vytvoření centra IoT
V této části použijete Azure CLI k vytvoření centra IoT a skupiny prostředků. Skupina prostředků Azure je logický kontejner, ve kterém se nasazují a spravují prostředky Azure. Centrum IoT funguje jako centrální centrum zpráv pro obousměrnou komunikaci mezi vaší aplikací IoT a zařízeními.
Pokud už ve svém předplatném Azure máte centrum IoT, můžete tuto část přeskočit.
Vytvoření centra IoT a skupiny prostředků:
Spusťte aplikaci CLI. Pokud chcete spustit příkazy rozhraní příkazového řádku ve zbývající části tohoto článku, zkopírujte syntaxi příkazu, vložte ho do aplikace CLI, upravte hodnoty proměnných a stiskněte
Enter
.- Pokud používáte Cloud Shell, vyberte v příkazech rozhraní příkazového řádku tlačítko Vyzkoušet a spusťte Cloud Shell v rozděleném okně prohlížeče. Nebo můžete Cloud Shell otevřít na samostatné kartě prohlížeče.
- Pokud používáte Azure CLI místně, spusťte konzolovou aplikaci CLI a přihlaste se k Azure CLI.
Spuštěním příkazu az extension add nainstalujte nebo upgradujte rozšíření azure-iot na aktuální verzi.
az extension add --upgrade --name azure-iot
V aplikaci CLI spusťte příkaz az group create a vytvořte skupinu prostředků. Následující příkaz vytvoří skupinu prostředků MyResourceGroup v umístění eastus.
Poznámka:
Volitelně můžete nastavit jiné umístění. Pokud chcete zobrazit dostupná umístění, spusťte
az account list-locations
příkaz . V tomto rychlém startu se používá eastus , jak je znázorněno v ukázkovém příkazu.az group create --name MyResourceGroup --location eastus
Spuštěním příkazu az iot hub create vytvořte IoT Hub. Vytvoření centra IoT může trvat několik minut.
YourIotHubName. Nahraďte tento zástupný symbol a okolní složené závorky v následujícím příkazu pomocí názvu, který jste zvolili pro centrum IoT. Název centra IoT musí být v Azure globálně jedinečný. Ve zbývající části tohoto rychlého startu použijte název centra IoT, ať se zobrazí zástupný symbol.
az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
Vytvoření instance Web PubSub
Pokud už ve svém předplatném Azure máte instanci Web PubSub, můžete tuto část přeskočit.
Spuštěním příkazu az extension add nainstalujte nebo upgradujte rozšíření webpubsub na aktuální verzi.
az extension add --upgrade --name webpubsub
Pomocí příkazu az webpubsub az webpubsub vytvořte web pubSub ve skupině prostředků, kterou jste vytvořili. Následující příkaz vytvoří prostředek Free Web PubSub ve skupině prostředků myResourceGroup v eastUS:
Důležité
Každý prostředek Web PubSub musí mít jedinečný název. V následujících příkladech nahraďte <název_prostředku-unique-resource názvem> podsítě Web PubSub.
az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1
Výstup tohoto příkazu zobrazuje vlastnosti nově vytvořeného prostředku. Poznamenejte si hodnoty dvou vlastností uvedených níže:
- Název prostředku: Název, který jste zadali výše uvedenému parametru
--name
. - hostName: V příkladu je
<your-unique-resource-name>.webpubsub.azure.com/
název hostitele .
V tuto chvíli je váš účet Azure jediným autorizovaným k provádění jakýchkoli operací s tímto novým prostředkem.
Místní vytvoření a spuštění funkcí
Vytvořte pro projekt prázdnou složku a pak v nové složce spusťte následující příkaz.
func init --worker-runtime javascript --model V4
Vytvořte
index
funkci pro čtení a hostování statické webové stránky pro klienty.func new -n index -t HttpTrigger
Aktualizujte
src/functions/index.js
následujícím kódem, který slouží obsahu HTML jako statický web.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, }; } });
Vytvořte
index.html
soubor v kořenové složce.<!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>
Vytvořte
negotiate
funkci, kterou klienti používají k získání adresy URL připojení služby a přístupového tokenu.func new -n negotiate -t HttpTrigger
Aktualizujte
src/functions/negotiate.js
na použitíWebPubSubConnection
, která obsahuje vygenerovaný token.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')) }; }, });
Vytvořte funkci pro
messagehandler
generování oznámení pomocí"IoT Hub (Event Hub)"
šablony.Nezpracované připojovací řetězec se v tomto článku zobrazují jenom pro demonstrační účely. V produkčních prostředích vždy chraňte přístupové klíče. Pomocí služby Azure Key Vault můžete bezpečně spravovat a obměňovat klíče a zabezpečit připojení
WebPubSubServiceClient
.func new --template "Azure Event Hub trigger" --name messagehandler
Aktualizujte
src/functions/messagehandler.js
a přidejte výstupní vazbu Web PubSub s následujícím kódem JSON. Jako název centra používáme proměnnou%hubName%
pro ioT eventHubName i web PubSub Hub.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); } });
Aktualizujte nastavení funkce.
Přidejte nastavení a nahraďte
hubName
{YourIoTHubName}
názvem centra, který jste použili při vytváření ioT Hubu.func settings add hubName "{YourIoTHubName}"
Získejte připojovací řetězec služby pro IoT Hub.
az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
Nastavte
IOTHubConnectionString
hodnotu a nahraďte<iot-connection-string>
ji hodnotou.func settings add IOTHubConnectionString "<iot-connection-string>"
- Získejte připojovací řetězec pro web pubSub.
az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
Nastavte
WebPubSubConnectionString
hodnotu a nahraďte<webpubsub-connection-string>
ji hodnotou.func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
Poznámka:
Aktivační
Azure Event Hub trigger
událost funkce použitá v ukázce má závislost na službě Azure Storage, ale při místním spuštění funkce můžete použít emulátor místního úložiště. Pokud se zobrazí například chybaThere was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.
, budete si muset stáhnout a povolit emulátor úložiště.Spusťte funkci místně.
Teď můžete místní funkci spustit pomocí následujícího příkazu.
func start
Statickou stránku místního hostitele můžete navštívit:
https://localhost:7071/api/index
.
Spuštění zařízení pro odesílání dat
Registrace zařízení
Zařízení musí být zaregistrované ve vašem centru IoT Hub, aby se mohlo připojit. Pokud už máte zařízení zaregistrované ve službě IoT Hub, můžete tuto část přeskočit.
Spuštěním příkazu az iot hub device-identity create v Azure Cloud Shellu vytvořte identitu zařízení.
YourIoTHubName: Nahraďte tento zástupný symbol názvem, který jste zvolili pro centrum IoT.
az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
Spuštěním příkazu Show pro modul Az PowerShell hub device-identity connection-string v Azure Cloud Shellu získejte připojovací řetězec zařízení, které jste právě zaregistrovali:
YourIoTHubName: Nahraďte tento zástupný symbol názvem, který jste zvolili pro centrum IoT.
az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
Poznamenejte si připojovací řetězec zařízení, které vypadá takto:
HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}
Pro nejrychlejší výsledky simulujte teplotní data pomocí simulátoru Raspberry Pi Azure IoT Online. Vložte zařízení připojovací řetězec a vyberte tlačítko Spustit.
Pokud máte fyzický senzor Raspberry Pi a BME280, můžete měřit a hlásit skutečné hodnoty teploty a vlhkosti pomocí kurzu Připojení Raspberry Pi ke službě Azure IoT Hub (Node.js).
Spuštění webu vizualizace
Otevření stránky indexu hostitele funkce: http://localhost:7071/api/index
zobrazení řídicího panelu v reálném čase Zaregistrujte více zařízení a v reálném čase se zobrazí aktualizace více zařízení na řídicím panelu. Otevřete více prohlížečů a uvidíte, že se každá stránka aktualizuje v reálném čase.
Vyčištění prostředků
Pokud chcete pokračovat v práci s dalšími rychlými starty a kurzy, možná budete chtít tyto prostředky zachovat.
Pokud už ji nepotřebujete, můžete k odebrání skupiny prostředků a všech souvisejících prostředků použít příkaz az group delete v Azure CLI:
az group delete --name "myResourceGroup"