--- /dev/null
+/*\r
+ * FreeRTOS Kernel V10.2.1\r
+ * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://www.FreeRTOS.org\r
+ * http://aws.amazon.com/freertos\r
+ *\r
+ * 1 tab == 4 spaces!\r
+ */\r
+\r
+/* This demo executes Jobs obtained from AWS IoT. An AWS IoT Job is used to define\r
+ * a set of remote operations that are sent to and executed on one or more devices\r
+ * connected to AWS IoT. Please refer to AWS documentation for more information\r
+ * about AWS IoT Jobs.\r
+ * https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html\r
+ *\r
+ * This demo creates a single application task that sets a callback for the\r
+ * jobs/notify-next topic and executes Jobs created from the AWS IoT console or AWS\r
+ * CLI. Please refer to AWS CLI documentation for more information in creating a\r
+ * Job document.\r
+ * https://docs.aws.amazon.com/cli/latest/reference/iot/create-job.html\r
+ *\r
+ * This demo expects Job documents to have an "action" JSON key. Actions can\r
+ * be one "print", "publish", or "exit".\r
+ * Print Jobs log a message to the local console, and must contain a "message",\r
+ * e.g. { "action": "print", "message": "Hello World!" }.\r
+ * Publish Jobs publish a message to an MQTT Topic. The Job document must\r
+ * contain a "message" and "topic" to publish to, e.g.\r
+ * { "action": "publish", "topic": "demo/jobs", "message": "Hello World!" }.\r
+ * The exit Job exits the demo. Sending { "action": "exit" } will end the program.\r
+ */\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+#include <stdio.h>\r
+\r
+/* Kernel includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+\r
+/* FreeRTOS+TCP includes. */\r
+#include "FreeRTOS_IP.h"\r
+\r
+/* IoT SDK includes. */\r
+#include "aws_iot_jobs.h"\r
+#include "aws_iot_demo_profile.h"\r
+#include "iot_mqtt.h"\r
+#include "iot_taskpool_freertos.h"\r
+#include "aws_iot_doc_parser.h"\r
+#include "platform/iot_clock.h"\r
+#include "platform/iot_threads.h"\r
+#include "platform/iot_network_freertos.h"\r
+\r
+#include "atomic.h"\r
+\r
+/* Preprocessor check iot configuration. */\r
+#include "aws_iot_setup_check.h"\r
+\r
+/* Demo specific includes. */\r
+#include "demo_config.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief The keep-alive interval used for this example.\r
+ *\r
+ * An MQTT ping request will be sent periodically at this interval.\r
+ *\r
+ * @note: This value is set to zero to disable MQTT\r
+ * keep alive for the Windows simulator project.\r
+ * The FreeRTOS kernel does not accurately calculate time for the Windows\r
+ * Simulator. Therefore, MQTT PING Request messages may be sent\r
+ * at an incorrect time interval to the broker. If the broker does\r
+ * not receive a ping request within 1.5x the time sent in a\r
+ * connection request, the broker may close the connection.\r
+ * To enable the keep alive feature, set this value\r
+ * to the desired interval in seconds.\r
+ */\r
+#define jobsexampleKEEP_ALIVE_SECONDS ( 0 )\r
+\r
+/**\r
+ * @brief The timeout for MQTT operations in this example.\r
+ */\r
+#define jobsexampleMQTT_TIMEOUT_MS ( 5000 )\r
+\r
+/**\r
+ * @brief Use default timeout when calling AwsIotJobs_Init.\r
+ */\r
+#define jobsexampleUSE_DEFAULT_MQTT_TIMEOUT ( 0 )\r
+\r
+/**\r
+ * @brief The bit which is set in the demo task's notification value from the\r
+ * disconnect callback to inform the demo task about the MQTT disconnect.\r
+ */\r
+#define jobsexampleDISCONNECTED_BIT ( 1UL << 0UL )\r
+\r
+/**\r
+ * @brief The bit which is set in the demo task's notification value from the\r
+ * operation complete callback to inform the demo task to exit.\r
+ */\r
+#define jobsexampleEXIT_BIT ( 1UL << 1UL )\r
+\r
+/**\r
+ * @brief Length of the client identifier for this demo.\r
+ */\r
+#define jobsexampleCLIENT_IDENTIFIER_LENGTH ( sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1 )\r
+\r
+/**\r
+ * @brief The JSON key of the Job ID.\r
+ *\r
+ * Job documents are in JSON documents received from the AWS IoT Jobs service.\r
+ * All such JSON documents will contain this key, whose value represents the unique\r
+ * identifier of a Job.\r
+ */\r
+#define jobsexampleID_KEY "jobId"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleID_KEY.\r
+ */\r
+#define jobsexampleID_KEY_LENGTH ( sizeof( jobsexampleID_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief The JSON key of the Job document.\r
+ *\r
+ * Job documents are in JSON documents received from the AWS IoT Jobs service.\r
+ * All such JSON documents will contain this key, whose value is an application-specific\r
+ * Job document.\r
+ */\r
+#define jobsexampleDOC_KEY "jobDocument"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleDOC_KEY.\r
+ */\r
+#define jobsexampleDOC_KEY_LENGTH ( sizeof( jobsexampleDOC_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief The JSON key whose value represents the action this demo should take.\r
+ *\r
+ * This demo program expects this key to be in the Job document. It is a key\r
+ * specific to this demo.\r
+ */\r
+#define jobsexampleACTION_KEY "action"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleACTION_KEY.\r
+ */\r
+#define jobsexampleACTION_KEY_LENGTH ( sizeof( jobsexampleACTION_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief A message associated with the Job action.\r
+ *\r
+ * This demo program expects this key to be in the Job document if the "action"\r
+ * is either "publish" or "print". It represents the message that should be\r
+ * published or printed, respectively.\r
+ */\r
+#define jobsexampleMESSAGE_KEY "message"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleMESSAGE_KEY.\r
+ */\r
+#define jobsexampleMESSAGE_KEY_LENGTH ( sizeof( jobsexampleMESSAGE_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief An MQTT topic associated with the Job "publish" action.\r
+ *\r
+ * This demo program expects this key to be in the Job document if the "action"\r
+ * is "publish". It represents the MQTT topic on which the message should be\r
+ * published.\r
+ */\r
+#define jobsexampleTOPIC_KEY "topic"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleTOPIC_KEY.\r
+ */\r
+#define jobsexampleTOPIC_KEY_LENGTH ( sizeof( jobsexampleTOPIC_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief The minimum length of a string in a JSON Job document.\r
+ *\r
+ * At the very least the Job ID must have the quotes that identify it as a JSON\r
+ * string and 1 character for the string itself (the string must not be empty).\r
+ */\r
+#define jobsexampleJSON_STRING_MIN_LENGTH ( ( size_t ) 3 )\r
+\r
+/**\r
+ * @brief The maximum length of a Job ID.\r
+ *\r
+ * This limit is defined by AWS service limits. See the following page for more\r
+ * information.\r
+ *\r
+ * https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits\r
+ */\r
+#define jobsexampleID_MAX_LENGTH ( ( size_t ) 64 )\r
+\r
+/**\r
+ * @brief A value passed as context to #prvOperationCompleteCallback to specify that\r
+ * it should notify the demo task of an exit request.\r
+ */\r
+#define jobsexampleSHOULD_EXIT ( ( void * ) ( ( intptr_t ) 1 ) )\r
+\r
+/**\r
+ * @brief Time to wait before exiting demo.\r
+ *\r
+ * The milliseconds to wait before exiting. This is because the MQTT Broker\r
+ * will disconnect us if we are idle too long, and we have disabled keep alive.\r
+ */\r
+#define jobsexampleMS_BEFORE_EXIT ( 10 * 60 * 1000 )\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Currently supported actions that a Job document can specify.\r
+ */\r
+typedef enum _jobAction\r
+{\r
+ JOB_ACTION_PRINT, /**< Print a message. */\r
+ JOB_ACTION_PUBLISH, /**< Publish a message to an MQTT topic. */\r
+ JOB_ACTION_EXIT, /**< Exit the demo. */\r
+ JOB_ACTION_UNKNOWN /**< Unknown action. */\r
+} _jobAction_t;\r
+\r
+/**\r
+ * @brief The task used to demonstrate Jobs.\r
+ *\r
+ * @param[in] pvParameters Parameters as passed at the time of task creation. Not\r
+ * used in this example.\r
+ */\r
+static void prvJobsDemoTask( void * pvParameters );\r
+\r
+/**\r
+ * @brief The callback invoked by the MQTT library when the MQTT connection gets\r
+ * disconnected.\r
+ *\r
+ * @param[in] pvCallbackContext Callback context as provided at the time of\r
+ * connect.\r
+ * @param[in] pxCallbackParams Contains the reason why the MQTT connection was\r
+ * disconnected.\r
+ */\r
+static void prvExample_OnDisconnect( void * pvCallbackContext,\r
+ IotMqttCallbackParam_t * pxCallbackParams );\r
+\r
+/**\r
+ * @brief Connects to the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT\r
+ * and awsiotdemoprofileAWS_MQTT_PORT.\r
+ */\r
+static void prvMQTTConnect( void );\r
+\r
+/**\r
+ * @brief Disconnects from the MQTT broker gracefully by sending an MQTT\r
+ * DISCONNECT message.\r
+ */\r
+static void prvMQTTDisconnect( void );\r
+\r
+/**\r
+ * @brief Set callback for publishes to the jobs/notify-next topic.\r
+ */\r
+static void prvSetNotifyNextCallback( void );\r
+\r
+/**\r
+ * @brief Converts a string in a Job document to a #_jobAction_t.\r
+ *\r
+ * @param[in] pcAction The Job action as a string.\r
+ * @param[in] xActionLength The length of `pcAction`.\r
+ *\r
+ * @return A #_jobAction_t equivalent to the given string.\r
+ */\r
+static _jobAction_t prvGetAction( const char * pcAction,\r
+ size_t xActionLength );\r
+\r
+/**\r
+ * @brief Extracts a JSON string from the Job document.\r
+ *\r
+ * @param[in] pcJsonDoc The JSON document to search.\r
+ * @param[in] xJsonDocLength Length of `pcJsonDoc`.\r
+ * @param[in] pcKey The JSON key to search for.\r
+ * @param[in] xKeyLength Length of `pcKey`.\r
+ * @param[out] ppcValue The extracted JSON value.\r
+ * @param[out] pxValueLength Length of ppcValue.\r
+ *\r
+ * @return `pdTRUE` if the key was found and the value is valid; `pdFALSE` otherwise.\r
+ */\r
+static BaseType_t prvGetJsonString( const char * pcJsonDoc,\r
+ size_t xJsonDocLength,\r
+ const char * pcKey,\r
+ size_t xKeyLength,\r
+ const char ** ppcValue,\r
+ size_t * pxValueLength );\r
+\r
+/**\r
+ * @brief Job operation completion callback. This function is invoked when an\r
+ * asynchronous Job operation finishes.\r
+ *\r
+ * @param[in] pvCallbackContext Set to a non-NULL value to exit the demo.\r
+ * @param[in] pxCallbackParam Information on the Job operation that completed.\r
+ */\r
+static void prvOperationCompleteCallback( void * pvCallbackContext,\r
+ AwsIotJobsCallbackParam_t * pxCallbackParam );\r
+\r
+\r
+/**\r
+ * @brief Process an action with a message, such as "print" or "publish".\r
+ *\r
+ * @param[in] xMqttConnection The MQTT connection to use if the action is "publish".\r
+ * @param[in] xAction Either #JOB_ACTION_PRINT or #JOB_ACTION_PUBLISH.\r
+ * @param[in] pcJobDoc A pointer to the Job document.\r
+ * @param[in] xJobDocLength The length of the Job document.\r
+ *\r
+ * @return #AWS_IOT_JOB_STATE_SUCCEEDED on success; #AWS_IOT_JOB_STATE_FAILED otherwise.\r
+ */\r
+static AwsIotJobState_t prvProcessMessage( IotMqttConnection_t xMqttConnection,\r
+ _jobAction_t xAction,\r
+ const char * pcJobDoc,\r
+ size_t xJobDocLength );\r
+\r
+/**\r
+ * @brief Process a Job received from the Notify Next callback.\r
+ *\r
+ * @param[in] pxJobInfo The parameter to the Notify Next callback that contains\r
+ * information about the received Job.\r
+ * @param[in] pcJobId A pointer to the Job ID.\r
+ * @param[in] xJobIdLength The length of the Job ID.\r
+ * @param[in] pcJobDoc A pointer to the Job document.\r
+ * @param[in] xJobDocLength The length of the Job document.\r
+ */\r
+static void prvProcessJob( const AwsIotJobsCallbackParam_t * pxJobInfo,\r
+ const char * pcJobId,\r
+ size_t xJobIdLength,\r
+ const char * pcJobDoc,\r
+ size_t xJobDocLength );\r
+\r
+/**\r
+ * @brief Jobs Notify Next callback. This function is invoked when a new Job is\r
+ * received from the Jobs service.\r
+ *\r
+ * @param[in] pCallbackContext Ignored.\r
+ * @param[in] pxCallbackInfo Contains the received Job.\r
+ */\r
+static void prvJobsCallback( void * pCallbackContext,\r
+ AwsIotJobsCallbackParam_t * pxCallbackInfo );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief The MQTT connection handle used in this example.\r
+ */\r
+static IotMqttConnection_t xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+\r
+/*\r
+ * @brief The main task handle in this demo.\r
+ */\r
+static TaskHandle_t xMainTaskHandle;\r
+\r
+/**\r
+ * @brief Parameters used to create the system task pool.\r
+ */\r
+static const IotTaskPoolInfo_t xTaskPoolParameters =\r
+{\r
+ /* Minimum number of threads in a task pool.\r
+ * Note the slimmed down version of the task\r
+ * pool used by this library does not auto-scale\r
+ * the number of tasks in the pool so in this\r
+ * case this sets the number of tasks in the\r
+ * pool. */\r
+ 1,\r
+\r
+ /* Maximum number of threads in a task pool.\r
+ * Note the slimmed down version of the task\r
+ * pool used by this library does not auto-scale\r
+ * the number of tasks in the pool so in this\r
+ * case this parameter is just ignored. */\r
+ 1,\r
+\r
+ /* Stack size for every task pool thread - in\r
+ * bytes, hence multiplying by the number of bytes\r
+ * in a word as configMINIMAL_STACK_SIZE is\r
+ * specified in words. */\r
+ configMINIMAL_STACK_SIZE * sizeof( portSTACK_TYPE ),\r
+ /* Priority for every task pool thread. */\r
+ tskIDLE_PRIORITY,\r
+};\r
+\r
+/***************** Structures that define the connection. *********************/\r
+\r
+\r
+static const struct IotNetworkServerInfo xMQTTBrokerInfo =\r
+{\r
+ .pHostName = awsiotdemoprofileAWS_ENDPOINT,\r
+ .port = awsiotdemoprofileAWS_MQTT_PORT\r
+};\r
+\r
+static struct IotNetworkCredentials xNetworkSecurityCredentials =\r
+{\r
+ /* Optional TLS extensions. For this demo, they are disabled. */\r
+ .pAlpnProtos = NULL,\r
+ .maxFragmentLength = 0,\r
+\r
+ /* SNI is enabled by default. */\r
+ .disableSni = false,\r
+\r
+ /* Provide the certificate for validating the server. Only required for\r
+ demos using TLS. */\r
+ .pRootCa = awsiotdemoprofileAWS_CERTIFICATE_PEM,\r
+ .rootCaSize = sizeof( awsiotdemoprofileAWS_CERTIFICATE_PEM ),\r
+\r
+ /* Strong mutual authentication to authenticate both the broker and\r
+ * the client. */\r
+ .pClientCert = awsiotdemoprofileCLIENT_CERTIFICATE_PEM,\r
+ .clientCertSize = sizeof( awsiotdemoprofileCLIENT_CERTIFICATE_PEM ),\r
+ .pPrivateKey = awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM,\r
+ .privateKeySize = sizeof( awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM )\r
+};\r
+\r
+static IotMqttNetworkInfo_t xNetworkInfo =\r
+{\r
+ /* No connection to the MQTT broker has been established yet and we want to\r
+ * establish a new connection. */\r
+ .createNetworkConnection = true,\r
+ .u.setup.pNetworkServerInfo = &( xMQTTBrokerInfo ),\r
+\r
+ /* Set the TLS credentials for the new MQTT connection. */\r
+ .u.setup.pNetworkCredentialInfo = &xNetworkSecurityCredentials,\r
+\r
+ /* Use FreeRTOS+TCP network interface. */\r
+ .pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,\r
+\r
+ /* Setup the callback which is called when the MQTT connection is\r
+ * disconnected. The task handle is passed as the callback context which\r
+ * is used by the callback to send a task notification to this task.*/\r
+ .disconnectCallback.function = prvExample_OnDisconnect\r
+};\r
+\r
+static const IotMqttConnectInfo_t xConnectInfo =\r
+{\r
+ /* Set this flag to true if connecting to the AWS IoT MQTT broker. */\r
+ .awsIotMqttMode = false,\r
+\r
+ /* Start with a clean session i.e. direct the MQTT broker to discard any\r
+ * previous session data. Also, establishing a connection with clean session\r
+ * will ensure that the broker does not store any data when this client\r
+ * gets disconnected. */\r
+ .cleanSession = true,\r
+\r
+ /* Since we are starting with a clean session, there are no previous\r
+ * subscriptions to be restored. */\r
+ .pPreviousSubscriptions = NULL,\r
+ .previousSubscriptionCount = 0,\r
+\r
+ /* We do not want to publish Last Will and Testament (LWT) message if the\r
+ * client gets disconnected. */\r
+ .pWillInfo = NULL,\r
+\r
+ /* Send an MQTT PING request every minute to keep the connection open if\r
+ * there is no other MQTT traffic. */\r
+ .keepAliveSeconds = jobsexampleKEEP_ALIVE_SECONDS,\r
+\r
+ /* The client identifier is used to uniquely identify this MQTT client to\r
+ * the MQTT broker. In a production device the identifier can be something\r
+ * unique, such as a device serial number. */\r
+ .pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER,\r
+ .clientIdentifierLength = ( uint16_t ) sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1,\r
+\r
+ /* This example does not authenticate the client and therefore username and\r
+ * password fields are not used. */\r
+ .pUserName = NULL,\r
+ .userNameLength = 0,\r
+ .pPassword = NULL,\r
+ .passwordLength = 0\r
+};\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvExample_OnDisconnect( void * pvCallbackContext,\r
+ IotMqttCallbackParam_t * pxCallbackParams )\r
+{\r
+TaskHandle_t xDemoTaskHandle = ( TaskHandle_t ) pvCallbackContext;\r
+\r
+ /* Ensure that we initiated the disconnect. */\r
+ configASSERT( pxCallbackParams->u.disconnectReason == IOT_MQTT_DISCONNECT_CALLED );\r
+\r
+ /* Inform the demo task about the disconnect. */\r
+ xTaskNotify( xDemoTaskHandle,\r
+ jobsexampleDISCONNECTED_BIT,\r
+ eSetBits /* Set the jobsexampleDISCONNECTED_BIT in the demo task's notification value. */\r
+ );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vStartJobsDemo( void )\r
+{\r
+TickType_t xShortDelay = ( TickType_t ) pdMS_TO_TICKS( ( TickType_t ) 500 );\r
+\r
+ /* Wait a short time to allow receipt of the ARP replies. */\r
+ vTaskDelay( xShortDelay );\r
+\r
+ /* This example uses a single application task, which in turn is used to\r
+ * connect, subscribe, publish, unsubscribe and disconnect from the MQTT\r
+ * broker. */\r
+ xTaskCreate( prvJobsDemoTask, /* Function that implements the task. */\r
+ "JobsDemo", /* Text name for the task - only used for debugging. */\r
+ democonfigDEMO_STACKSIZE, /* Size of stack (in words, not bytes) to allocate for the task. */\r
+ NULL, /* Task parameter - not used in this case. */\r
+ tskIDLE_PRIORITY, /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */\r
+ NULL ); /* Used to pass out a handle to the created task - not used in this case. */\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvJobsDemoTask( void * pvParameters )\r
+{\r
+IotMqttError_t xResult;\r
+IotNetworkError_t xNetworkInit;\r
+uint32_t ulNotificationValue = 0;\r
+const TickType_t xNoDelay = ( TickType_t ) 0;\r
+AwsIotJobsError_t xStatus = AWS_IOT_JOBS_SUCCESS;\r
+AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
+AwsIotJobsRequestInfo_t xRequestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;\r
+\r
+ /* Remove compiler warnings about unused parameters. */\r
+ ( void ) pvParameters;\r
+\r
+ xMainTaskHandle = xTaskGetCurrentTaskHandle();\r
+\r
+ /* The MQTT library needs a task pool, so create the system task pool. */\r
+ xResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );\r
+ configASSERT( xResult == IOT_TASKPOOL_SUCCESS );\r
+\r
+ /* Initialize the network stack abstraction for FreeRTOS. */\r
+ xNetworkInit = IotNetworkFreeRTOS_Init();\r
+ configASSERT( xNetworkInit == IOT_NETWORK_SUCCESS );\r
+\r
+ /* MQTT library must be initialized before it can be used. This is just one\r
+ * time initialization. */\r
+ xResult = IotMqtt_Init();\r
+ configASSERT( xResult == IOT_MQTT_SUCCESS );\r
+\r
+ /* Initialize Jobs library. */\r
+ xResult = AwsIotJobs_Init( jobsexampleUSE_DEFAULT_MQTT_TIMEOUT );\r
+ configASSERT( xResult == AWS_IOT_JOBS_SUCCESS );\r
+\r
+ /****************************** Connect. ******************************/\r
+\r
+ /* Establish a connection to the AWS IoT MQTT broker. This example connects to\r
+ * the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT and\r
+ * awsiotdemoprofileAWS_MQTT_PORT at the top of this file.\r
+ */\r
+ configPRINTF( ( "Attempt to connect to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
+ prvMQTTConnect();\r
+ configPRINTF( ( "Connected to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
+\r
+ /* Don't expect any notifications to be pending yet. */\r
+ configASSERT( ulTaskNotifyTake( pdTRUE, xNoDelay ) == 0 );\r
+\r
+ configPRINTF( ( "Setting callback for jobs/notify-next\r\n" ) );\r
+ prvSetNotifyNextCallback();\r
+\r
+ /* Call DescribeAsync to see if there are any pending jobs. */\r
+ xRequestInfo.mqttConnection = xMQTTConnection;\r
+ xRequestInfo.pThingName = awsiotdemoprofileCLIENT_IDENTIFIER;\r
+ xRequestInfo.thingNameLength = jobsexampleCLIENT_IDENTIFIER_LENGTH;\r
+ xRequestInfo.pJobId = AWS_IOT_JOBS_NEXT_JOB;\r
+ xRequestInfo.jobIdLength = AWS_IOT_JOBS_NEXT_JOB_LENGTH;\r
+\r
+ /* Use the same callback as notify-next so any pending jobs will be\r
+ * executed the same way. */\r
+ xCallbackInfo.function = prvJobsCallback;\r
+\r
+ xStatus = AwsIotJobs_DescribeAsync( &xRequestInfo, AWS_IOT_JOBS_NO_EXECUTION_NUMBER, true, 0, &xCallbackInfo, NULL );\r
+ configPRINTF( ( "Describe queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
+\r
+ /* Print out a short user guide to the console. The default logging\r
+ * limit of 255 characters can be changed in demo_logging.c, but breaking\r
+ * up the only instance of a 1000+ character string is more practical. */\r
+ configPRINTF( (\r
+ "\r\n"\r
+ "/*-----------------------------------------------------------*/\r\n"\r
+ "\r\n"\r
+ "The Jobs demo is now ready to accept Jobs.\r\n"\r
+ "Jobs may be created using the AWS IoT console or AWS CLI.\r\n"\r
+ "See the following link for more information.\r\n"\r
+ "\r\n" ) );\r
+ configPRINTF( (\r
+ "\r"\r
+ "https://docs.aws.amazon.com/cli/latest/reference/iot/create-job.html\r\n"\r
+ "\r\n"\r
+ "This demo expects Job documents to have an \"action\" JSON key.\r\n"\r
+ "The following actions are currently supported:\r\n" ) );\r
+ configPRINTF( (\r
+ "\r"\r
+ " - print \r\n"\r
+ " Logs a message to the local console. The Job document must also contain a \"message\".\r\n"\r
+ " For example: { \"action\": \"print\", \"message\": \"Hello world!\"} will cause\r\n"\r
+ " \"Hello world!\" to be printed on the console.\r\n" ) );\r
+ configPRINTF( (\r
+ "\r"\r
+ " - publish \r\n"\r
+ " Publishes a message to an MQTT topic. The Job document must also contain a \"message\" and \"topic\".\r\n" ) );\r
+ configPRINTF( (\r
+ "\r"\r
+ " For example: { \"action\": \"publish\", \"topic\": \"demo/jobs\", \"message\": \"Hello world!\"} will cause\r\n"\r
+ " \"Hello world!\" to be published to the topic \"demo/jobs\".\r\n" ) );\r
+ configPRINTF( (\r
+ "\r"\r
+ " - exit \r\n"\r
+ " Exits the demo program. This program will run until { \"action\": \"exit\" } is received.\r\n"\r
+ "\r\n"\r
+ "/*-----------------------------------------------------------*/\r\n" ) );\r
+\r
+ /* Wait for an exit job to be received. If an exit job is not received within\r
+ * jobsexampleMS_BEFORE_EXIT, exit anyway. This is because we have disabled\r
+ * keep-alive, and the server will disconnect as after some time. */\r
+ xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */\r
+ jobsexampleEXIT_BIT, /* Clear bit on exit. */\r
+ &( ulNotificationValue ), /* Obtain the notification value. */\r
+ pdMS_TO_TICKS( jobsexampleMS_BEFORE_EXIT) );\r
+ /* Check was due to receiving an exit job. */\r
+ if( ( ulNotificationValue & jobsexampleEXIT_BIT ) != jobsexampleEXIT_BIT )\r
+ {\r
+ configPRINTF( ( "Disconnecting as %u milliseconds have elapsed.\r\n", jobsexampleMS_BEFORE_EXIT ) );\r
+ }\r
+\r
+ /* Disconnect MQTT gracefully. */\r
+ prvMQTTDisconnect();\r
+ configPRINTF( ( "Disconnected from %s\r\n\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
+\r
+ /* Wait for the disconnect operation to complete which is informed to us\r
+ * by the disconnect callback (prvExample_OnDisconnect)by setting\r
+ * the jobsexampleDISCONNECTED_BIT in this task's notification value. */\r
+ xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */\r
+ jobsexampleDISCONNECTED_BIT, /* Clear bit on exit. */\r
+ &( ulNotificationValue ), /* Obtain the notification value. */\r
+ pdMS_TO_TICKS( jobsexampleMQTT_TIMEOUT_MS ) );\r
+ configASSERT( ( ulNotificationValue & jobsexampleDISCONNECTED_BIT ) == jobsexampleDISCONNECTED_BIT );\r
+\r
+ configPRINTF( ( "prvJobsDemoTask() completed successfully. Total free heap is %u\r\n", xPortGetFreeHeapSize() ) );\r
+ configPRINTF( ( "Demo completed successfully.\r\n" ) );\r
+\r
+ /* Clean up initialized libraries. */\r
+ AwsIotJobs_Cleanup();\r
+ IotMqtt_Cleanup();\r
+ IotNetworkFreeRTOS_Cleanup();\r
+\r
+ /* FreeRTOS Tasks must _vTaskDelete( NULL )_ before exiting the function. */\r
+ vTaskDelete( NULL );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvMQTTConnect( void )\r
+{\r
+IotMqttError_t xResult;\r
+\r
+ /* Set the context to pass into the disconnect callback function. */\r
+ xNetworkInfo.disconnectCallback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();\r
+\r
+ /* Establish the connection to the MQTT broker - It is a blocking call and\r
+ * will return only when connection is complete or a timeout occurs. */\r
+ xResult = IotMqtt_Connect( &( xNetworkInfo ),\r
+ &( xConnectInfo ),\r
+ jobsexampleMQTT_TIMEOUT_MS,\r
+ &( xMQTTConnection ) );\r
+ configASSERT( xResult == IOT_MQTT_SUCCESS );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvMQTTDisconnect( void )\r
+{\r
+ /* Send a MQTT DISCONNECT packet to the MQTT broker to do a graceful\r
+ * disconnect. */\r
+ IotMqtt_Disconnect( xMQTTConnection,\r
+ 0 /* flags - 0 means a graceful disconnect by sending MQTT DISCONNECT. */\r
+ );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvSetNotifyNextCallback( void )\r
+{\r
+AwsIotJobsError_t xCallbackStatus = AWS_IOT_JOBS_SUCCESS;\r
+AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
+\r
+ /* Set the jobs callback function. */\r
+ xCallbackInfo.function = prvJobsCallback;\r
+\r
+ /************************ Set notify-next callbacks **********************/\r
+\r
+ xCallbackStatus = AwsIotJobs_SetNotifyNextCallback( xMQTTConnection,\r
+ awsiotdemoprofileCLIENT_IDENTIFIER,\r
+ jobsexampleCLIENT_IDENTIFIER_LENGTH,\r
+ 0,\r
+ &xCallbackInfo );\r
+\r
+ configASSERT( xCallbackStatus == AWS_IOT_JOBS_SUCCESS );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static _jobAction_t prvGetAction( const char * pcAction,\r
+ size_t xActionLength )\r
+{\r
+_jobAction_t xAction = JOB_ACTION_UNKNOWN;\r
+\r
+ configASSERT( pcAction != NULL );\r
+\r
+ if( strncmp( pcAction, "print", xActionLength ) == 0 )\r
+ {\r
+ xAction = JOB_ACTION_PRINT;\r
+ }\r
+ else if( strncmp( pcAction, "publish", xActionLength ) == 0 )\r
+ {\r
+ xAction = JOB_ACTION_PUBLISH;\r
+ }\r
+ else if( strncmp( pcAction, "exit", xActionLength ) == 0 )\r
+ {\r
+ xAction = JOB_ACTION_EXIT;\r
+ }\r
+\r
+ return xAction;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static BaseType_t prvGetJsonString( const char * pcJsonDoc,\r
+ size_t xJsonDocLength,\r
+ const char * pcKey,\r
+ size_t xKeyLength,\r
+ const char ** ppcValue,\r
+ size_t * pxValueLength )\r
+{\r
+BaseType_t xKeyFound = pdFALSE;\r
+\r
+ configASSERT( pcJsonDoc != NULL );\r
+ configASSERT( pcKey != NULL );\r
+\r
+ /*\r
+ * Note: This parser used is specific for parsing AWS IoT document received\r
+ * through a mutually authenticated connection. This parser will not check\r
+ * for the correctness of the document as it is designed for low memory\r
+ * footprint rather than checking for correctness of the document. This\r
+ * parser is not meant to be used as a general purpose JSON parser.\r
+ */\r
+ xKeyFound = ( BaseType_t ) AwsIotDocParser_FindValue(\r
+ pcJsonDoc,\r
+ xJsonDocLength,\r
+ pcKey,\r
+ xKeyLength,\r
+ ppcValue,\r
+ pxValueLength );\r
+\r
+ if( xKeyFound == pdTRUE )\r
+ {\r
+ /* Exclude empty strings. */\r
+ if( *pxValueLength < jobsexampleJSON_STRING_MIN_LENGTH )\r
+ {\r
+ xKeyFound = pdFALSE;\r
+ }\r
+ else\r
+ {\r
+ /* Adjust the value to remove the quotes. */\r
+ ( *ppcValue )++;\r
+ ( *pxValueLength ) -= 2;\r
+ }\r
+ }\r
+\r
+ return xKeyFound;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvOperationCompleteCallback( void * pvCallbackContext,\r
+ AwsIotJobsCallbackParam_t * pxCallbackParam )\r
+{\r
+ configASSERT( pxCallbackParam != NULL );\r
+\r
+ /* This function is invoked when either a StartNext or Update completes. */\r
+ if( pxCallbackParam->callbackType == AWS_IOT_JOBS_START_NEXT_COMPLETE )\r
+ {\r
+ configPRINTF( ( "Job StartNext complete with result %s.\r\n",\r
+ AwsIotJobs_strerror( pxCallbackParam->u.operation.result ) ) );\r
+ }\r
+ else\r
+ {\r
+ configPRINTF( ( "Job Update complete with result %s.\r\n",\r
+ AwsIotJobs_strerror( pxCallbackParam->u.operation.result ) ) );\r
+ }\r
+\r
+ /* If a non-NULL context is given, set the flag to exit the demo. */\r
+ if( pvCallbackContext != NULL )\r
+ {\r
+ xTaskNotify( xMainTaskHandle,\r
+ jobsexampleEXIT_BIT,\r
+ eSetBits /* Set the jobsexampleEXIT_BIT in the demo task's notification value. */\r
+ );\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static AwsIotJobState_t prvProcessMessage( IotMqttConnection_t xMqttConnection,\r
+ _jobAction_t xAction,\r
+ const char * pcJobDoc,\r
+ size_t xJobDocLength )\r
+{\r
+AwsIotJobState_t xStatus = AWS_IOT_JOB_STATE_SUCCEEDED;\r
+IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
+IotMqttPublishInfo_t xPublishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
+const char * pcMessage = NULL, * pcTopic = NULL;\r
+size_t xMessageLength = 0, xTopicLength = 0;\r
+\r
+ configASSERT( pcJobDoc != NULL );\r
+\r
+ /* Both "print" and "publish" require a "message" key. Search the Job\r
+ * document for this key. */\r
+ if( prvGetJsonString( pcJobDoc,\r
+ xJobDocLength,\r
+ jobsexampleMESSAGE_KEY,\r
+ jobsexampleMESSAGE_KEY_LENGTH,\r
+ &pcMessage,\r
+ &xMessageLength ) == pdFALSE )\r
+ {\r
+ configPRINTF( ( "Job document for \"print\" or \"publish\" does not contain a %s key.\r\n",\r
+ jobsexampleMESSAGE_KEY ) );\r
+\r
+ xStatus = AWS_IOT_JOB_STATE_FAILED;\r
+ }\r
+\r
+ if( xStatus == AWS_IOT_JOB_STATE_SUCCEEDED )\r
+ {\r
+ if( xAction == JOB_ACTION_PRINT )\r
+ {\r
+ /* Print the given message if the action is "print". */\r
+ configPRINTF( (\r
+ "\r\n"\r
+ "/*-----------------------------------------------------------*/\r\n"\r
+ "\r\n"\r
+ "%.*s\r\n"\r
+ "\r\n"\r
+ "/*-----------------------------------------------------------*/\r\n"\r
+ "\r\n", xMessageLength, pcMessage ) );\r
+ }\r
+ else\r
+ {\r
+ /* Extract the topic if the action is "publish". */\r
+ if( prvGetJsonString( pcJobDoc,\r
+ xJobDocLength,\r
+ jobsexampleTOPIC_KEY,\r
+ jobsexampleTOPIC_KEY_LENGTH,\r
+ &pcTopic,\r
+ &xTopicLength ) == pdFALSE )\r
+ {\r
+ configPRINTF( ( "Job document for action \"publish\" does not contain a %s key.\r\n",\r
+ jobsexampleTOPIC_KEY ) );\r
+\r
+ xStatus = AWS_IOT_JOB_STATE_FAILED;\r
+ }\r
+\r
+ if( xStatus == AWS_IOT_JOB_STATE_SUCCEEDED )\r
+ {\r
+ xPublishInfo.qos = IOT_MQTT_QOS_0;\r
+ xPublishInfo.pTopicName = pcTopic;\r
+ xPublishInfo.topicNameLength = ( uint16_t ) xTopicLength;\r
+ xPublishInfo.pPayload = pcMessage;\r
+ xPublishInfo.payloadLength = xMessageLength;\r
+\r
+ xMqttStatus = IotMqtt_PublishAsync( xMqttConnection, &xPublishInfo, 0, NULL, NULL );\r
+\r
+ if( xMqttStatus != IOT_MQTT_SUCCESS )\r
+ {\r
+ xStatus = AWS_IOT_JOB_STATE_FAILED;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return xStatus;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvProcessJob( const AwsIotJobsCallbackParam_t * pxJobInfo,\r
+ const char * pcJobId,\r
+ size_t xJobIdLength,\r
+ const char * pcJobDoc,\r
+ size_t xJobDocLength )\r
+{\r
+AwsIotJobsError_t xStatus = AWS_IOT_JOBS_SUCCESS;\r
+AwsIotJobsUpdateInfo_t xUpdateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;\r
+AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
+const char * pcAction = NULL;\r
+size_t xActionLength = 0;\r
+_jobAction_t xAction = JOB_ACTION_UNKNOWN;\r
+AwsIotJobsRequestInfo_t xRequestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;\r
+\r
+ configASSERT( pxJobInfo != NULL );\r
+ configASSERT( pcJobId != NULL );\r
+ configASSERT( pcJobDoc != NULL );\r
+\r
+ configPRINTF( ( "Job document received: %.*s\r\n", xJobDocLength, pcJobDoc ) );\r
+\r
+ xRequestInfo.mqttConnection = pxJobInfo->mqttConnection;\r
+ xRequestInfo.pThingName = pxJobInfo->pThingName;\r
+ xRequestInfo.thingNameLength = pxJobInfo->thingNameLength;\r
+ xRequestInfo.pJobId = pcJobId;\r
+ xRequestInfo.jobIdLength = xJobIdLength;\r
+\r
+ /* Tell the Jobs service that the device has started working on the Job.\r
+ * Use the StartNext API to set the Job's status to IN_PROGRESS. */\r
+ xCallbackInfo.function = prvOperationCompleteCallback;\r
+\r
+ xStatus = AwsIotJobs_StartNextAsync( &xRequestInfo, &xUpdateInfo, 0, &xCallbackInfo, NULL );\r
+\r
+ configPRINTF( ( "Jobs StartNext queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
+\r
+ /* Get the action for this device. */\r
+ if( prvGetJsonString( pcJobDoc,\r
+ xJobDocLength,\r
+ jobsexampleACTION_KEY,\r
+ jobsexampleACTION_KEY_LENGTH,\r
+ &pcAction,\r
+ &xActionLength ) == pdTRUE )\r
+ {\r
+ xAction = prvGetAction( pcAction, xActionLength );\r
+\r
+ switch( xAction )\r
+ {\r
+ case JOB_ACTION_EXIT:\r
+ xCallbackInfo.pCallbackContext = jobsexampleSHOULD_EXIT;\r
+ xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;\r
+ break;\r
+\r
+ case JOB_ACTION_PRINT:\r
+ case JOB_ACTION_PUBLISH:\r
+ xUpdateInfo.newStatus = prvProcessMessage( pxJobInfo->mqttConnection,\r
+ xAction,\r
+ pcJobDoc,\r
+ xJobDocLength );\r
+ break;\r
+\r
+ default:\r
+ configPRINTF( ( "Received Job document with unknown action %.*s.\r\n",\r
+ xActionLength,\r
+ pcAction ) );\r
+\r
+ xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_FAILED;\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ configPRINTF( ( "Received Job document does not contain an %s key.\r\n",\r
+ jobsexampleACTION_KEY ) );\r
+\r
+ /* The given Job document is not valid for this demo. */\r
+ xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_FAILED;\r
+ }\r
+\r
+ configPRINTF( ( "Setting state of %.*s to %s.\r\n",\r
+ xJobIdLength,\r
+ pcJobId,\r
+ AwsIotJobs_StateName( xUpdateInfo.newStatus ) ) );\r
+\r
+ /* Tell the Jobs service that the device has finished the Job. */\r
+ xStatus = AwsIotJobs_UpdateAsync( &xRequestInfo, &xUpdateInfo, 0, &xCallbackInfo, NULL );\r
+\r
+ configPRINTF( ( "Jobs Update queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvJobsCallback( void * pCallbackContext,\r
+ AwsIotJobsCallbackParam_t * pxCallbackInfo )\r
+{\r
+BaseType_t xIdKeyFound = pdFALSE, xDocKeyFound = pdFALSE;\r
+const char * pcJobId = NULL;\r
+size_t xJobIdLength = 0;\r
+const char * pcJobDoc = NULL;\r
+size_t xJobDocLength = 0;\r
+const char * pcRawDocument = NULL;\r
+size_t xRawDocumentLength = 0;\r
+\r
+ /* Silence warnings about unused parameters. */\r
+ ( void ) pCallbackContext;\r
+\r
+ configASSERT( pxCallbackInfo != NULL );\r
+\r
+ /* Check if this callback was called from a describe operation or\r
+ * due to notify-next. */\r
+ if( pxCallbackInfo->callbackType == AWS_IOT_JOBS_DESCRIBE_COMPLETE )\r
+ {\r
+ pcRawDocument = pxCallbackInfo->u.operation.pResponse;\r
+ xRawDocumentLength = pxCallbackInfo->u.operation.responseLength;\r
+ }\r
+ else\r
+ {\r
+ pcRawDocument = pxCallbackInfo->u.callback.pDocument;\r
+ xRawDocumentLength = pxCallbackInfo->u.callback.documentLength;\r
+ }\r
+\r
+ /* Get the Job ID. */\r
+ xIdKeyFound = prvGetJsonString( pcRawDocument,\r
+ xRawDocumentLength,\r
+ jobsexampleID_KEY,\r
+ jobsexampleID_KEY_LENGTH,\r
+ &pcJobId,\r
+ &xJobIdLength );\r
+\r
+ if( xIdKeyFound == pdTRUE )\r
+ {\r
+ if( xJobIdLength > jobsexampleID_MAX_LENGTH )\r
+ {\r
+ configPRINTF( ( "Received Job ID %.*s longer than %lu, which is the "\r
+ "maximum allowed by AWS IoT. Ignoring Job.\r\n",\r
+ xJobIdLength,\r
+ pcJobId,\r
+ ( unsigned long ) jobsexampleID_MAX_LENGTH ) );\r
+\r
+ xIdKeyFound = pdFALSE;\r
+ }\r
+ else\r
+ {\r
+ configPRINTF( ( "Job %.*s received.\r\n", xJobIdLength, pcJobId ) );\r
+ }\r
+ }\r
+\r
+ /* Get the Job document.\r
+ *\r
+ * Note: This parser used is specific for parsing AWS IoT document received\r
+ * through a mutually authenticated connection. This parser will not check\r
+ * for the correctness of the document as it is designed for low memory\r
+ * footprint rather than checking for correctness of the document. This\r
+ * parser is not meant to be used as a general purpose JSON parser.\r
+ */\r
+ xDocKeyFound = ( BaseType_t ) AwsIotDocParser_FindValue(\r
+ pcRawDocument,\r
+ xRawDocumentLength,\r
+ jobsexampleDOC_KEY,\r
+ jobsexampleDOC_KEY_LENGTH,\r
+ &pcJobDoc,\r
+ &xJobDocLength );\r
+\r
+ /* When both the Job ID and Job document are available, process the Job. */\r
+ if( ( xIdKeyFound == pdTRUE ) && ( xDocKeyFound == pdTRUE ) )\r
+ {\r
+ /* Process the Job document. */\r
+ prvProcessJob( pxCallbackInfo,\r
+ pcJobId,\r
+ xJobIdLength,\r
+ pcJobDoc,\r
+ xJobDocLength );\r
+ }\r
+ else\r
+ {\r
+ /* The Jobs service sends an empty Job document when all Jobs are complete. */\r
+ if( ( xIdKeyFound == pdFALSE ) && ( xDocKeyFound == pdFALSE ) )\r
+ {\r
+ configPRINTF( (\r
+ "\r\n"\r
+ "/*-----------------------------------------------------------*/\r\n"\r
+ "\r\n"\r
+ "All available Jobs complete.\r\n"\r
+ "\r\n"\r
+ "/*-----------------------------------------------------------*/\r\n"\r
+ "\r\n" ) );\r
+ }\r
+ else\r
+ {\r
+ configPRINTF( ( "Received an invalid Job document: %.*s\r\n",\r
+ xRawDocumentLength,\r
+ pcRawDocument ) );\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r