]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/jobs/jobs_notify_next/DemoTasks/JobsNotifyNextExamples.c
Update version number in readiness for V10.3.0 release. Sync SVN with reviewed releas...
[freertos] / FreeRTOS-Labs / Demo / FreeRTOS_IoT_Libraries / jobs / jobs_notify_next / DemoTasks / JobsNotifyNextExamples.c
1 /*\r
2  * FreeRTOS Kernel V10.3.0\r
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /* This demo executes Jobs obtained from AWS IoT. An AWS IoT Job is used to define\r
29  * a set of remote operations that are sent to and executed on one or more devices\r
30  * connected to AWS IoT. Please refer to AWS documentation for more information\r
31  * about AWS IoT Jobs.\r
32  * https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html\r
33  *\r
34  * This demo creates a single application task that sets a callback for the\r
35  * jobs/notify-next topic and executes Jobs created from the AWS IoT console or AWS\r
36  * CLI. Please refer to AWS CLI documentation for more information in creating a\r
37  * Job document.\r
38  * https://docs.aws.amazon.com/cli/latest/reference/iot/create-job.html\r
39  *\r
40  * This demo expects Job documents to have an "action" JSON key. Actions can\r
41  * be one "print", "publish", or "exit".\r
42  * Print Jobs log a message to the local console, and must contain a "message",\r
43  * e.g. { "action": "print", "message": "Hello World!" }.\r
44  * Publish Jobs publish a message to an MQTT Topic. The Job document must\r
45  * contain a "message" and "topic" to publish to, e.g.\r
46  * { "action": "publish", "topic": "demo/jobs", "message": "Hello World!" }.\r
47  * The exit Job exits the demo. Sending { "action": "exit" } will end the program.\r
48  */\r
49 \r
50 /* Standard includes. */\r
51 #include <string.h>\r
52 #include <stdio.h>\r
53 \r
54 /* Kernel includes. */\r
55 #include "FreeRTOS.h"\r
56 #include "task.h"\r
57 \r
58 /* FreeRTOS+TCP includes. */\r
59 #include "FreeRTOS_IP.h"\r
60 \r
61 /* IoT SDK includes. */\r
62 #include "aws_iot_jobs.h"\r
63 #include "aws_iot_demo_profile.h"\r
64 #include "iot_mqtt.h"\r
65 #include "iot_taskpool_freertos.h"\r
66 #include "aws_iot_doc_parser.h"\r
67 #include "platform/iot_clock.h"\r
68 #include "platform/iot_threads.h"\r
69 #include "platform/iot_network_freertos.h"\r
70 \r
71 #include "atomic.h"\r
72 \r
73 /* Preprocessor check iot configuration. */\r
74 #include "aws_iot_setup_check.h"\r
75 \r
76 /* Demo specific includes. */\r
77 #include "demo_config.h"\r
78 \r
79 /*-----------------------------------------------------------*/\r
80 \r
81 /**\r
82  * @brief The keep-alive interval used for this example.\r
83  *\r
84  * An MQTT ping request will be sent periodically at this interval.\r
85  *\r
86  * @note: This value is set to zero to disable MQTT\r
87  * keep alive for the Windows simulator project.\r
88  * The FreeRTOS kernel does not accurately calculate time for the Windows\r
89  * Simulator. Therefore, MQTT PING Request messages may be sent\r
90  * at an incorrect time interval to the broker. If the broker does\r
91  * not receive a ping request within 1.5x the time sent in a\r
92  * connection request, the broker may close the connection.\r
93  * To enable the keep alive feature, set this value\r
94  * to the desired interval in seconds.\r
95  */\r
96 #define jobsexampleKEEP_ALIVE_SECONDS          ( 0 )\r
97 \r
98 /**\r
99  * @brief The timeout for MQTT operations in this example.\r
100  */\r
101 #define jobsexampleMQTT_TIMEOUT_MS             ( 5000 )\r
102 \r
103 /**\r
104  * @brief Use default timeout when calling AwsIotJobs_Init.\r
105  */\r
106 #define jobsexampleUSE_DEFAULT_MQTT_TIMEOUT    ( 0 )\r
107 \r
108 /**\r
109  * @brief The bit which is set in the demo task's notification value from the\r
110  * disconnect callback to inform the demo task about the MQTT disconnect.\r
111  */\r
112 #define jobsexampleDISCONNECTED_BIT            ( 1UL << 0UL )\r
113 \r
114 /**\r
115  * @brief The bit which is set in the demo task's notification value from the\r
116  * operation complete callback to inform the demo task to exit.\r
117  */\r
118 #define jobsexampleEXIT_BIT                    ( 1UL << 1UL )\r
119 \r
120 /**\r
121  * @brief Length of the client identifier for this demo.\r
122  */\r
123 #define jobsexampleCLIENT_IDENTIFIER_LENGTH    ( sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1 )\r
124 \r
125 /**\r
126  * @brief The JSON key of the Job ID.\r
127  *\r
128  * Job documents are in JSON documents received from the AWS IoT Jobs service.\r
129  * All such JSON documents will contain this key, whose value represents the unique\r
130  * identifier of a Job.\r
131  */\r
132 #define jobsexampleID_KEY                      "jobId"\r
133 \r
134 /**\r
135  * @brief The length of #jobsexampleID_KEY.\r
136  */\r
137 #define jobsexampleID_KEY_LENGTH               ( sizeof( jobsexampleID_KEY ) - 1 )\r
138 \r
139 /**\r
140  * @brief The JSON key of the Job document.\r
141  *\r
142  * Job documents are in JSON documents received from the AWS IoT Jobs service.\r
143  * All such JSON documents will contain this key, whose value is an application-specific\r
144  * Job document.\r
145  */\r
146 #define jobsexampleDOC_KEY                     "jobDocument"\r
147 \r
148 /**\r
149  * @brief The length of #jobsexampleDOC_KEY.\r
150  */\r
151 #define jobsexampleDOC_KEY_LENGTH              ( sizeof( jobsexampleDOC_KEY ) - 1 )\r
152 \r
153 /**\r
154  * @brief The JSON key whose value represents the action this demo should take.\r
155  *\r
156  * This demo program expects this key to be in the Job document. It is a key\r
157  * specific to this demo.\r
158  */\r
159 #define jobsexampleACTION_KEY                  "action"\r
160 \r
161 /**\r
162  * @brief The length of #jobsexampleACTION_KEY.\r
163  */\r
164 #define jobsexampleACTION_KEY_LENGTH           ( sizeof( jobsexampleACTION_KEY ) - 1 )\r
165 \r
166 /**\r
167  * @brief A message associated with the Job action.\r
168  *\r
169  * This demo program expects this key to be in the Job document if the "action"\r
170  * is either "publish" or "print". It represents the message that should be\r
171  * published or printed, respectively.\r
172  */\r
173 #define jobsexampleMESSAGE_KEY                 "message"\r
174 \r
175 /**\r
176  * @brief The length of #jobsexampleMESSAGE_KEY.\r
177  */\r
178 #define jobsexampleMESSAGE_KEY_LENGTH          ( sizeof( jobsexampleMESSAGE_KEY ) - 1 )\r
179 \r
180 /**\r
181  * @brief An MQTT topic associated with the Job "publish" action.\r
182  *\r
183  * This demo program expects this key to be in the Job document if the "action"\r
184  * is "publish". It represents the MQTT topic on which the message should be\r
185  * published.\r
186  */\r
187 #define jobsexampleTOPIC_KEY                   "topic"\r
188 \r
189 /**\r
190  * @brief The length of #jobsexampleTOPIC_KEY.\r
191  */\r
192 #define jobsexampleTOPIC_KEY_LENGTH            ( sizeof( jobsexampleTOPIC_KEY ) - 1 )\r
193 \r
194 /**\r
195  * @brief The minimum length of a string in a JSON Job document.\r
196  *\r
197  * At the very least the Job ID must have the quotes that identify it as a JSON\r
198  * string and 1 character for the string itself (the string must not be empty).\r
199  */\r
200 #define jobsexampleJSON_STRING_MIN_LENGTH      ( ( size_t ) 3 )\r
201 \r
202 /**\r
203  * @brief The maximum length of a Job ID.\r
204  *\r
205  * This limit is defined by AWS service limits. See the following page for more\r
206  * information.\r
207  *\r
208  * https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits\r
209  */\r
210 #define jobsexampleID_MAX_LENGTH               ( ( size_t ) 64 )\r
211 \r
212 /**\r
213  * @brief A value passed as context to #prvOperationCompleteCallback to specify that\r
214  * it should notify the demo task of an exit request.\r
215  */\r
216 #define jobsexampleSHOULD_EXIT                 ( ( void * ) ( ( intptr_t ) 1 ) )\r
217 \r
218 /**\r
219  * @brief Time to wait before exiting demo.\r
220  *\r
221  * The milliseconds to wait before exiting. This is because the MQTT Broker\r
222  * will disconnect us if we are idle too long, and we have disabled keep alive.\r
223  */\r
224 #define jobsexampleMS_BEFORE_EXIT              ( 10 * 60 * 1000 )\r
225 \r
226 /*-----------------------------------------------------------*/\r
227 \r
228 /**\r
229  * @brief Currently supported actions that a Job document can specify.\r
230  */\r
231 typedef enum _jobAction\r
232 {\r
233         JOB_ACTION_PRINT,   /**< Print a message. */\r
234         JOB_ACTION_PUBLISH, /**< Publish a message to an MQTT topic. */\r
235         JOB_ACTION_EXIT,    /**< Exit the demo. */\r
236         JOB_ACTION_UNKNOWN  /**< Unknown action. */\r
237 } _jobAction_t;\r
238 \r
239 /**\r
240  * @brief The task used to demonstrate Jobs.\r
241  *\r
242  * @param[in] pvParameters Parameters as passed at the time of task creation. Not\r
243  * used in this example.\r
244  */\r
245 static void prvJobsDemoTask( void * pvParameters );\r
246 \r
247 /**\r
248  * @brief The callback invoked by the MQTT library when the MQTT connection gets\r
249  * disconnected.\r
250  *\r
251  * @param[in] pvCallbackContext Callback context as provided at the time of\r
252  * connect.\r
253  * @param[in] pxCallbackParams Contains the reason why the MQTT connection was\r
254  * disconnected.\r
255  */\r
256 static void prvExample_OnDisconnect( void * pvCallbackContext,\r
257                                                                          IotMqttCallbackParam_t * pxCallbackParams );\r
258 \r
259 /**\r
260  * @brief Connects to the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT\r
261  * and awsiotdemoprofileAWS_MQTT_PORT.\r
262  */\r
263 static void prvMQTTConnect( void );\r
264 \r
265 /**\r
266  * @brief Disconnects from the MQTT broker gracefully by sending an MQTT\r
267  * DISCONNECT message.\r
268  */\r
269 static void prvMQTTDisconnect( void );\r
270 \r
271 /**\r
272  * @brief Set callback for publishes to the jobs/notify-next topic.\r
273  */\r
274 static void prvSetNotifyNextCallback( void );\r
275 \r
276 /**\r
277  * @brief Converts a string in a Job document to a #_jobAction_t.\r
278  *\r
279  * @param[in] pcAction The Job action as a string.\r
280  * @param[in] xActionLength The length of `pcAction`.\r
281  *\r
282  * @return A #_jobAction_t equivalent to the given string.\r
283  */\r
284 static _jobAction_t prvGetAction( const char * pcAction,\r
285                                                                   size_t xActionLength );\r
286 \r
287 /**\r
288  * @brief Extracts a JSON string from the Job document.\r
289  *\r
290  * @param[in] pcJsonDoc The JSON document to search.\r
291  * @param[in] xJsonDocLength Length of `pcJsonDoc`.\r
292  * @param[in] pcKey The JSON key to search for.\r
293  * @param[in] xKeyLength Length of `pcKey`.\r
294  * @param[out] ppcValue The extracted JSON value.\r
295  * @param[out] pxValueLength Length of ppcValue.\r
296  *\r
297  * @return `pdTRUE` if the key was found and the value is valid; `pdFALSE` otherwise.\r
298  */\r
299 static BaseType_t prvGetJsonString( const char * pcJsonDoc,\r
300                                                                         size_t xJsonDocLength,\r
301                                                                         const char * pcKey,\r
302                                                                         size_t xKeyLength,\r
303                                                                         const char ** ppcValue,\r
304                                                                         size_t * pxValueLength );\r
305 \r
306 /**\r
307  * @brief Job operation completion callback. This function is invoked when an\r
308  * asynchronous Job operation finishes.\r
309  *\r
310  * @param[in] pvCallbackContext Set to a non-NULL value to exit the demo.\r
311  * @param[in] pxCallbackParam Information on the Job operation that completed.\r
312  */\r
313 static void prvOperationCompleteCallback( void * pvCallbackContext,\r
314                                                                                   AwsIotJobsCallbackParam_t * pxCallbackParam );\r
315 \r
316 \r
317 /**\r
318  * @brief Process an action with a message, such as "print" or "publish".\r
319  *\r
320  * @param[in] xMqttConnection The MQTT connection to use if the action is "publish".\r
321  * @param[in] xAction Either #JOB_ACTION_PRINT or #JOB_ACTION_PUBLISH.\r
322  * @param[in] pcJobDoc A pointer to the Job document.\r
323  * @param[in] xJobDocLength The length of the Job document.\r
324  *\r
325  * @return #AWS_IOT_JOB_STATE_SUCCEEDED on success; #AWS_IOT_JOB_STATE_FAILED otherwise.\r
326  */\r
327 static AwsIotJobState_t prvProcessMessage( IotMqttConnection_t xMqttConnection,\r
328                                                                                    _jobAction_t xAction,\r
329                                                                                    const char * pcJobDoc,\r
330                                                                                    size_t xJobDocLength );\r
331 \r
332 /**\r
333  * @brief Process a Job received from the Notify Next callback.\r
334  *\r
335  * @param[in] pxJobInfo The parameter to the Notify Next callback that contains\r
336  * information about the received Job.\r
337  * @param[in] pcJobId A pointer to the Job ID.\r
338  * @param[in] xJobIdLength The length of the Job ID.\r
339  * @param[in] pcJobDoc A pointer to the Job document.\r
340  * @param[in] xJobDocLength The length of the Job document.\r
341  */\r
342 static void prvProcessJob( const AwsIotJobsCallbackParam_t * pxJobInfo,\r
343                                                    const char * pcJobId,\r
344                                                    size_t xJobIdLength,\r
345                                                    const char * pcJobDoc,\r
346                                                    size_t xJobDocLength );\r
347 \r
348 /**\r
349  * @brief Jobs Notify Next callback. This function is invoked when a new Job is\r
350  * received from the Jobs service.\r
351  *\r
352  * @param[in] pCallbackContext Ignored.\r
353  * @param[in] pxCallbackInfo Contains the received Job.\r
354  */\r
355 static void prvJobsCallback( void * pCallbackContext,\r
356                                                          AwsIotJobsCallbackParam_t * pxCallbackInfo );\r
357 \r
358 /*-----------------------------------------------------------*/\r
359 \r
360 /**\r
361  * @brief The MQTT connection handle used in this example.\r
362  */\r
363 static IotMqttConnection_t xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
364 \r
365 /*\r
366  * @brief The main task handle in this demo.\r
367  */\r
368 static TaskHandle_t xMainTaskHandle;\r
369 \r
370 /**\r
371  * @brief Parameters used to create the system task pool.\r
372  */\r
373 static const IotTaskPoolInfo_t xTaskPoolParameters =\r
374 {\r
375         /* Minimum number of threads in a task pool.\r
376          * Note the slimmed down version of the task\r
377          * pool used by this library does not auto-scale\r
378          * the number of tasks in the pool so in this\r
379          * case this sets the number of tasks in the\r
380          * pool. */\r
381         1,\r
382 \r
383         /* Maximum number of threads in a task pool.\r
384          * Note the slimmed down version of the task\r
385          * pool used by this library does not auto-scale\r
386          * the number of tasks in the pool so in this\r
387          * case this parameter is just ignored. */\r
388         1,\r
389 \r
390         /* Stack size for every task pool thread - in\r
391          * bytes, hence multiplying by the number of bytes\r
392          * in a word as configMINIMAL_STACK_SIZE is\r
393          * specified in words. */\r
394         configMINIMAL_STACK_SIZE * sizeof( portSTACK_TYPE ),\r
395         /* Priority for every task pool thread. */\r
396         tskIDLE_PRIORITY,\r
397 };\r
398 \r
399 /***************** Structures that define the connection. *********************/\r
400 \r
401 \r
402 static const struct IotNetworkServerInfo xMQTTBrokerInfo =\r
403 {\r
404         .pHostName = awsiotdemoprofileAWS_ENDPOINT,\r
405         .port = awsiotdemoprofileAWS_MQTT_PORT\r
406 };\r
407 \r
408 static struct IotNetworkCredentials xNetworkSecurityCredentials =\r
409 {\r
410         /* Optional TLS extensions. For this demo, they are disabled. */\r
411         .pAlpnProtos = NULL,\r
412         .maxFragmentLength = 0,\r
413 \r
414         /* SNI is enabled by default. */\r
415         .disableSni = false,\r
416 \r
417         /* Provide the certificate for validating the server. Only required for\r
418         demos using TLS. */\r
419         .pRootCa = awsiotdemoprofileAWS_CERTIFICATE_PEM,\r
420         .rootCaSize = sizeof( awsiotdemoprofileAWS_CERTIFICATE_PEM ),\r
421 \r
422         /* Strong mutual authentication to authenticate both the broker and\r
423          * the client. */\r
424         .pClientCert = awsiotdemoprofileCLIENT_CERTIFICATE_PEM,\r
425         .clientCertSize = sizeof( awsiotdemoprofileCLIENT_CERTIFICATE_PEM ),\r
426         .pPrivateKey = awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM,\r
427         .privateKeySize = sizeof( awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM )\r
428 };\r
429 \r
430 static IotMqttNetworkInfo_t xNetworkInfo =\r
431 {\r
432         /* No connection to the MQTT broker has been established yet and we want to\r
433          * establish a new connection. */\r
434         .createNetworkConnection = true,\r
435         .u.setup.pNetworkServerInfo = &( xMQTTBrokerInfo ),\r
436 \r
437         /* Set the TLS credentials for the new MQTT connection. */\r
438         .u.setup.pNetworkCredentialInfo = &xNetworkSecurityCredentials,\r
439 \r
440         /* Use FreeRTOS+TCP network interface. */\r
441         .pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,\r
442 \r
443         /* Setup the callback which is called when the MQTT connection is\r
444          * disconnected. The task handle is passed as the callback context which\r
445          * is used by the callback to send a task notification to this task.*/\r
446         .disconnectCallback.function = prvExample_OnDisconnect\r
447 };\r
448 \r
449 static const IotMqttConnectInfo_t xConnectInfo =\r
450 {\r
451         /* Set this flag to true if connecting to the AWS IoT MQTT broker. */\r
452         .awsIotMqttMode = false,\r
453 \r
454         /* Start with a clean session i.e. direct the MQTT broker to discard any\r
455          * previous session data. Also, establishing a connection with clean session\r
456          * will ensure that the broker does not store any data when this client\r
457          * gets disconnected. */\r
458         .cleanSession = true,\r
459 \r
460         /* Since we are starting with a clean session, there are no previous\r
461          * subscriptions to be restored. */\r
462         .pPreviousSubscriptions = NULL,\r
463         .previousSubscriptionCount = 0,\r
464 \r
465         /* We do not want to publish Last Will and Testament (LWT) message if the\r
466          * client gets disconnected. */\r
467         .pWillInfo = NULL,\r
468 \r
469         /* Send an MQTT PING request every minute to keep the connection open if\r
470          * there is no other MQTT traffic. */\r
471         .keepAliveSeconds = jobsexampleKEEP_ALIVE_SECONDS,\r
472 \r
473         /* The client identifier is used to uniquely identify this MQTT client to\r
474          * the MQTT broker.  In a production device the identifier can be something\r
475          * unique, such as a device serial number. */\r
476         .pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER,\r
477         .clientIdentifierLength = ( uint16_t ) sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1,\r
478 \r
479         /* This example does not authenticate the client and therefore username and\r
480          * password fields are not used. */\r
481         .pUserName = NULL,\r
482         .userNameLength = 0,\r
483         .pPassword = NULL,\r
484         .passwordLength = 0\r
485 };\r
486 /*-----------------------------------------------------------*/\r
487 \r
488 static void prvExample_OnDisconnect( void * pvCallbackContext,\r
489                                                                          IotMqttCallbackParam_t * pxCallbackParams )\r
490 {\r
491 TaskHandle_t xDemoTaskHandle = ( TaskHandle_t ) pvCallbackContext;\r
492 \r
493         /* Ensure that we initiated the disconnect. */\r
494         configASSERT( pxCallbackParams->u.disconnectReason == IOT_MQTT_DISCONNECT_CALLED );\r
495 \r
496         /* Inform the demo task about the disconnect. */\r
497         xTaskNotify( xDemoTaskHandle,\r
498                                  jobsexampleDISCONNECTED_BIT,\r
499                                  eSetBits /* Set the jobsexampleDISCONNECTED_BIT in the demo task's notification value. */\r
500                                  );\r
501 }\r
502 /*-----------------------------------------------------------*/\r
503 \r
504 void vStartJobsDemo( void )\r
505 {\r
506 TickType_t xShortDelay = ( TickType_t ) pdMS_TO_TICKS( ( TickType_t ) 500 );\r
507 \r
508         /* Wait a short time to allow receipt of the ARP replies. */\r
509         vTaskDelay( xShortDelay );\r
510 \r
511         /* This example uses a single application task, which in turn is used to\r
512          * connect, subscribe, publish, unsubscribe and disconnect from the MQTT\r
513          * broker. */\r
514         xTaskCreate( prvJobsDemoTask,             /* Function that implements the task. */\r
515                                  "JobsDemo",                       /* Text name for the task - only used for debugging. */\r
516                                  democonfigDEMO_STACKSIZE, /* Size of stack (in words, not bytes) to allocate for the task. */\r
517                                  NULL,                                   /* Task parameter - not used in this case. */\r
518                                  tskIDLE_PRIORITY,               /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */\r
519                                  NULL );                                   /* Used to pass out a handle to the created task - not used in this case. */\r
520 }\r
521 /*-----------------------------------------------------------*/\r
522 \r
523 static void prvJobsDemoTask( void * pvParameters )\r
524 {\r
525 IotMqttError_t xResult;\r
526 IotNetworkError_t xNetworkInit;\r
527 uint32_t ulNotificationValue = 0;\r
528 const TickType_t xNoDelay = ( TickType_t ) 0;\r
529 AwsIotJobsError_t xStatus = AWS_IOT_JOBS_SUCCESS;\r
530 AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
531 AwsIotJobsRequestInfo_t xRequestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;\r
532 \r
533         /* Remove compiler warnings about unused parameters. */\r
534         ( void ) pvParameters;\r
535 \r
536         xMainTaskHandle = xTaskGetCurrentTaskHandle();\r
537 \r
538         /* The MQTT library needs a task pool, so create the system task pool. */\r
539         xResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );\r
540         configASSERT( xResult == IOT_TASKPOOL_SUCCESS );\r
541 \r
542         /* Initialize the network stack abstraction for FreeRTOS. */\r
543         xNetworkInit = IotNetworkFreeRTOS_Init();\r
544         configASSERT( xNetworkInit == IOT_NETWORK_SUCCESS );\r
545 \r
546         /* MQTT library must be initialized before it can be used. This is just one\r
547          * time initialization. */\r
548         xResult = IotMqtt_Init();\r
549         configASSERT( xResult == IOT_MQTT_SUCCESS );\r
550 \r
551         /* Initialize Jobs library. */\r
552         xResult = AwsIotJobs_Init( jobsexampleUSE_DEFAULT_MQTT_TIMEOUT );\r
553         configASSERT( xResult == AWS_IOT_JOBS_SUCCESS );\r
554 \r
555         /****************************** Connect. ******************************/\r
556 \r
557         /* Establish a connection to the AWS IoT MQTT broker. This example connects to\r
558          * the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT and\r
559          * awsiotdemoprofileAWS_MQTT_PORT at the top of this file.\r
560          */\r
561         configPRINTF( ( "Attempt to connect to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
562         prvMQTTConnect();\r
563         configPRINTF( ( "Connected to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
564 \r
565         /* Don't expect any notifications to be pending yet. */\r
566         configASSERT( ulTaskNotifyTake( pdTRUE, xNoDelay ) == 0 );\r
567 \r
568         configPRINTF( ( "Setting callback for jobs/notify-next\r\n" ) );\r
569         prvSetNotifyNextCallback();\r
570 \r
571         /* Call DescribeAsync to see if there are any pending jobs. */\r
572         xRequestInfo.mqttConnection = xMQTTConnection;\r
573         xRequestInfo.pThingName = awsiotdemoprofileCLIENT_IDENTIFIER;\r
574         xRequestInfo.thingNameLength = jobsexampleCLIENT_IDENTIFIER_LENGTH;\r
575         xRequestInfo.pJobId = AWS_IOT_JOBS_NEXT_JOB;\r
576         xRequestInfo.jobIdLength = AWS_IOT_JOBS_NEXT_JOB_LENGTH;\r
577 \r
578         /* Use the same callback as notify-next so any pending jobs will be\r
579          * executed the same way. */\r
580         xCallbackInfo.function = prvJobsCallback;\r
581 \r
582         xStatus = AwsIotJobs_DescribeAsync( &xRequestInfo, AWS_IOT_JOBS_NO_EXECUTION_NUMBER, true, 0, &xCallbackInfo, NULL );\r
583         configPRINTF( ( "Describe queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
584 \r
585         /* Print out a short user guide to the console. The default logging\r
586          * limit of 255 characters can be changed in demo_logging.c, but breaking\r
587          * up the only instance of a 1000+ character string is more practical. */\r
588         configPRINTF( (\r
589                                           "\r\n"\r
590                                           "/*-----------------------------------------------------------*/\r\n"\r
591                                           "\r\n"\r
592                                           "The Jobs demo is now ready to accept Jobs.\r\n"\r
593                                           "Jobs may be created using the AWS IoT console or AWS CLI.\r\n"\r
594                                           "See the following link for more information.\r\n"\r
595                                           "\r\n" ) );\r
596         configPRINTF( (\r
597                                           "\r"\r
598                                           "https://docs.aws.amazon.com/cli/latest/reference/iot/create-job.html\r\n"\r
599                                           "\r\n"\r
600                                           "This demo expects Job documents to have an \"action\" JSON key.\r\n"\r
601                                           "The following actions are currently supported:\r\n" ) );\r
602         configPRINTF( (\r
603                                           "\r"\r
604                                           " - print          \r\n"\r
605                                           "   Logs a message to the local console. The Job document must also contain a \"message\".\r\n"\r
606                                           "   For example: { \"action\": \"print\", \"message\": \"Hello world!\"} will cause\r\n"\r
607                                           "   \"Hello world!\" to be printed on the console.\r\n" ) );\r
608         configPRINTF( (\r
609                                           "\r"\r
610                                           " - publish        \r\n"\r
611                                           "   Publishes a message to an MQTT topic. The Job document must also contain a \"message\" and \"topic\".\r\n" ) );\r
612         configPRINTF( (\r
613                                           "\r"\r
614                                           "   For example: { \"action\": \"publish\", \"topic\": \"demo/jobs\", \"message\": \"Hello world!\"} will cause\r\n"\r
615                                           "   \"Hello world!\" to be published to the topic \"demo/jobs\".\r\n" ) );\r
616         configPRINTF( (\r
617                                           "\r"\r
618                                           " - exit           \r\n"\r
619                                           "   Exits the demo program. This program will run until { \"action\": \"exit\" } is received.\r\n"\r
620                                           "\r\n"\r
621                                           "/*-----------------------------------------------------------*/\r\n" ) );\r
622 \r
623         /* Wait for an exit job to be received. If an exit job is not received within\r
624          * jobsexampleMS_BEFORE_EXIT, exit anyway. This is because we have disabled\r
625          * keep-alive, and the server will disconnect as after some time. */\r
626         xTaskNotifyWait( 0UL,                                      /* Don't clear any bits on entry. */\r
627                                          jobsexampleEXIT_BIT,      /* Clear bit on exit. */\r
628                                          &( ulNotificationValue ), /* Obtain the notification value. */\r
629                                          pdMS_TO_TICKS( jobsexampleMS_BEFORE_EXIT) );\r
630         /* Check was due to receiving an exit job. */\r
631         if( ( ulNotificationValue & jobsexampleEXIT_BIT ) != jobsexampleEXIT_BIT )\r
632         {\r
633                 configPRINTF( ( "Disconnecting as %u milliseconds have elapsed.\r\n", jobsexampleMS_BEFORE_EXIT ) );\r
634         }\r
635 \r
636         /* Disconnect MQTT gracefully. */\r
637         prvMQTTDisconnect();\r
638         configPRINTF( ( "Disconnected from %s\r\n\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
639 \r
640         /* Wait for the disconnect operation to complete which is informed to us\r
641          * by the disconnect callback (prvExample_OnDisconnect)by setting\r
642          * the jobsexampleDISCONNECTED_BIT in this task's notification value. */\r
643         xTaskNotifyWait( 0UL,                                             /* Don't clear any bits on entry. */\r
644                                          jobsexampleDISCONNECTED_BIT, /* Clear bit on exit. */\r
645                                          &( ulNotificationValue ),        /* Obtain the notification value. */\r
646                                          pdMS_TO_TICKS( jobsexampleMQTT_TIMEOUT_MS ) );\r
647         configASSERT( ( ulNotificationValue & jobsexampleDISCONNECTED_BIT ) == jobsexampleDISCONNECTED_BIT );\r
648 \r
649         configPRINTF( ( "prvJobsDemoTask() completed successfully. Total free heap is %u\r\n", xPortGetFreeHeapSize() ) );\r
650         configPRINTF( ( "Demo completed successfully.\r\n" ) );\r
651 \r
652         /* Clean up initialized libraries. */\r
653         AwsIotJobs_Cleanup();\r
654         IotMqtt_Cleanup();\r
655         IotNetworkFreeRTOS_Cleanup();\r
656 \r
657         /* FreeRTOS Tasks must _vTaskDelete( NULL )_ before exiting the function. */\r
658         vTaskDelete( NULL );\r
659 }\r
660 /*-----------------------------------------------------------*/\r
661 \r
662 static void prvMQTTConnect( void )\r
663 {\r
664 IotMqttError_t xResult;\r
665 \r
666         /* Set the context to pass into the disconnect callback function. */\r
667         xNetworkInfo.disconnectCallback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();\r
668 \r
669         /* Establish the connection to the MQTT broker - It is a blocking call and\r
670          * will return only when connection is complete or a timeout occurs. */\r
671         xResult = IotMqtt_Connect( &( xNetworkInfo ),\r
672                                                            &( xConnectInfo ),\r
673                                                            jobsexampleMQTT_TIMEOUT_MS,\r
674                                                            &( xMQTTConnection ) );\r
675         configASSERT( xResult == IOT_MQTT_SUCCESS );\r
676 }\r
677 /*-----------------------------------------------------------*/\r
678 \r
679 static void prvMQTTDisconnect( void )\r
680 {\r
681         /* Send a MQTT DISCONNECT packet to the MQTT broker to do a graceful\r
682          * disconnect. */\r
683         IotMqtt_Disconnect( xMQTTConnection,\r
684                                                 0 /* flags - 0 means a graceful disconnect by sending MQTT DISCONNECT. */\r
685                                                 );\r
686 }\r
687 /*-----------------------------------------------------------*/\r
688 \r
689 static void prvSetNotifyNextCallback( void )\r
690 {\r
691 AwsIotJobsError_t xCallbackStatus = AWS_IOT_JOBS_SUCCESS;\r
692 AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
693 \r
694         /* Set the jobs callback function. */\r
695         xCallbackInfo.function = prvJobsCallback;\r
696 \r
697         /************************ Set notify-next callbacks **********************/\r
698 \r
699         xCallbackStatus = AwsIotJobs_SetNotifyNextCallback( xMQTTConnection,\r
700                                                                                                                 awsiotdemoprofileCLIENT_IDENTIFIER,\r
701                                                                                                                 jobsexampleCLIENT_IDENTIFIER_LENGTH,\r
702                                                                                                                 0,\r
703                                                                                                                 &xCallbackInfo );\r
704 \r
705         configASSERT( xCallbackStatus == AWS_IOT_JOBS_SUCCESS );\r
706 }\r
707 /*-----------------------------------------------------------*/\r
708 \r
709 static _jobAction_t prvGetAction( const char * pcAction,\r
710                                                                   size_t xActionLength )\r
711 {\r
712 _jobAction_t xAction = JOB_ACTION_UNKNOWN;\r
713 \r
714         configASSERT( pcAction != NULL );\r
715 \r
716         if( strncmp( pcAction, "print", xActionLength ) == 0 )\r
717         {\r
718                 xAction = JOB_ACTION_PRINT;\r
719         }\r
720         else if( strncmp( pcAction, "publish", xActionLength ) == 0 )\r
721         {\r
722                 xAction = JOB_ACTION_PUBLISH;\r
723         }\r
724         else if( strncmp( pcAction, "exit", xActionLength ) == 0 )\r
725         {\r
726                 xAction = JOB_ACTION_EXIT;\r
727         }\r
728 \r
729         return xAction;\r
730 }\r
731 /*-----------------------------------------------------------*/\r
732 \r
733 static BaseType_t prvGetJsonString( const char * pcJsonDoc,\r
734                                                                         size_t xJsonDocLength,\r
735                                                                         const char * pcKey,\r
736                                                                         size_t xKeyLength,\r
737                                                                         const char ** ppcValue,\r
738                                                                         size_t * pxValueLength )\r
739 {\r
740 BaseType_t xKeyFound = pdFALSE;\r
741 \r
742         configASSERT( pcJsonDoc != NULL );\r
743         configASSERT( pcKey != NULL );\r
744 \r
745         /*\r
746          * Note: This parser used is specific for parsing AWS IoT document received\r
747          * through a mutually authenticated connection. This parser will not check\r
748          * for the correctness of the document as it is designed for low memory\r
749          * footprint rather than checking for correctness of the document. This\r
750          * parser is not meant to be used as a general purpose JSON parser.\r
751          */\r
752         xKeyFound = ( BaseType_t ) AwsIotDocParser_FindValue(\r
753                 pcJsonDoc,\r
754                 xJsonDocLength,\r
755                 pcKey,\r
756                 xKeyLength,\r
757                 ppcValue,\r
758                 pxValueLength );\r
759 \r
760         if( xKeyFound == pdTRUE )\r
761         {\r
762                 /* Exclude empty strings. */\r
763                 if( *pxValueLength < jobsexampleJSON_STRING_MIN_LENGTH )\r
764                 {\r
765                         xKeyFound = pdFALSE;\r
766                 }\r
767                 else\r
768                 {\r
769                         /* Adjust the value to remove the quotes. */\r
770                         ( *ppcValue )++;\r
771                         ( *pxValueLength ) -= 2;\r
772                 }\r
773         }\r
774 \r
775         return xKeyFound;\r
776 }\r
777 /*-----------------------------------------------------------*/\r
778 \r
779 static void prvOperationCompleteCallback( void * pvCallbackContext,\r
780                                                                                   AwsIotJobsCallbackParam_t * pxCallbackParam )\r
781 {\r
782         configASSERT( pxCallbackParam != NULL );\r
783 \r
784         /* This function is invoked when either a StartNext or Update completes. */\r
785         if( pxCallbackParam->callbackType == AWS_IOT_JOBS_START_NEXT_COMPLETE )\r
786         {\r
787                 configPRINTF( ( "Job StartNext complete with result %s.\r\n",\r
788                                                 AwsIotJobs_strerror( pxCallbackParam->u.operation.result ) ) );\r
789         }\r
790         else\r
791         {\r
792                 configPRINTF( ( "Job Update complete with result %s.\r\n",\r
793                                                 AwsIotJobs_strerror( pxCallbackParam->u.operation.result ) ) );\r
794         }\r
795 \r
796         /* If a non-NULL context is given, set the flag to exit the demo. */\r
797         if( pvCallbackContext != NULL )\r
798         {\r
799                 xTaskNotify( xMainTaskHandle,\r
800                                          jobsexampleEXIT_BIT,\r
801                                          eSetBits /* Set the jobsexampleEXIT_BIT in the demo task's notification value. */\r
802                                          );\r
803         }\r
804 }\r
805 /*-----------------------------------------------------------*/\r
806 \r
807 static AwsIotJobState_t prvProcessMessage( IotMqttConnection_t xMqttConnection,\r
808                                                                                    _jobAction_t xAction,\r
809                                                                                    const char * pcJobDoc,\r
810                                                                                    size_t xJobDocLength )\r
811 {\r
812 AwsIotJobState_t xStatus = AWS_IOT_JOB_STATE_SUCCEEDED;\r
813 IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
814 IotMqttPublishInfo_t xPublishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
815 const char * pcMessage = NULL, * pcTopic = NULL;\r
816 size_t xMessageLength = 0, xTopicLength = 0;\r
817 \r
818         configASSERT( pcJobDoc != NULL );\r
819 \r
820         /* Both "print" and "publish" require a "message" key. Search the Job\r
821          * document for this key. */\r
822         if( prvGetJsonString( pcJobDoc,\r
823                                                   xJobDocLength,\r
824                                                   jobsexampleMESSAGE_KEY,\r
825                                                   jobsexampleMESSAGE_KEY_LENGTH,\r
826                                                   &pcMessage,\r
827                                                   &xMessageLength ) == pdFALSE )\r
828         {\r
829                 configPRINTF( ( "Job document for \"print\" or \"publish\" does not contain a %s key.\r\n",\r
830                                                 jobsexampleMESSAGE_KEY ) );\r
831 \r
832                 xStatus = AWS_IOT_JOB_STATE_FAILED;\r
833         }\r
834 \r
835         if( xStatus == AWS_IOT_JOB_STATE_SUCCEEDED )\r
836         {\r
837                 if( xAction == JOB_ACTION_PRINT )\r
838                 {\r
839                         /* Print the given message if the action is "print". */\r
840                         configPRINTF( (\r
841                                                           "\r\n"\r
842                                                           "/*-----------------------------------------------------------*/\r\n"\r
843                                                           "\r\n"\r
844                                                           "%.*s\r\n"\r
845                                                           "\r\n"\r
846                                                           "/*-----------------------------------------------------------*/\r\n"\r
847                                                           "\r\n", xMessageLength, pcMessage ) );\r
848                 }\r
849                 else\r
850                 {\r
851                         /* Extract the topic if the action is "publish". */\r
852                         if( prvGetJsonString( pcJobDoc,\r
853                                                                   xJobDocLength,\r
854                                                                   jobsexampleTOPIC_KEY,\r
855                                                                   jobsexampleTOPIC_KEY_LENGTH,\r
856                                                                   &pcTopic,\r
857                                                                   &xTopicLength ) == pdFALSE )\r
858                         {\r
859                                 configPRINTF( ( "Job document for action \"publish\" does not contain a %s key.\r\n",\r
860                                                                 jobsexampleTOPIC_KEY ) );\r
861 \r
862                                 xStatus = AWS_IOT_JOB_STATE_FAILED;\r
863                         }\r
864 \r
865                         if( xStatus == AWS_IOT_JOB_STATE_SUCCEEDED )\r
866                         {\r
867                                 xPublishInfo.qos = IOT_MQTT_QOS_0;\r
868                                 xPublishInfo.pTopicName = pcTopic;\r
869                                 xPublishInfo.topicNameLength = ( uint16_t ) xTopicLength;\r
870                                 xPublishInfo.pPayload = pcMessage;\r
871                                 xPublishInfo.payloadLength = xMessageLength;\r
872 \r
873                                 xMqttStatus = IotMqtt_PublishAsync( xMqttConnection, &xPublishInfo, 0, NULL, NULL );\r
874 \r
875                                 if( xMqttStatus != IOT_MQTT_SUCCESS )\r
876                                 {\r
877                                         xStatus = AWS_IOT_JOB_STATE_FAILED;\r
878                                 }\r
879                         }\r
880                 }\r
881         }\r
882 \r
883         return xStatus;\r
884 }\r
885 /*-----------------------------------------------------------*/\r
886 \r
887 static void prvProcessJob( const AwsIotJobsCallbackParam_t * pxJobInfo,\r
888                                                    const char * pcJobId,\r
889                                                    size_t xJobIdLength,\r
890                                                    const char * pcJobDoc,\r
891                                                    size_t xJobDocLength )\r
892 {\r
893 AwsIotJobsError_t xStatus = AWS_IOT_JOBS_SUCCESS;\r
894 AwsIotJobsUpdateInfo_t xUpdateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;\r
895 AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
896 const char * pcAction = NULL;\r
897 size_t xActionLength = 0;\r
898 _jobAction_t xAction = JOB_ACTION_UNKNOWN;\r
899 AwsIotJobsRequestInfo_t xRequestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;\r
900 \r
901         configASSERT( pxJobInfo != NULL );\r
902         configASSERT( pcJobId != NULL );\r
903         configASSERT( pcJobDoc != NULL );\r
904 \r
905         configPRINTF( ( "Job document received: %.*s\r\n", xJobDocLength, pcJobDoc ) );\r
906 \r
907         xRequestInfo.mqttConnection = pxJobInfo->mqttConnection;\r
908         xRequestInfo.pThingName = pxJobInfo->pThingName;\r
909         xRequestInfo.thingNameLength = pxJobInfo->thingNameLength;\r
910         xRequestInfo.pJobId = pcJobId;\r
911         xRequestInfo.jobIdLength = xJobIdLength;\r
912 \r
913         /* Tell the Jobs service that the device has started working on the Job.\r
914          * Use the StartNext API to set the Job's status to IN_PROGRESS. */\r
915         xCallbackInfo.function = prvOperationCompleteCallback;\r
916 \r
917         xStatus = AwsIotJobs_StartNextAsync( &xRequestInfo, &xUpdateInfo, 0, &xCallbackInfo, NULL );\r
918 \r
919         configPRINTF( ( "Jobs StartNext queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
920 \r
921         /* Get the action for this device. */\r
922         if( prvGetJsonString( pcJobDoc,\r
923                                                   xJobDocLength,\r
924                                                   jobsexampleACTION_KEY,\r
925                                                   jobsexampleACTION_KEY_LENGTH,\r
926                                                   &pcAction,\r
927                                                   &xActionLength ) == pdTRUE )\r
928         {\r
929                 xAction = prvGetAction( pcAction, xActionLength );\r
930 \r
931                 switch( xAction )\r
932                 {\r
933                         case JOB_ACTION_EXIT:\r
934                                 xCallbackInfo.pCallbackContext = jobsexampleSHOULD_EXIT;\r
935                                 xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;\r
936                                 break;\r
937 \r
938                         case JOB_ACTION_PRINT:\r
939                         case JOB_ACTION_PUBLISH:\r
940                                 xUpdateInfo.newStatus = prvProcessMessage( pxJobInfo->mqttConnection,\r
941                                                                                                                    xAction,\r
942                                                                                                                    pcJobDoc,\r
943                                                                                                                    xJobDocLength );\r
944                                 break;\r
945 \r
946                         default:\r
947                                 configPRINTF( ( "Received Job document with unknown action %.*s.\r\n",\r
948                                                                 xActionLength,\r
949                                                                 pcAction ) );\r
950 \r
951                                 xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_FAILED;\r
952                                 break;\r
953                 }\r
954         }\r
955         else\r
956         {\r
957                 configPRINTF( ( "Received Job document does not contain an %s key.\r\n",\r
958                                                 jobsexampleACTION_KEY ) );\r
959 \r
960                 /* The given Job document is not valid for this demo. */\r
961                 xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_FAILED;\r
962         }\r
963 \r
964         configPRINTF( ( "Setting state of %.*s to %s.\r\n",\r
965                                         xJobIdLength,\r
966                                         pcJobId,\r
967                                         AwsIotJobs_StateName( xUpdateInfo.newStatus ) ) );\r
968 \r
969         /* Tell the Jobs service that the device has finished the Job. */\r
970         xStatus = AwsIotJobs_UpdateAsync( &xRequestInfo, &xUpdateInfo, 0, &xCallbackInfo, NULL );\r
971 \r
972         configPRINTF( ( "Jobs Update queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
973 }\r
974 /*-----------------------------------------------------------*/\r
975 \r
976 static void prvJobsCallback( void * pCallbackContext,\r
977                                                          AwsIotJobsCallbackParam_t * pxCallbackInfo )\r
978 {\r
979 BaseType_t xIdKeyFound = pdFALSE, xDocKeyFound = pdFALSE;\r
980 const char * pcJobId = NULL;\r
981 size_t xJobIdLength = 0;\r
982 const char * pcJobDoc = NULL;\r
983 size_t xJobDocLength = 0;\r
984 const char * pcRawDocument = NULL;\r
985 size_t xRawDocumentLength = 0;\r
986 \r
987         /* Silence warnings about unused parameters. */\r
988         ( void ) pCallbackContext;\r
989 \r
990         configASSERT( pxCallbackInfo != NULL );\r
991 \r
992         /* Check if this callback was called from a describe operation or\r
993          * due to notify-next. */\r
994         if( pxCallbackInfo->callbackType == AWS_IOT_JOBS_DESCRIBE_COMPLETE )\r
995         {\r
996                 pcRawDocument = pxCallbackInfo->u.operation.pResponse;\r
997                 xRawDocumentLength = pxCallbackInfo->u.operation.responseLength;\r
998         }\r
999         else\r
1000         {\r
1001                 pcRawDocument = pxCallbackInfo->u.callback.pDocument;\r
1002                 xRawDocumentLength = pxCallbackInfo->u.callback.documentLength;\r
1003         }\r
1004 \r
1005         /* Get the Job ID. */\r
1006         xIdKeyFound = prvGetJsonString( pcRawDocument,\r
1007                                                                         xRawDocumentLength,\r
1008                                                                         jobsexampleID_KEY,\r
1009                                                                         jobsexampleID_KEY_LENGTH,\r
1010                                                                         &pcJobId,\r
1011                                                                         &xJobIdLength );\r
1012 \r
1013         if( xIdKeyFound == pdTRUE )\r
1014         {\r
1015                 if( xJobIdLength > jobsexampleID_MAX_LENGTH )\r
1016                 {\r
1017                         configPRINTF( ( "Received Job ID %.*s longer than %lu, which is the "\r
1018                                                         "maximum allowed by AWS IoT. Ignoring Job.\r\n",\r
1019                                                         xJobIdLength,\r
1020                                                         pcJobId,\r
1021                                                         ( unsigned long ) jobsexampleID_MAX_LENGTH ) );\r
1022 \r
1023                         xIdKeyFound = pdFALSE;\r
1024                 }\r
1025                 else\r
1026                 {\r
1027                         configPRINTF( ( "Job %.*s received.\r\n", xJobIdLength, pcJobId ) );\r
1028                 }\r
1029         }\r
1030 \r
1031         /* Get the Job document.\r
1032          *\r
1033          * Note: This parser used is specific for parsing AWS IoT document received\r
1034          * through a mutually authenticated connection. This parser will not check\r
1035          * for the correctness of the document as it is designed for low memory\r
1036          * footprint rather than checking for correctness of the document. This\r
1037          * parser is not meant to be used as a general purpose JSON parser.\r
1038          */\r
1039         xDocKeyFound = ( BaseType_t ) AwsIotDocParser_FindValue(\r
1040                 pcRawDocument,\r
1041                 xRawDocumentLength,\r
1042                 jobsexampleDOC_KEY,\r
1043                 jobsexampleDOC_KEY_LENGTH,\r
1044                 &pcJobDoc,\r
1045                 &xJobDocLength );\r
1046 \r
1047         /* When both the Job ID and Job document are available, process the Job. */\r
1048         if( ( xIdKeyFound == pdTRUE ) && ( xDocKeyFound == pdTRUE ) )\r
1049         {\r
1050                 /* Process the Job document. */\r
1051                 prvProcessJob( pxCallbackInfo,\r
1052                                            pcJobId,\r
1053                                            xJobIdLength,\r
1054                                            pcJobDoc,\r
1055                                            xJobDocLength );\r
1056         }\r
1057         else\r
1058         {\r
1059                 /* The Jobs service sends an empty Job document when all Jobs are complete. */\r
1060                 if( ( xIdKeyFound == pdFALSE ) && ( xDocKeyFound == pdFALSE ) )\r
1061                 {\r
1062                         configPRINTF( (\r
1063                                                           "\r\n"\r
1064                                                           "/*-----------------------------------------------------------*/\r\n"\r
1065                                                           "\r\n"\r
1066                                                           "All available Jobs complete.\r\n"\r
1067                                                           "\r\n"\r
1068                                                           "/*-----------------------------------------------------------*/\r\n"\r
1069                                                           "\r\n" ) );\r
1070                 }\r
1071                 else\r
1072                 {\r
1073                         configPRINTF( ( "Received an invalid Job document: %.*s\r\n",\r
1074                                                         xRawDocumentLength,\r
1075                                                         pcRawDocument ) );\r
1076                 }\r
1077         }\r
1078 }\r
1079 /*-----------------------------------------------------------*/\r