1

Full disclosure, I have asked this question on Azure IoT SDK C github project, but since they recommend looking on StackOverflow, I decided to post here as well.

I am having trouble implementing a Direct Module Method handler in my azure-iot-sdk-c based IoT Edge Module. I could not find a documentation page with an example implementation, so I assembled my implementation from various SDK documentation pages and unit test "examples".

To test this, I have a dedicated Linux based PC (Ubuntu 18.04) running iotedge 1.0.8-2. I can see that my module is starting and printing its version and firing the connection status callback message. I even even see that the ModuleTwin callback is firing and printing the payload when I manually edit the module identity twin for my device in the portal.

However, when I try to manually invoke a Direct Method on my module within my device in the portal, I see nothing printed and I get the following error in the portal:

{"message":"GatewayTimeout:{\r\n  \"Message\": \"{\\\"errorCode\\\":504101,\\\"trackingId\\\":\\\"8215e001484d41a19245639844f44f78-G:9-TimeStamp:01/14/2020 21:20:42-G:0-TimeStamp:01/14/2020 21:20:42\\\",\\\"message\\\":\\\"Timed out waiting for the response from device.\\\",\\\"info\\\":{},\\\"timestampUtc\\\":\\\"2020-01-14T21:20:42.0556758Z\\\"}\",\r\n  \"ExceptionMessage\": \"\"\r\n}"}

The relevant code is below. I looked on StackOverflow but examples there are not C SDK based. Where am I going wrong with Direct Module Methods? Thank you!

Update: An interesting observation is that if I change this code to use MQTT from AMQP, then everything works. Is AMQP not supported for Direct Module Methods?

#include <iothub_module_client_ll.h>
#include <iothub_client_options.h>
#include <iothub_message.h>
#include <azure_c_shared_utility/threadapi.h>
#include <azure_c_shared_utility/crt_abstractions.h>
#include <azure_c_shared_utility/platform.h>
#include <azure_c_shared_utility/shared_util_options.h>
#include <iothubtransportamqp.h>
#include <iothub.h>
#include <time.h>

#include <stdio.h>
#include <stdlib.h>

// Linker defined build information (see Makefile)
extern char   __BUILD_DATE;
extern char   __BUILD_NUMBER;

// Set the default value for module communication (e.g. AMQP) log tracing, yet
// allow compile time overrides.
#ifndef LOG_TRACE_ENABLED
  #define LOG_TRACE_ENABLED 0
#endif

static void moduleTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* /*userContextCallback*/)
{
    EPRINT("DEBUG: Module Twin callback called with (state=%s)", MU_ENUM_TO_STRING(DEVICE_TWIN_UPDATE_STATE, update_state));
    EPRINT("DEBUG: payload=%.*s", (int)size, (const char *)payLoad);
    fflush(NULL);

    //JSON_Value *root_value = json_parse_string(payLoad);
    //JSON_Object *root_object = json_value_get_object(root_value);
    //if (json_object_dotget_value(root_object, "desired.TemperatureThreshold") != NULL) {
    //    temperatureThreshold = json_object_dotget_number(root_object, "desired.TemperatureThreshold");
    //}
    //if (json_object_get_value(root_object, "TemperatureThreshold") != NULL) {
    //    temperatureThreshold = json_object_get_number(root_object, "TemperatureThreshold");
    //}
}

static int DirectMethodCb(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* /*userContextCallback*/)
{
  const char *METHOD_NAME = "TestMethod";
  const int METHOD_RESPONSE_SUCCESS = 200;
  const int METHOD_RESPONSE_ERROR = 401;

  int responseCode;

  EPRINT("DEBUG: Method name:    %s", method_name);
  EPRINT("DEBUG: Method payload: %.*s", (int)size, (const char*)payload);

  if (strcmp(METHOD_NAME, method_name))
  {
    EPRINT("Method name incorrect - expected %s but got %s", METHOD_NAME, method_name);
    responseCode = METHOD_RESPONSE_ERROR;
  }
  /*
  else if (size != strlen(expectedMethodPayload))
  {
    LogError("payload size incorect - expected %zu but got %zu", strlen(expectedMethodPayload), size);
    responseCode = METHOD_RESPONSE_ERROR;
  }
  else if (memcmp(payload, expectedMethodPayload, size))
  {
    LogError("Payload strings do not match");
    responseCode = METHOD_RESPONSE_ERROR;
  }
  */
  else
  {
    *resp_size = size;
    if (size == 0)
    {
      *response = NULL;
      EPRINT("DEBUG: Empty, but good response");
      responseCode = METHOD_RESPONSE_SUCCESS;
    }
    else
    {
      if ((*response = (unsigned char*)malloc(*resp_size)) == NULL)
      {
        EPRINT("allocation failure");
        responseCode = METHOD_RESPONSE_ERROR;
      }
      else
      {
        (void)memcpy(*response, payload, *resp_size);
        EPRINT("DEBUG: All good - echoing back the payload");
        responseCode = METHOD_RESPONSE_SUCCESS;
      }
    }
  }

  EPRINT("DEBUG: completing with return code %d", responseCode);
  fflush(NULL);
  return responseCode;
}

static void ConnectionStatusCb(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* /*userContextCallback*/)
{
  EPRINT("DEBUG: ConnectionStatusCb(status=%d %s, reason=%d %s",
      result, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS, result),
      reason, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, reason)
      );
  fflush(NULL);
}

int main(void)
{
  IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle = nullptr;
  int retval = 1;

  do
  {
    printf("\n\n=======================\n");
    printf("Build date  : %lu\n", (unsigned long) &__BUILD_DATE);
    printf("Build number: %lu\n", (unsigned long) &__BUILD_NUMBER);
    fflush(NULL);

    srand((unsigned int)time(NULL));

    if (0 != IoTHub_Init())
    {
      EPRINT("Failed to initialize the platform.");
      break;
    }

    iotHubModuleClientHandle = IoTHubModuleClient_LL_CreateFromEnvironment(AMQP_Protocol);
    if (nullptr == iotHubModuleClientHandle)
    {
      EPRINT("IoTHubModuleClient_LL_CreateFromEnvironment failed");
      break;
    }

    IOTHUB_CLIENT_RESULT result = IoTHubModuleClient_LL_SetModuleMethodCallback(iotHubModuleClientHandle, DirectMethodCb, iotHubModuleClientHandle);
    if (IOTHUB_CLIENT_OK != result)
    {
      EPRINT("IoTHubModuleClient_SetModuleMethodCallback failed: %d", result);
      break;
    }

    result = IoTHubModuleClient_LL_SetConnectionStatusCallback(iotHubModuleClientHandle, ConnectionStatusCb, iotHubModuleClientHandle);
    if (IOTHUB_CLIENT_OK != result)
    {
      EPRINT("IoTHubDeviceClient_SetConnectionStatusCallback failed: %d", result);
      break;
    }

#if LOG_TRACE_ENABLED
    bool traceOn = true;
    IoTHubModuleClient_LL_SetOption(iotHubModuleClientHandle, OPTION_LOG_TRACE, &traceOn);
#endif // LOG_TRACE_ENABLED

    result = IoTHubModuleClient_LL_SetModuleTwinCallback(iotHubModuleClientHandle, moduleTwinCallback, iotHubModuleClientHandle);
    if (IOTHUB_CLIENT_OK != result)
    {
      EPRINT("IoTHubModuleClient_LL_SetModuleTwinCallback failed: %d", result);
      break;
    }

    while (true)
    {
      IoTHubModuleClient_LL_DoWork(iotHubModuleClientHandle);
      ThreadAPI_Sleep(100);
    }
  } while(false);

  if (nullptr != iotHubModuleClientHandle)
  {
    IoTHubModuleClient_LL_Destroy(iotHubModuleClientHandle);
  }
  IoTHub_Deinit();

  return retval;
}
Paul Grinberg
  • 1,184
  • 14
  • 37

0 Answers0