0

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?

NoExit
  • 3
  • 1
  • 4
  • To be more clear, when register the exact same method in a "regular" azure IoT device that isn't running the IoT Edge runtime, I see the JSON-result in the azure portal and not just [object Object]. It shouldn't differ if the method response comes from an azure IoT Edge module or an "regular" IoT device, right? – NoExit Aug 27 '18 at 09:47

1 Answers1

0

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;
        }
    }
}
Michael Xu
  • 4,382
  • 1
  • 8
  • 16
  • Ok, but how come that if I register the exact same method in a "regular" azure IoT device that isn't running the IoT Edge runtime, I see the JSON-result in the azure portal and not just [object Object]. That's what got me thinking that I'm doing something wrong since it shouldn't differ right? – NoExit Aug 27 '18 at 08:53
  • @NoExit, i think this issue may be due to the Azure Portal. You can post a feedback [here](https://feedback.azure.com/forums/321918-azure-iot).This forum is to get feedback on the new IoT services: Azure IoT Hub and Azure IoT suite. This forum will help the engineering team collect feedback from our customers and users and help us understand and prioritize the roadmap. – Michael Xu Aug 28 '18 at 01:00
  • Thanks @michael-xu-msft . Is there a way to use the DeviceExplorer-tool to invoke a direct method call on a module running on the IoT Edge runtime? If so, what should the method name be? I've tried with moduleName/methodName without success since I somehow must select in what module I want to invoke the given method name, right? – NoExit Aug 28 '18 at 08:22
  • @NoExit, DeviceExplorer tool does not support invoking a direct method on a module. I think you can try to use the **CallMethod** sample mentioned in my post. – Michael Xu Aug 28 '18 at 08:30
  • Thanks @michael-xu-msft I got the **CallMethod** example working. To bad that the current IoT Edge runtime doesn't expose a connection string as an environment variable so I didn't get the **ReceiverModule** to work. I guess you're supposed to use the ModuleClient nowadays instead of the DeviceClient – NoExit Aug 28 '18 at 11:59
  • @NoExit, yes, I used ModuleClient instead of the DeviceClient. Please note my update. – Michael Xu Aug 29 '18 at 01:04