排程及廣播作業 (Node.js)

使用 Azure IoT 中樞排程和追蹤會更新數百萬部裝置的作業。 使用作業以:

  • 更新所需屬性
  • 更新標籤
  • 叫用直接方法

就概念而言,作業會包裝上述其中一個動作,然後針對由裝置對應項 (twin) 查詢所定義的一組裝置,追蹤執行進度。 例如,後端應用程式可以在 10,000 個裝置上使用作業叫用重新啟動方法,此方法由裝置對應項查詢指定並排定在未來執行。 接著,該應用程式可以追蹤每個這些裝置接收和執行重新啟動方法的進度。

從下列文章深入了解這當中的每一項功能:

注意

本文中所述的功能僅適用於 IoT 中樞的標準層。 如需基本和標準/免費IoT 中樞層的詳細資訊,請參閱為您的解決方案選擇正確的IoT 中樞層

本文說明如何建立兩個 Node.js 應用程式:

  • Node.js 模擬裝置應用程式 simDevice.js,其實作 lockDoor 直接方法,後端應用程式可呼叫此方法。

  • Node.js 主控台應用程式 scheduleJobService.js,其會建立兩個作業。 一個作業會呼叫 lockDoor 直接方法,而另一個作業會將所需的屬性更新傳送至多個裝置。

注意

如需可用來建置裝置和後端應用程式的 SDK 工具詳細資訊,請參閱 Azure IoT SDK

必要條件

  • IoT 中樞。 使用 CLIAzure 入口網站建立一個。

  • 已註冊的裝置。 在 Azure 入口網站中註冊一個。

  • Node.js 版本 10.0.x 或更新版本。 準備開發環境說明如何在 Windows 或 Linux 上安裝本文的 Node.js。

  • 請確定您的防火牆已開啟連接埠 8883。 本文中的裝置範例會使用 MQTT 通訊協定,其會透過連接埠 8883 進行通訊。 某些公司和教育網路環境可能會封鎖此連接埠。 如需此問題的詳細資訊和解決方法,請參閱連線至 IoT 中樞 (MQTT)

建立模擬裝置應用程式

在本節中,您建立 Node.js 主控台應用程式,回應雲端所呼叫的直接方法,可觸發模擬的 lockDoor 方法。

  1. 建立名為 simDevice 的新空白資料夾。 在命令提示字元中,使用下列命令在 simDevice 資料夾中建立 package.json 檔案。 接受所有預設值:

    npm init
    
  2. 在命令提示字元中,於 simDevice 資料夾中執行下列命令來安裝 azure-iot-device 裝置 SDK 套件和 azure-iot-device-mqtt 套件:

    npm install azure-iot-device azure-iot-device-mqtt --save
    
  3. 使用文字編輯器,在 [simDevice] 資料夾中建立新的 simDevice.js 檔案。

  4. simDevice.js 檔案的開頭新增下列 'require' 陳述式:

    'use strict';
    
    var Client = require('azure-iot-device').Client;
    var Protocol = require('azure-iot-device-mqtt').Mqtt;
    
  5. 新增 connectionString 變數,並用它來建立用戶端執行個體。 將 {yourDeviceConnectionString} 預留位置值由您先前所複製的裝置連接字串取代。

    var connectionString = '{yourDeviceConnectionString}';
    var client = Client.fromConnectionString(connectionString, Protocol);
    
  6. 新增下列函式以處理 lockDoor 方法。

    var onLockDoor = function(request, response) {
    
        // Respond the cloud app for the direct method
        response.send(200, function(err) {
            if (err) {
                console.error('An error occurred when sending a method response:\n' + err.toString());
            } else {
                console.log('Response to method \'' + request.methodName + '\' sent successfully.');
            }
        });
    
        console.log('Locking Door!');
    };
    
  7. 新增下列程式碼以註冊 lockDoor 方法的處理常式。

    client.open(function(err) {
         if (err) {
             console.error('Could not connect to IotHub client.');
         }  else {
             console.log('Client connected to IoT Hub. Register handler for lockDoor direct method.');
             client.onDeviceMethod('lockDoor', onLockDoor);
         }
    });
    
  8. 儲存並關閉 simDevice.js 檔案。

注意

為了簡單起見,本文章不會實作重試原則。 在生產環境程式碼中,您應該如暫時性錯誤處理一文中所建議,實作重試原則 (例如指數型輪詢)。

取得 IoT 中樞連接字串

在本文中,您會建立後端服務,其會排程在裝置上叫用直接方法的作業、排程更新裝置對應項的作業,以及監視每項作業的進度。 若要執行這些作業,則服務需要有登錄讀取登錄寫入權限。 根據預設,每個 IoT 中樞都是使用授與這些權限且名為 registryReadWrite 的共用存取原則所建立。

若要取得 registryReadWrite 原則的 IoT 中樞連接字串,請遵循下列步驟:

  1. Azure 入口網站中選取 [資源群組]。 選取中樞所在的資源群組,然後從資源清單選取中樞。

  2. 在中樞的左側窗格中,選取 [共用存取原則]。

  3. 從原則清單中,選取 registryReadWrite 原則。

  4. 複製 [主要連接字串] 並儲存該值。

    顯示如何擷取連接字串的螢幕擷取畫面

如需 IoT 中樞共用存取原則和權限的詳細資訊,請參閱存取控制及權限

排定用於呼叫直接方法及更新裝置對應項 (twin) 屬性的作業

在本節中,您會建立一個 Node.js 主控台 App,此 App 會使用直接方法在裝置上初始化遠端 lockDoor,並更新裝置對應項 (twin) 的屬性。

  1. 建立名為 scheduleJobService 的新空白資料夾。 在命令提示字元中,使用下列命令在 scheduleJobService 資料夾中建立 package.json 檔案。 接受所有預設值:

    npm init
    
  2. 在命令提示字元中,於 scheduleJobService 資料夾中執行下列命令來安裝 azure-iothub 裝置 SDK 套件和 azure-iot-device-mqtt 套件:

    npm install azure-iothub uuid --save
    
  3. 使用文字編輯器,在 [scheduleJobService] 資料夾中建立新的 scheduleJobService.js 檔案。

  4. scheduleJobService.js 檔案的開頭新增下列「要求」陳述式:

    'use strict';
    
    var uuid = require('uuid');
    var JobClient = require('azure-iothub').JobClient;
    
  5. 新增下列變數宣告。 將 {iothubconnectionstring} 預留位置值由先前在取得 IoT 中樞連接字串內複製的值取代。 如果您註冊的裝置與 myDeviceId 不同,請務必在查詢條件中加以變更。

    var connectionString = '{iothubconnectionstring}';
    var queryCondition = "deviceId IN ['myDeviceId']";
    var startTime = new Date();
    var maxExecutionTimeInSeconds =  300;
    var jobClient = JobClient.fromConnectionString(connectionString);
    
  6. 新增下列將用來監視作業執行的函式:

    function monitorJob (jobId, callback) {
        var jobMonitorInterval = setInterval(function() {
            jobClient.getJob(jobId, function(err, result) {
            if (err) {
                console.error('Could not get job status: ' + err.message);
            } else {
                console.log('Job: ' + jobId + ' - status: ' + result.status);
                if (result.status === 'completed' || result.status === 'failed' || result.status === 'cancelled') {
                clearInterval(jobMonitorInterval);
                callback(null, result);
                }
            }
            });
        }, 5000);
    }
    
  7. 新增下列程式碼來排定呼叫裝置方法的作業:

    var methodParams = {
        methodName: 'lockDoor',
        payload: null,
        responseTimeoutInSeconds: 15 // Timeout after 15 seconds if device is unable to process method
    };
    
    var methodJobId = uuid.v4();
    console.log('scheduling Device Method job with id: ' + methodJobId);
    jobClient.scheduleDeviceMethod(methodJobId,
                                queryCondition,
                                methodParams,
                                startTime,
                                maxExecutionTimeInSeconds,
                                function(err) {
        if (err) {
            console.error('Could not schedule device method job: ' + err.message);
        } else {
            monitorJob(methodJobId, function(err, result) {
                if (err) {
                    console.error('Could not monitor device method job: ' + err.message);
                } else {
                    console.log(JSON.stringify(result, null, 2));
                }
            });
        }
    });
    
  8. 新增下列程式碼來排定更新裝置對應項 (twin) 的作業:

    var twinPatch = {
       etag: '*',
       properties: {
           desired: {
               building: '43',
               floor: 3
           }
       }
    };
    
    var twinJobId = uuid.v4();
    
    console.log('scheduling Twin Update job with id: ' + twinJobId);
    jobClient.scheduleTwinUpdate(twinJobId,
                                queryCondition,
                                twinPatch,
                                startTime,
                                maxExecutionTimeInSeconds,
                                function(err) {
        if (err) {
            console.error('Could not schedule twin update job: ' + err.message);
        } else {
            monitorJob(twinJobId, function(err, result) {
                if (err) {
                    console.error('Could not monitor twin update job: ' + err.message);
                } else {
                    console.log(JSON.stringify(result, null, 2));
                }
            });
        }
    });
    
  9. 儲存並關閉 scheduleJobService.js 檔案。

執行應用程式

現在您已經準備好執行應用程式。

  1. 在命令提示字元中,於 simDevice 資料夾中執行下列命令來開始接聽重新啟動直接方法。

    node simDevice.js
    
  2. scheduleJobService 資料夾的命令提示字元中,執行下列命令以觸發鎖門作業並更新對應項

    node scheduleJobService.js
    
  3. 您會在主控台中看到裝置對直接方法的回應和工作狀態。

    下列顯示裝置對直接方法的回應:

    模擬裝置應用程式輸出

    以下顯示對直接方法和裝置對應項更新的服務排程作業,以及執行至完成的作業:

    執行模擬裝置應用程式

後續步驟

在本文中,您已排程工作來執行直接方法,並更新裝置對應項的屬性。

若要繼續探索 IoT 中樞和裝置管理模式,請更新使用 Raspberry Pi 3 B+ 參考映像的 Azure IoT 中樞裝置更新教學課程中的映像。