[IoT] Azure IoT整合應用八:透過Call Method的方式,讓IoT Hub直接呼叫裝置的副程式,並取得回傳資料

之前有提過,Azure上的IoT Hub有個與眾不同的功能,就是它與裝置之間是處於雙向溝通的模式
所以訊息的傳遞,也可以從裝置到IoT Hub,IoT Hub也可以逆向傳送訊息到裝置中
不過Microsoft在2017年2月的更新版本中,IoT Hub可以直接對裝置端呼叫裝置中的副程式,而裝置也可以透過這個註冊的副程式,回傳資料到IoT Hub

要實作讓IoT Hub呼叫裝置上註冊的副程式,並回傳訊息的方式很簡單,在程式碼中加一些內容就可以了

1.在裝置的程式中,加入下面的程式碼

/// <summary>
/// 程式執行的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <returns></returns>
private async void frmMain_Load(object sender, EventArgs e)
{
    // 定義裝置為IoT的device
    if (deviceClient == null)
        deviceClient = DeviceClient.CreateFromConnectionString("[IoT Hub的連接字串]", Microsoft.Azure.Devices.Client.TransportType.Amqp);

    // 註冊回傳事件的動作
    await deviceClient.SetMethodHandlerAsync("[在這裡輸入要註冊進IoT Hub的Function名稱]", OnCallMethod, null);
}

/// <summary>
/// 當IoT Call Method時,回傳的動作
/// </summary>
/// <param name="request"></param>
/// <param name="userContext"></param>
/// <returns></returns>
private async Task<MethodResponse> OnCallMethod(MethodRequest request, object userContext)
{           
    // 取得從IoT Hub上傳入的參數
    string strInput = request.DataAsJson;

    // 取得設定要直接回傳的值
    string strReturnValue = "[在這裡可以放入JSON的物件並轉換成為字串]";

    // 進行回傳動作
    return new MethodResponse(Encoding.UTF8.GetBytes(strReturnValue), 200);
}

這段程式碼的動作,共分為兩個部份,frmMain_Load的動作中,執行了向IoT Hub註冊副程式名稱的動作,並且在device端的程式碼中,加入了被呼叫的副程式
而OnCallMethod就是當IoT Hub呼叫device端的副程式會執行的動作,並透過回傳JSON的字串,將訊息回傳到IoT Hub中。在OnCallMethod的內容中,request.DataAsJson的內容則是從IoT Hub上傳入至device端的訊息內容

程式碼的部份,就加入這樣的功能就可以,接著就可以直接開啟DeviceExplorer,進行Call Method的動作

DeviceExplorer必須自行上Github下載並編譯才能執行,下載網址為
https://github.com/Azure/azure-iot-sdk-csharp/tree/master/tools/DeviceExplorer

2.開啟DeviceExplorer,並連上IoT Hub後,切換到[Call Method On Device]的頁籤,在這個頁籤中,選擇了要從IoT Hub呼叫的裝置代碼,並輸入要呼叫在device端註冊的Function名稱,而下方的[Method Payload]則是要從IoT Hub送到device端的JSON訊息

3.接著打開裝置端的程式,將Call Method的資訊作一個填入的動作,從下圖中可以看到,在裝置的程式中,Method Name必須要與IoT Hub傳入的Method Name相同,而下方的Return Value,則是當IoT Hub執行Call Method的時候會回傳的JSON訊息

4.到這裡,IoT Hub與Device端都已經準備好,從DeviceExplorer按下呼叫後,可以看到在右方的device端接收到了IoT Hub上傳下來的訊息並顯示在畫面上,而從device端回傳的JSON訊息,也顯示在左方的 DeviceExplorer的畫面中。這代表了雙方的訊息已經透過註冊後的副程式進行直接的呼叫,並進行訊息的交換

如果不想下載DeviceExplorer的話,現在在Azure Portal上也可以透過現有的功能直接進行註冊副程式的呼叫,叫用的方式也很簡單

1.進入Azure Portal後,點開IoT Hub的項目後,點選左方[Device Explorer]的功能選單,再點選要呼叫副程式的裝置項目,接著在[裝置詳細資料]的視窗上,點選[直接方法]

2.接著在[直接方法]的視窗中,把[方法名稱] (Method Name)、[裝載] (Method Payload) 這兩個參數填入,並按下[叫用方法],執行完成後就可以看到下圖的結果,IoT Hub送出的訊息,透過呼叫直接方法,把訊息送至Device端,Device端的JSON也回傳至下方的[結果]畫面

如果說想透過程式碼的方式進行方法的呼叫,而不想使用Portal或是Device Explorer執行的話,透過SDK已經作好的套件也是可以達到一樣的效果的

1.我們先建立一個新的應用程式,並在畫面上放入DeviceId、Message以及Call Method用的文字方塊

2.接著在[Send]的按鈕事件中,加入下面的程式碼

/// <summary>
/// 回送訊息的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnSend_Click(object sender, EventArgs e)
{
    string strDeviceId = txtDeviceId.Text;
    string strMessage = txtMessage.Text;

    if (!string.IsNullOrEmpty(strMessage))
    {
        var serviceMessage = new Microsoft.Azure.Devices.Message(Encoding.ASCII.GetBytes(strMessage));
        serviceMessage.Ack = DeliveryAcknowledgement.Full;
        serviceMessage.MessageId = Guid.NewGuid().ToString();
        await serviceClient.SendAsync(strDeviceId, serviceMessage);
    }
    else
        MessageBox.Show("請輸入要傳送的文字訊息");
}

這段程式碼,主要的內容就是將要下傳至裝置的訊息,透過SendAsync的方法,傳入到指定的裝置上,若是有需要進行訊息回傳的話,透過這個方法就可以達到了

3.在[Call Method]的按鈕事件中,加入下面的程式碼

/// <summary>
/// 呼叫Device端事件方法的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnCallMethod_Click(object sender, EventArgs e)
{
    string strDeviceId = txtDeviceId.Text;

    if (txtCallMethod.Text != "")
    {
        // 指定TimeOut的秒數
        TimeSpan objTimeSpan = new TimeSpan(0, 0, 0, int.Parse(txtTimeout.Text));
        // 建立呼叫用的DeviceMethod
        CloudToDeviceMethod objMethod = new CloudToDeviceMethod(txtCallMethod.Text, objTimeSpan);
        // 放入要下傳的Json字串
        objMethod.SetPayloadJson(txtPayLoad.Text);
        // 執行呼叫的動作
        CloudToDeviceMethodResult result =  await serviceClient.InvokeDeviceMethodAsync(txtDeviceId.Text, objMethod);
        // 將結果放入到控制項中
        txtResultStatus.Text = result.Status.ToString();
        txtCallMethodResult.Text = result.GetPayloadAsJson();
    }
    else
        MessageBox.Show("請輸入要呼叫的事件方法名稱");
}

這段程式碼就是用來呼叫裝置端事件的程式了,除了可以透過SetPayloadJson的方法,將有需要下傳到裝置端的JSON資料放入並一起呼叫外,在執行Call Method完成後,也可以透過CloudToDeviceMothodResult的物件,將呼叫得狀態以及從裝置端要回傳的JSON訊息內容,放入至文字方塊的內容

實際執行的結果如下圖

從上圖的結果可以看到,透過CallMethod的方法,不論是從Device端要傳送到IoT Hub的JSON訊息,或是要從IoT Hub下送至裝置端的內容,都可以完整的互相傳送了

舊版的IoT Hub,僅能透過訊息傳送的方式逆送至Device端,當Device端接收到訊息後,必須在程式碼中加入相對應的判斷才能進行訊息的回傳,但是回傳的內容不外乎將訊息再次傳入至IoT Hub,或是呼叫WebHooks的方式進行訊息的傳遞。現在,透過了呼叫直接方法(Call Method)的功能,IoT Hub就能直接執行裝置上的副程式,並直接取得裝置要與IoT Hub溝通並交換的訊息了

參考資料:
了解 IoT 中樞的直接方法並從中樞叫用直接方法
使用直接方法 (.NET/.NET)

參考程式下載:
https://github.com/madukapai/maduka-Azure-IoT