Megosztás a következőn keresztül:


Oktatóanyag: IoT-eszközadatok megjelenítése az IoT Hubról az Azure Web PubSub szolgáltatás és az Azure Functions használatával

Ebben az oktatóanyagban megtudhatja, hogyan hozhat létre kiszolgáló nélküli alkalmazást az Azure Web PubSub szolgáltatás és az Azure Functions használatával valós idejű adatvizualizációval az IoT Hubról.

Ebben az oktatóanyagban az alábbiakkal fog megismerkedni:

  • Kiszolgáló nélküli adatvizualizációs alkalmazás létrehozása
  • Együttműködés a Web PubSub függvény bemeneti és kimeneti kötéseivel és az Azure IoT Hubbal
  • A mintafüggvények helyi futtatása

Előfeltételek

If you don't have an Azure subscription, create an Azure free account before you begin.

IoT Hub létrehozása

Ebben a szakaszban az Azure CLI használatával hozhat létre egy IoT Hubot és egy erőforráscsoportot. Az Azure-erőforráscsoport olyan logikai tároló, amelybe a rendszer üzembe helyezi és kezeli az Azure-erőforrásokat. Az IoT Hub központi üzenetközpontként szolgál az IoT-alkalmazás és az eszközök közötti kétirányú kommunikációhoz.

Ha már rendelkezik IoT Hubbal az Azure-előfizetésében, kihagyhatja ezt a szakaszt.

IoT Hub és erőforráscsoport létrehozása:

  1. Indítsa el a CLI-alkalmazást. A parancssori felület parancsainak a cikk további részében való futtatásához másolja ki a parancs szintaxisát, illessze be a parancssori felület alkalmazásába, szerkessze a változóértékeket, és nyomja le Entera billentyűt.

    • Ha Cloud Shellt használ, kattintson a CLI-parancsok Kipróbálás gombjára a Cloud Shell felosztott böngészőablakban való elindításához. Vagy megnyithatja a Cloud Shellt egy külön böngészőlapon.
    • Ha helyileg használja az Azure CLI-t, indítsa el a CLI-konzolalkalmazást, és jelentkezzen be az Azure CLI-be.
  2. Futtassa az az extension add parancsot az azure-iot bővítmény telepítéséhez vagy frissítéséhez az aktuális verzióra.

    az extension add --upgrade --name azure-iot
    
  3. A CLI-alkalmazásban futtassa az az group create parancsot egy erőforráscsoport létrehozásához. Az alábbi parancs létrehoz egy MyResourceGroup nevű erőforráscsoportot az eastus helyen.

    Megjegyzés:

    Igény szerint másik helyet is beállíthat. Az elérhető helyek megtekintéséhez futtassa a következőt az account list-locations: . Ez a rövid útmutató az eastus függvényt használja a példaparancsban látható módon.

    az group create --name MyResourceGroup --location eastus
    
  4. Futtassa az az iot hub create parancsot egy IoT Hub létrehozásához. Az IoT Hub létrehozása eltarthat néhány percig.

    YourIotHubName. Cserélje le ezt a helyőrzőt és a környező zárójeleket a következő parancsban az IoT Hubhoz választott névvel. Az IoT Hub nevének globálisan egyedinek kell lennie az Azure-ban. Használja az IoT Hub nevét a rövid útmutató többi részében, ahol a helyőrzőt látja.

    az iot hub create --resource-group MyResourceGroup --name {your_iot_hub_name}
    

Web PubSub-példány létrehozása

Ha már rendelkezik Web PubSub-példánysal az Azure-előfizetésében, kihagyhatja ezt a szakaszt.

Futtassa az az extension add parancsot a webpubsub bővítmény telepítéséhez vagy frissítéséhez az aktuális verzióra.

az extension add --upgrade --name webpubsub

Az Azure CLI az webpubsub create paranccsal hozzon létre egy Web PubSub-t a létrehozott erőforráscsoportban. Az alábbi parancs létrehoz egy ingyenes Web PubSub-erőforrást a myResourceGroup erőforráscsoportban az EastUS-ban:

Fontos

Minden Web PubSub-erőforrásnak egyedi névvel kell rendelkeznie. Az alábbi példákban cserélje le <az egyedi erőforrás nevét> a Web PubSub nevére.

az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1

A parancs kimenete az újonnan létrehozott erőforrás tulajdonságait jeleníti meg. Jegyezze fel az alábbi két tulajdonságot:

  • Erőforrás neve: A fenti paraméternek --name megadott név.
  • hostName: A példában a gazdagép neve .<your-unique-resource-name>.webpubsub.azure.com/

Ezen a ponton az Azure-fiók az egyetlen jogosult az új erőforráson végzett műveletek végrehajtására.

A függvények helyi létrehozása és futtatása

  1. Hozzon létre egy üres mappát a projekthez, majd futtassa az alábbi parancsot az új mappában.

    func init --worker-runtime javascript --model V4
    
  2. index Statikus weblap olvasására és üzemeltetésére szolgáló függvény létrehozása az ügyfelek számára.

    func new -n index -t HttpTrigger
    

    Frissítsen src/functions/index.js a következő kóddal, amely a HTML-tartalmat statikus webhelyként szolgálja ki.

    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, 
            };
        }
    });
    
  3. Hozzon létre egy index.html fájlt a gyökérmappában.

    <!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>
    
  4. Hozzon létre egy függvényt negotiate , amelyet az ügyfelek a szolgáltatáskapcsolat URL-címének és hozzáférési jogkivonatának lekéréséhez használnak.

    func new -n negotiate -t HttpTrigger
    

    Frissítsen src/functions/negotiate.js a létrehozott jogkivonatot tartalmazó használatra 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')) };
        },
    });
    
  5. Hozzon létre egy függvényt messagehandler , amely értesítéseket hoz létre a "IoT Hub (Event Hub)" sablon használatával.

     func new --template "Azure Event Hub trigger" --name messagehandler
    
    • Frissítsen src/functions/messagehandler.js a Web PubSub kimeneti kötés hozzáadásához a következő json-kóddal. A változót %hubName% az IoT eventHubName és a Web PubSub hub központi neveként is használjuk.

      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);
          }
      });
      
  6. Frissítse a függvénybeállításokat.

    1. Adjon hozzá hubName beállítást, és cserélje le {YourIoTHubName} az IoT Hub létrehozásakor használt hubnevet.

      func settings add hubName "{YourIoTHubName}"
      
    2. Az IoT Hub szolgáltatás Csatlakozás ion sztringjének lekérése.

    az iot hub connection-string show --policy-name service --hub-name {YourIoTHubName} --output table --default-eventhub
    

    Állítsa be IOTHubConnectionString, és cserélje le <iot-connection-string> az értéket.

    func settings add IOTHubConnectionString "<iot-connection-string>"
    
    1. A Web PubSub Csatlakozás ion sztringjének lekérése.
    az webpubsub key show --name "<your-unique-resource-name>" --resource-group "<your-resource-group>" --query primaryConnectionString
    

    Állítsa be WebPubSubConnectionString, és cserélje le <webpubsub-connection-string> az értéket.

    func settings add WebPubSubConnectionString "<webpubsub-connection-string>"
    

    Megjegyzés:

    A Azure Event Hub trigger mintában használt függvény-eseményindító függ az Azure Storage-tól, de helyi tárolóemulátort is használhat, ha a függvény helyileg fut. Ha például There was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.hibaüzenetet kap, le kell töltenie és engedélyeznie kell a Storage Emulatort.

  7. Futtassa helyileg a függvényt.

    Most már futtathatja a helyi függvényt az alábbi paranccsal.

    func start
    

    A helyi gazdagép statikus lapját a következő webhelyen tekintheti meg: https://localhost:7071/api/index.

Az eszköz futtatása adatok küldéséhez

Eszköz regisztrálása

Az eszköznek regisztrálva kell lennie az IoT Hubbal, hogy csatlakozhasson hozzá. Ha már regisztrált egy eszközt az IoT Hubon, kihagyhatja ezt a szakaszt.

  1. Futtassa az az iot hub device-identity create parancsot az Azure Cloud Shellben az eszközidentitás létrehozásához.

    YourIoTHubName: Cserélje le ezt a helyőrzőt az IoT Hubhoz választott névre.

    az iot hub device-identity create --hub-name {YourIoTHubName} --device-id simDevice
    
  2. Futtassa az Az PowerShell-modul iot hub device-identity connection-string show parancsát az Azure Cloud Shellben az imént regisztrált eszköz eszköz kapcsolati sztring lekéréséhez:

    YourIoTHubName: Cserélje le az alábbi helyőrzőt az IoT Hubhoz választott névre.

    az iot hub device-identity connection-string show --hub-name {YourIoTHubName} --device-id simDevice --output table
    

    Jegyezze fel az eszköz kapcsolati sztring, amely így néz ki:

    HostName={YourIoTHubName}.azure-devices.net;DeviceId=simDevice;SharedAccessKey={YourSharedAccessKey}

A vizualizációs webhely futtatása

A függvény gazdagép indexoldalának megnyitása: http://localhost:7071/api/index a valós idejű irányítópult megtekintéséhez. Regisztráljon több eszközt, és látni fogja, hogy az irányítópult valós időben frissít több eszközt. Nyisson meg több böngészőt, és látni fogja, hogy minden oldal valós időben frissül.

Screenshot of multiple devices data visualization using Web PubSub service.

Clean up resources

Ha azt tervezi, hogy az ezt követő rövid útmutatókkal és oktatóanyagokkal dolgozik tovább, ne törölje ezeket az erőforrásokat.

Ha már nincs rá szükség, az Azure CLI az group delete parancsával eltávolíthatja az erőforráscsoportot és az összes kapcsolódó erőforrást:

az group delete --name "myResourceGroup"

További lépések