Search code examples
azure-iot-hubazure-iot-sdkazure-iot-edge

Azure IoT Edge module direct method responses shows as [object Object]


When invoking a direct method on a specific module I just receive the result [object Object] in the azure portal and I don't know what I'm doing wrong. Note that when I did exactly the same using the azure IoT SDK for c# (without running the azure iot runtime), I properly received the JSON object and it was not just shown as [object Object].

Note that I'm developing this in c# and the docker containers (used for IoT edge runtime and it's modules) is running Linux as OS.
I have the following sample method that I've registered as a direct method.

In the iot edge runtime Init() function I do the following:
await ioTHubModuleClient.SetMethodHandlerAsync("Sample1", Sample1, null);

The sample method looks like:

private static Task<MethodResponse> Sample1(MethodRequest methodRequest, object userContext)
    {            
        // Get data but don't do anything with it... Works fine!
        var data = Encoding.UTF8.GetString(methodRequest.Data);

        var methodResponse = new MethodResponse(Encoding.UTF8.GetBytes("{\"status\": \"ok\"}"), 200);
        return Task.FromResult(methodResponse);
    }  

I can monitor this module in the debug mode by setting breakpoints in the Sample1 method. I can't find what I'm doing wrong? Why is the response returned from this Sample1 method just shown as [object Object] and why don't I see the JSON-object {"status": "ok"} as I did when not using the Azure IoT Edge runtime?


Solution

  • The callback result for the Direct Method is object Task< MethodResponse >.It does not serialize to Json string to show in the Azure Portal. But you can use the Service Client Sdk to get the callback response and then serialize to JSON string.

    The latest Microsoft Azure IoT Hub SDK for C# supports Modules and IoT Edge. You can refer to this sample with using the SDK.

    Update:

    In the latest Azure IoT Hub SDK(Microsoft.Azure.Devices.Client 1.18), please use ModuleClinet instead of DeviceClient. You can refer to the following code in module.

    namespace SampleModuleA
    {
        using System;
        using System.IO;
        using System.Runtime.InteropServices;
        using System.Runtime.Loader;
        using System.Security.Cryptography.X509Certificates;
        using System.Text;
        using System.Threading;
        using System.Threading.Tasks;
        using Microsoft.Azure.Devices.Client.Transport.Mqtt;
        using Microsoft.Azure.Devices.Client;
        using Newtonsoft.Json;
        class Program
        {
            static int counter;
    
            static void Main(string[] args)
            {
                Init().Wait();
    
                // Wait until the app unloads or is cancelled
                var cts = new CancellationTokenSource();
                AssemblyLoadContext.Default.Unloading += (ctx) => cts.Cancel();
                Console.CancelKeyPress += (sender, cpe) => cts.Cancel();
                WhenCancelled(cts.Token).Wait();
            }
    
            /// <summary>
            /// Handles cleanup operations when app is cancelled or unloads
            /// </summary>
            public static Task WhenCancelled(CancellationToken cancellationToken)
            {
                var tcs = new TaskCompletionSource<bool>();
                cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
                return tcs.Task;
            }
    
            /// <summary>
            /// Initializes the ModuleClient and sets up the callback to receive
            /// messages containing temperature information
            /// </summary>
            static async Task Init()
            {
                MqttTransportSettings mqttSetting = new MqttTransportSettings(TransportType.Mqtt_WebSocket_Only);
                ITransportSettings[] settings = { mqttSetting };
    
                // Open a connection to the Edge runtime
                ModuleClient ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);
                await ioTHubModuleClient.OpenAsync();
                Console.WriteLine("[{0:HH:mm:ss ffff}]IoT Hub SampleModuleA client initialized.", DateTime.Now);
    
                await ioTHubModuleClient.SetMethodHandlerAsync("DirectMethod1", DirectMethod1, ioTHubModuleClient);
    
                // Register callback to be called when a message is received by the module
                await ioTHubModuleClient.SetInputMessageHandlerAsync("input1", PipeMessage, ioTHubModuleClient);
            }
    
            static async Task<MethodResponse> DirectMethod1(MethodRequest methodRequest, object moduleClient)
            {
                Console.WriteLine("Call DirectMethod1.");
                MethodResponse resp = null;
    
                //to do Something
    
                return resp;
            }
    
            /// <summary>
            /// This method is called whenever the module is sent a message from the EdgeHub. 
            /// It just pipe the messages without any change.
            /// It prints all the incoming messages.
            /// </summary>
            static async Task<MessageResponse> PipeMessage(Message message, object userContext)
            {
                int counterValue = Interlocked.Increment(ref counter);
    
                var moduleClient = userContext as ModuleClient;
                if (moduleClient == null)
                {
                    throw new InvalidOperationException("UserContext doesn't contain " + "expected values");
                }
    
                byte[] messageBytes = message.GetBytes();
                string messageString = Encoding.UTF8.GetString(messageBytes);
                Console.WriteLine($"Received message: {counterValue}, Body: [{messageString}]");
    
                if (!string.IsNullOrEmpty(messageString))
                {
                    var pipeMessage = new Message(messageBytes);
                    foreach (var prop in message.Properties)
                    {
                        pipeMessage.Properties.Add(prop.Key, prop.Value);
                    }
                    await moduleClient.SendEventAsync("output1", pipeMessage);
                    Console.WriteLine("Received message sent");
                }
                return MessageResponse.Completed;
            }
        }
    }