blob: 71c4bbf61a24d8e6290be3ed79d6d6497b4504f4 [file] [log] [blame]
Qiufang Dai35c31332020-05-13 15:29:06 +08001/*
2 * Amazon FreeRTOS Shadow V1.0.3
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * http://aws.amazon.com/freertos
23 * http://www.FreeRTOS.org
24 */
25
26/**
27 * @file aws_shadow.c
28 * @brief Shadow API. Provide simple function to modify/create/delete Things shadows.
29 */
30
31/* C library includes. */
32#include <stdio.h>
33#include <string.h>
34#include <stdlib.h>
35
36/* FreeRTOS includes. */
37#include "FreeRTOS.h"
38#include "task.h"
39#include "semphr.h"
40
41/* AWS includes. */
42#include "aws_shadow_config.h"
43#include "aws_shadow_config_defaults.h"
44#include "aws_shadow.h"
45#include "aws_shadow_json.h"
46
47/**
48 * @brief Format strings for the AWS IoT Shadow MQTT topics.
49 * Refer to docs.aws.amazon.com/iot/latest/developerguide/device-shadow-mqtt.html
50 * If the MQTT topics change, update them here. Use %s for 'thingName'.
51 *
52 * These topics may also be changed to trim Thing Shadow messages.
53 * Refer to docs.aws.amazon.com/iot/latest/developerguide/using-device-shadows.html#device-shadow-trim-messages
54 */
55/** @{ */
56#define shadowTOPIC_PREFIX "$aws/things/%s/shadow/" /* All shadow topics begin with this. */
57#define shadowTOPIC_OPERATION_UPDATE "update"
58#define shadowTOPIC_OPERATION_GET "get"
59#define shadowTOPIC_OPERATION_DELETE "delete"
60#define shadowTOPIC_SUFFIX_ACCEPTED "/accepted" /* All accepted topics end with this. */
61#define shadowTOPIC_SUFFIX_REJECTED "/rejected" /* All rejected topics end with this. */
62
63#define shadowTOPIC_UPDATE shadowTOPIC_PREFIX shadowTOPIC_OPERATION_UPDATE
64#define shadowTOPIC_UPDATE_ACCEPTED shadowTOPIC_UPDATE shadowTOPIC_SUFFIX_ACCEPTED
65#define shadowTOPIC_UPDATE_REJECTED shadowTOPIC_UPDATE shadowTOPIC_SUFFIX_REJECTED
66#define shadowTOPIC_UPDATE_DOCUMENTS shadowTOPIC_UPDATE "/documents"
67#define shadowTOPIC_UPDATE_DELTA shadowTOPIC_UPDATE "/delta"
68#define shadowTOPIC_GET shadowTOPIC_PREFIX shadowTOPIC_OPERATION_GET
69#define shadowTOPIC_GET_ACCEPTED shadowTOPIC_GET shadowTOPIC_SUFFIX_ACCEPTED
70#define shadowTOPIC_GET_REJECTED shadowTOPIC_GET shadowTOPIC_SUFFIX_REJECTED
71#define shadowTOPIC_DELETE shadowTOPIC_PREFIX shadowTOPIC_OPERATION_DELETE
72#define shadowTOPIC_DELETE_ACCEPTED shadowTOPIC_DELETE shadowTOPIC_SUFFIX_ACCEPTED
73#define shadowTOPIC_DELETE_REJECTED shadowTOPIC_DELETE shadowTOPIC_SUFFIX_REJECTED
74/** @} */
75
76/** Maximum length of a Shadow MQTT topic. 128 is currently the longest Thing
77* Name that AWS IoT accepts. shadowTOPIC_UPDATE_DOCUMENTS is currently the
78* longest topic. All Shadow MQTT topics should be shorter than this value. */
79#define configMAX_THING_NAME_LENGTH 128
80#define shadowTOPIC_BUFFER_LENGTH ( configMAX_THING_NAME_LENGTH + ( int16_t ) sizeof( shadowTOPIC_UPDATE_DOCUMENTS ) )
81
82#if shadowconfigENABLE_DEBUG_LOGS == 1
83 #define Shadow_debug_printf( X ) configPRINTF( X )
84#else
85 #define Shadow_debug_printf( X )
86#endif
87
88/**
89 * @brief Shadow operation that can be parsed from MQTT topics.
90 *
91 */
92typedef enum ShadowOperationName
93{
94 eShadowOperationUpdate,
95 eShadowOperationGet,
96 eShadowOperationDelete,
97 eShadowOperationUpdateDocuments,
98 eShadowOperationUpdateDelta,
99 eShadowOperationDeletedByAnother,
100 eShadowOperationOther
101} ShadowOperationName_t;
102
103/**
104 * @brief Data on the Shadow operation currently in progress.
105 *
106 */
107typedef struct ShadowOperationData
108{
109 ShadowOperationName_t xOperationInProgress;
110 ShadowOperationParams_t * pxOperationParams;
111} ShadowOperationData_t;
112
113/**
114 * @brief Data on the timeout by which a function needs to complete.
115 *
116 */
117typedef struct TimeOutData
118{
119 TimeOut_t xTimeOut;
120 TickType_t xTicksRemaining;
121} TimeOutData_t;
122
123/**
124 * @brief Data passed to prvShadowOperation.
125 *
126 */
127typedef struct ShadowOperationCallParams
128{
129 BaseType_t xShadowClientID;
130
131 ShadowOperationName_t xOperationName;
132 const char * pcOperationName;
133
134 /* Operation MQTT topics. */
135 const char * pcOperationTopic;
136 const char * pcOperationAcceptedTopic;
137 const char * pcOperationRejectedTopic;
138
139 /* The message to publish to MQTT topics. */
140 const char * pcPublishMessage;
141 uint32_t ulPublishMessageLength;
142
143 ShadowOperationParams_t * pxOperationParams;
144 TickType_t xTimeoutTicks;
145} ShadowOperationCallParams_t;
146
147/**
148 * @brief An entry of the callback catalog.
149 *
150 * The callback catalog is the member of the Shadow Client that stores the
151 * callback functions associated with Thing Names.
152 */
153typedef struct CallbackCatalogEntry
154{
155 ShadowCallbackParams_t xCallbackInfo;
156 BaseType_t xInUse;
157} CallbackCatalogEntry_t;
158
159/**
160 * @brief The Shadow Client.
161 *
162 */
163typedef struct ShadowClient
164{
165 /* MQTT Client handle. */
166 MQTTAgentHandle_t xMQTTClient;
167
168 /* Shadow Client flags. */
169 BaseType_t xInUse;
170 BaseType_t xUpdateSubscribed;
171 BaseType_t xGetSubscribed;
172 BaseType_t xDeleteSubscribed;
173
174 /* Synchronization mechanisms. */
175 SemaphoreHandle_t xOperationMutex; /* Allows only one in-progress operation. */
176 SemaphoreHandle_t xCallbackSemaphore; /* Communication with callbacks. */
177 StaticSemaphore_t xOperationMutexBuffer;
178 StaticSemaphore_t xCallbackSemaphoreBuffer;
179
180 /* Data shared between blocking function and MQTT callback. */
181 ShadowOperationData_t * pxOperationData;
182
183 /* The callback functions pass data to the API calls by setting xOperationResult.
184 * This value is volatile to ensure that the compiler knows the value can change
185 * without anything happening in the API call. */
186 volatile ShadowReturnCode_t xOperationResult;
187
188 /* Callback catalog stores Thing Names and registered callbacks. */
189 CallbackCatalogEntry_t pxCallbackCatalog[ shadowconfigMAX_THINGS_WITH_CALLBACKS ];
190
191 /* Stores the topic of the in-progress operation. Some of the Shadow API's
192 * static functions depend on this buffer remaining the same throughout a
193 * Shadow API operation. Therefore, only prvShadowOperation (and the static
194 * functions called by prvShadowOperation) should modify the contents of this
195 * buffer. */
196 uint8_t pucTopicBuffer[ shadowTOPIC_BUFFER_LENGTH ];
197} ShadowClient_t;
198
199/**
200 * @brief Searches memory for a not-in-use Shadow Client.
201 *
202 */
203static BaseType_t prvGetFreeShadowClient( void );
204
205/**
206 * @brief Returns the slot index of callback catalog for matching thing name, if thing name not found
207 * returns the next available unused slot index in catalog.
208 *
209 */
210static BaseType_t prvGetCallbackCatalogEntry( CallbackCatalogEntry_t * const pxCallbackCatalog,
211 const char * const pcThingName );
212
213
214/**
215 * @brief Wrapper function for MQTT API calls; converts MQTTAgentReturnCode_t to
216 * ShadowReturnCode_t and optionally prints debug messages.
217 *
218 */
219static ShadowReturnCode_t prvConvertMQTTReturnCode( MQTTAgentReturnCode_t xMQTTReturn,
220 ShadowClientHandle_t xShadowClientHandle,
221 const char * const pcDebugMessageSubject );
222
223
224/**
225 * @brief Function to register a callback by subscribing to Shadow MQTT topics or removes a callback
226 * Function by unsubscribing to Shadow MQTT topics
227 *
228 */
229static ShadowReturnCode_t prvRegisterCallback( BaseType_t xShadowClientID,
230 const void ** const ppvOldCallback,
231 const void ** const ppvNewCallback,
232 const char * const pcThingName,
233 const uint8_t * const pucTopicFormat,
234 TickType_t xTimeoutTicks );
235
236/**
237 * @brief Subscribes to an accepted and rejected topic
238 *
239 */
240static ShadowReturnCode_t prvShadowSubscribeToAcceptedRejected( BaseType_t
241 xShadowClientID,
242 const char * const pcThingName,
243 const char * const pcAcceptedTopic,
244 const char * const pcRejectedTopic,
245 TimeOutData_t * const pxTimeOutData );
246
247/**
248 * @brief Unsubscribe to an accepted and rejected topic.
249 *
250 */
251static ShadowReturnCode_t prvShadowUnsubscribeFromAcceptedRejected( BaseType_t xShadowClientID,
252 const char * const pcThingName,
253 const char * const pcAcceptedTopic,
254 const char * const pcRejectedTopic,
255 TimeOutData_t * const pxTimeOutData );
256
257/**
258 * @brief Universal MQTT callback; parses topics for Thing Name and operation matches.
259 *
260 */
261static BaseType_t prvShadowMQTTCallback( void * pvUserData,
262 const MQTTAgentCallbackParams_t * const pxCallbackParams );
263
264/**
265 * @brief Parses "accepted" or "rejected" from MQTT topic.
266 *
267 */
268static ShadowReturnCode_t prvParseShadowOperationStatus( const uint8_t * const
269 pucTopic,
270 uint16_t usTopicLength );
271
272/**
273 * @briefMatch topic with registered callback and return reference to catalog entry found or NULL in not found.
274 */
275static const CallbackCatalogEntry_t * prvMatchCallbackTopic( const ShadowClient_t *
276 const pxShadowClient,
277 const uint8_t * const pucTopic,
278 uint16_t usTopicLength,
279 ShadowOperationName_t * const pxOperationName );
280
281/**
282 * @brief Update callback for Shadow Operations.
283 */
284static void prvShadowUpdateCallback( BaseType_t xShadowClientID,
285 ShadowReturnCode_t xResult,
286 const ShadowOperationParams_t * const pxParams,
287 const char * const pcData,
288 uint32_t ulDataLength );
289
290/**
291 * @brief Get callback for shadow operations
292 */
293static void prvShadowGetCallback( BaseType_t xShadowClientID,
294 ShadowReturnCode_t xResult,
295 ShadowOperationParams_t * const pxParams,
296 const char * const pcData,
297 uint32_t ulDataLength,
298 MQTTBufferHandle_t xBuffer );
299
300/**
301 * @briefDelete callback for shadow operations
302 */
303static void prvShadowDeleteCallback( BaseType_t xShadowClientID,
304 ShadowReturnCode_t xResult,
305 const char * const pcData,
306 uint32_t ulDataLength );
307
308/**
309 * @brief Handles error codes and messages in callbacks.
310 */
311static ShadowReturnCode_t prvGetErrorCodeAndMessage( const char * const pcData,
312 uint32_t ulDataLength,
313 BaseType_t xShadowClientID,
314 const char * const pcOperationName );
315
316/**
317 * @briefShadow Operation common code.
318 */
319static ShadowReturnCode_t prvShadowOperation( ShadowOperationCallParams_t * pxParams );
320
321static void prvSetSubscribedFlag( ShadowClient_t * const pxShadowClient,
322 ShadowOperationName_t xOperationName,
323 BaseType_t ucValue );
324
325static uint8_t prvGetSubscribedFlag( const ShadowClient_t * const pxShadowClient,
326 ShadowOperationName_t xOperationName );
327
328/**
329 * @brief Memory allocated to store Shadow Clients.
330 */
331static ShadowClient_t pxShadowClients[ shadowconfigMAX_CLIENTS ];
332
333/**
334 * @brief Custom prvCreateTopic function since prvCreateTopic is not MISRA 2012 compliant (rule 21.6) .
335 */
336static uint16_t prvCreateTopic( char * pcTopicString,
337 const uint16_t usBufferLength,
338 const char * pcTopicFormat,
339 const char * pcthingName );
340
341/*-----------------------------------------------------------*/
342
343static BaseType_t prvGetFreeShadowClient( void )
344{
345 BaseType_t xIterator, xReturn = -1;
346
347 taskENTER_CRITICAL();
348 {
349 for( xIterator = 0; xIterator < shadowconfigMAX_CLIENTS; xIterator++ )
350 {
351 if( pxShadowClients[ xIterator ].xInUse == pdFALSE )
352 {
353 pxShadowClients[ xIterator ].xInUse = pdTRUE;
354 xReturn = xIterator;
355 break;
356 }
357 }
358 }
359 taskEXIT_CRITICAL();
360
361 return xReturn;
362}
363/*-----------------------------------------------------------*/
364
365static BaseType_t prvGetCallbackCatalogEntry( CallbackCatalogEntry_t * const pxCallbackCatalog,
366 const char * const pcThingName ) /*_RB_ I find the name of this function confusing compared to what it is actually doing. */
367{ /* Todo: sub manager. */
368 CallbackCatalogEntry_t * pxCallbackCatalogEntry;
369 BaseType_t xIterator, xReturn = -1, xThingNameFound = pdFALSE;
370 size_t xThingNameLengh;
371 size_t xThingName_cb_Lengh;
372
373 taskENTER_CRITICAL();
374 {
375 for( xIterator = 0; xIterator < shadowconfigMAX_THINGS_WITH_CALLBACKS; xIterator++ )
376 {
377 pxCallbackCatalogEntry = &( pxCallbackCatalog[ xIterator ] );
378
379 if( pxCallbackCatalogEntry->xInUse == pdFALSE )
380 {
381 xReturn = xIterator;
382 }
383 else
384 {
385 xThingNameLengh = strlen( pxCallbackCatalogEntry->xCallbackInfo.pcThingName );
386 xThingName_cb_Lengh = strlen( pcThingName );
387
388 if( xThingNameLengh == xThingName_cb_Lengh )
389 {
390 if( strncmp( pcThingName,
391 pxCallbackCatalogEntry->xCallbackInfo.pcThingName,
392 xThingNameLengh ) == 0 )
393 {
394 xReturn = xIterator;
395 xThingNameFound = pdTRUE;
396 break;
397 }
398 }
399 }
400 }
401 }
402 taskEXIT_CRITICAL();
403
404 if( xThingNameFound == pdFALSE )
405 {
406 pxCallbackCatalog[ xReturn ].xInUse = pdTRUE;
407 pxCallbackCatalog[ xReturn ].xCallbackInfo.pcThingName = pcThingName;
408 }
409
410 return xReturn;
411}
412
413/*-----------------------------------------------------------*/
414
415static ShadowReturnCode_t prvConvertMQTTReturnCode( MQTTAgentReturnCode_t xMQTTReturn,
416 ShadowClientHandle_t xShadowClientHandle,
417 const char * const pcDebugMessageSubject )
418{
419 ShadowReturnCode_t xReturn = eShadowUnknown;
420
421 switch( xMQTTReturn )
422 {
423 case eMQTTAgentSuccess:
424 Shadow_debug_printf( ( "[Shadow %d] MQTT: %s succeeded.\r\n",
425 ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
426 pcDebugMessageSubject ) );
427 xReturn = eShadowSuccess;
428 break;
429
430 case eMQTTAgentTimeout:
431 Shadow_debug_printf( ( "[Shadow %d] MQTT: %s timed out.\r\n",
432 ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
433 pcDebugMessageSubject ) );
434 xReturn = eShadowTimeout;
435 break;
436
437 case eMQTTAgentFailure:
438 default:
439 Shadow_debug_printf( ( "[Shadow %d] MQTT: %s failed.\r\n",
440 ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
441 pcDebugMessageSubject ) );
442 xReturn = eShadowFailure;
443 break;
444 }
445
446 /* Prevent compiler warning in case Shadow_debug_printf() is not defined. */
447 ( void ) xShadowClientHandle;
448 ( void ) pcDebugMessageSubject;
449
450 return xReturn;
451}
452
453/*-----------------------------------------------------------*/
454
455static uint16_t prvCreateTopic( char * pcTopicString,
456 const uint16_t usBufferLength,
457 const char * pcTopicFormat,
458 const char * pcthingName )
459{
460 uint16_t usTopicFormatIdx;
461 uint16_t usThingIdx;
462 uint16_t usTopicFormatSize;
463 uint16_t usThingNameSize;
464 uint16_t usTopicSize;
465 uint16_t usTopicIdx = 0;
466 char cCurrentChar;
467 char cPrevChar = '0';
468
469 usTopicFormatSize = ( uint16_t ) strlen( pcTopicFormat );
470 usThingNameSize = ( uint16_t ) strlen( pcthingName );
471
472 /* Remove 2 because %s character is not printed. */
473 usTopicSize = usTopicFormatSize + usThingNameSize - ( uint16_t ) 2;
474
475 configASSERT( usTopicSize < usBufferLength );
476
477 for( usTopicFormatIdx = 0;
478 usTopicFormatIdx < usTopicFormatSize;
479 usTopicFormatIdx++ )
480 {
481 cCurrentChar = pcTopicFormat[ usTopicFormatIdx ];
482
483 if( ( cPrevChar == '%' ) && ( cCurrentChar == 's' ) )
484 {
485 for( usThingIdx = 0;
486 usThingIdx < usThingNameSize;
487 usThingIdx++ )
488 {
489 pcTopicString[ usThingIdx + usTopicFormatIdx - ( uint16_t ) 1 ]
490 = ( char ) pcthingName[ usThingIdx ];
491 }
492
493 usTopicIdx += usThingNameSize - ( uint16_t ) 1; /* Remove 1 because %s characters are not printed*/
494 }
495 else
496 {
497 pcTopicString[ usTopicIdx ] = cCurrentChar;
498 usTopicIdx++;
499 }
500
501 cPrevChar = cCurrentChar;
502 }
503
504 pcTopicString[ usTopicIdx ] = '\0';
505
506 return usTopicSize;
507}
508
509/*-----------------------------------------------------------*/
510
511static ShadowReturnCode_t prvRegisterCallback( BaseType_t xShadowClientID,
512 const void ** const ppvOldCallback,
513 const void ** const ppvNewCallback,
514 const char * const pcThingName,
515 const uint8_t * const pucTopicFormat,
516 TickType_t xTimeoutTicks )
517{
518 uint8_t pucTopicString[ shadowTOPIC_BUFFER_LENGTH ];
519 ShadowReturnCode_t xReturn = eShadowSuccess;
520 MQTTAgentSubscribeParams_t xSubscribeParams;
521 MQTTAgentUnsubscribeParams_t xUnsubscribeParams;
522 ShadowClient_t * pxShadowClient;
523 MQTTAgentReturnCode_t xMQTTReturn;
524 uint16_t usTopicLength;
525
526 usTopicLength = prvCreateTopic( ( char * ) pucTopicString, shadowTOPIC_BUFFER_LENGTH,
527 ( const char * ) pucTopicFormat, pcThingName );
528 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
529
530 if( *ppvOldCallback != NULL )
531 {
532 xUnsubscribeParams.usTopicLength = usTopicLength;
533 xUnsubscribeParams.pucTopic = pucTopicString;
534
535 xMQTTReturn = MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
536 &xUnsubscribeParams,
537 xTimeoutTicks );
538
539 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
540 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
541 "Unsubscribe from callback topic" );
542 }
543
544 /* Registering a new callback; subscribe to topic. */
545 if( *ppvNewCallback != NULL )
546 {
547 xSubscribeParams.usTopicLength = usTopicLength;
548 xSubscribeParams.pucTopic = pucTopicString;
549
550 #if( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )
551 xSubscribeParams.pvPublishCallbackContext = NULL;
552 xSubscribeParams.pxPublishCallback = NULL;
553 #endif /* mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT */
554
555 /* Shadow service always publishes QoS 1, regardless of the value below. */
556 xSubscribeParams.xQoS = eMQTTQoS1;
557
558 xMQTTReturn = MQTT_AGENT_Subscribe( pxShadowClient->xMQTTClient,
559 &xSubscribeParams,
560 xTimeoutTicks );
561
562 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
563 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
564 "Subscribe to callback topic" );
565 }
566
567 /* Change the callback. */
568 if( xReturn == eShadowSuccess )
569 {
570 *ppvOldCallback = ( *ppvNewCallback );
571 }
572
573 return xReturn;
574}
575
576/*-----------------------------------------------------------*/
577
578static ShadowReturnCode_t prvShadowSubscribeToAcceptedRejected( BaseType_t
579 xShadowClientID,
580 const char * const pcThingName,
581 const char * const
582 pcAcceptedTopic,
583 const char * const pcRejectedTopic,
584 TimeOutData_t * const pxTimeOutData )
585{
586 ShadowReturnCode_t xReturn;
587 ShadowClient_t * pxShadowClient;
588 MQTTAgentSubscribeParams_t xSubscribeParams;
589 MQTTAgentUnsubscribeParams_t xUnsubscribeParams;
590 MQTTAgentReturnCode_t xMQTTReturn;
591 TickType_t xTimeoutTicks;
592
593 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
594
595 /* MQTT subscription parameters. */
596 xSubscribeParams.pucTopic = pxShadowClient->pucTopicBuffer;
597 /* Shadow service always publishes QoS 1, regardless of the value below. */
598 xSubscribeParams.xQoS = eMQTTQoS1;
599
600 #if( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )
601 xSubscribeParams.pvPublishCallbackContext = NULL;
602 xSubscribeParams.pxPublishCallback = NULL;
603 #endif /* mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT */
604
605 /* Fill the accepted topic. */
606 xSubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
607 shadowTOPIC_BUFFER_LENGTH,
608 pcAcceptedTopic,
609 pcThingName );
610
611 xMQTTReturn = MQTT_AGENT_Subscribe( pxShadowClient->xMQTTClient,
612 &xSubscribeParams,
613 pxTimeOutData->xTicksRemaining );
614
615 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
616 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
617 "Subscribe to accepted topic" );
618
619 if( xReturn == eShadowSuccess )
620 {
621 /* Fill the rejected topic. */
622 xSubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
623 shadowTOPIC_BUFFER_LENGTH,
624 pcRejectedTopic, pcThingName );
625
626 xMQTTReturn = MQTT_AGENT_Subscribe( pxShadowClient->xMQTTClient,
627 &xSubscribeParams,
628 pxTimeOutData->xTicksRemaining );
629
630 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
631 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
632 "Subscribe to rejected topic" );
633
634 if( xReturn != eShadowSuccess )
635 {
636 xUnsubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
637 shadowTOPIC_BUFFER_LENGTH,
638 pcAcceptedTopic,
639 pcThingName );
640
641 xUnsubscribeParams.pucTopic = pxShadowClient->pucTopicBuffer;
642
643 xTimeoutTicks = pdMS_TO_TICKS( shadowconfigCLEANUP_TIME_MS );
644
645 ( void ) MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
646 &xUnsubscribeParams,
647 xTimeoutTicks );
648
649 ( void ) prvConvertMQTTReturnCode( xMQTTReturn,
650 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
651 "Cleanup: Unsubscribe from accepted topic" );
652 }
653 }
654
655 return xReturn;
656}
657
658/*-----------------------------------------------------------*/
659static ShadowReturnCode_t prvShadowUnsubscribeFromAcceptedRejected( BaseType_t
660 xShadowClientID,
661 const char * const pcThingName,
662 const char * const
663 pcAcceptedTopic,
664 const char * const pcRejectedTopic,
665 TimeOutData_t * const pxTimeOutData )
666{
667 ShadowReturnCode_t xReturn = eShadowFailure;
668 ShadowClient_t * pxShadowClient;
669 MQTTAgentUnsubscribeParams_t xUnsubscribeParams;
670 MQTTAgentReturnCode_t xMQTTReturn;
671
672 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
673
674 /* MQTT unsubscribe parameters. */
675 xUnsubscribeParams.pucTopic = pxShadowClient->pucTopicBuffer;
676
677 if( pcAcceptedTopic != NULL )
678 {
679 /* Fill the accepted topic. */
680 xUnsubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
681 shadowTOPIC_BUFFER_LENGTH,
682 pcAcceptedTopic,
683 pcThingName );
684
685 xMQTTReturn = MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
686 &xUnsubscribeParams,
687 pxTimeOutData->xTicksRemaining );
688
689 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
690 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
691 "Unsubscribe from accepted topic" );
692 }
693
694 if( pcRejectedTopic != NULL )
695 {
696 /* Fill the rejected topic. */
697 xUnsubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
698 shadowTOPIC_BUFFER_LENGTH,
699 pcRejectedTopic,
700 pcThingName );
701
702 xMQTTReturn = MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
703 &xUnsubscribeParams,
704 pxTimeOutData->xTicksRemaining );
705
706 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
707 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
708 "Unsubscribe from rejected topic" );
709 }
710
711 return xReturn;
712}
713/*-----------------------------------------------------------*/
714
715static BaseType_t prvShadowMQTTCallback( void * pvUserData,
716 const MQTTAgentCallbackParams_t * const pxCallbackParams )
717{
718 BaseType_t xOperationMatched = pdFALSE;
719 ShadowClient_t * pxShadowClient;
720 const MQTTPublishData_t * pxPublishData;
721 ShadowOperationName_t xOperationName;
722 ShadowReturnCode_t xResult;
723 const CallbackCatalogEntry_t * pxCallbackCatalogEntry;
724 BaseType_t xReturn = pdFALSE;
725 BaseType_t xCompareLen;
726 BaseType_t xShadowClientID;
727
728
729 xShadowClientID = *( ( BaseType_t * ) pvUserData ); /*lint !e9087 Safe cast from pointer handle. */
730 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
731
732 if( pxCallbackParams->xMQTTEvent == eMQTTAgentPublish )
733 {
734 pxPublishData = ( &( pxCallbackParams->u.xPublishData ) );
735
736 /* If xOperationMutex is locked, the client is waiting on the acceptance
737 * or rejection of a publish. Publish results take priority over user notify
738 * callbacks. This also means that the client will not be notified of gets or
739 * deletes performed by itself in a user notify callback. However, the client
740 * will still be notified of updates performed by itself if it has registered
741 * a callback for /update/documents or update/delta. */
742 if( ( uint16_t ) uxSemaphoreGetCount( pxShadowClient->xOperationMutex ) == ( uint16_t ) 0 )
743 {
744 /* Verify Thing Name and operation by comparing the received topic with
745 * the current operation's topic. */
746
747 xCompareLen = ( BaseType_t ) configMIN( ( BaseType_t ) strlen( ( const char * ) pxShadowClient->pucTopicBuffer ),
748 ( BaseType_t ) pxPublishData->usTopicLength );
749
750 if( strncmp( ( const char * ) pxPublishData->pucTopic,
751 ( const char * ) pxShadowClient->pucTopicBuffer,
752 ( size_t ) xCompareLen ) == 0 )
753 {
754 /* Parse the in-progress operation and result. */
755 xOperationName = ( pxShadowClient->pxOperationData )->xOperationInProgress;
756 xResult = prvParseShadowOperationStatus( pxPublishData->pucTopic,
757 pxPublishData->usTopicLength );
758
759 /* Both an operation and result were identified, and both match
760 * the operation this Shadow Client is waiting on; call the
761 * operation-specific callback. */
762 if( ( xResult != eShadowUnknown ) && ( xOperationName != eShadowOperationOther ) )
763 {
764 xOperationMatched = pdTRUE;
765
766 switch( xOperationName )
767 {
768 case eShadowOperationUpdate:
769 prvShadowUpdateCallback( xShadowClientID,
770 xResult,
771 ( pxShadowClient->pxOperationData )->pxOperationParams,
772 ( const char * ) pxPublishData->pvData,
773 pxPublishData->ulDataLength );
774 break;
775
776 case eShadowOperationGet:
777 prvShadowGetCallback( xShadowClientID,
778 xResult,
779 ( pxShadowClient->pxOperationData )->pxOperationParams,
780 ( const char * ) pxPublishData->pvData,
781 pxPublishData->ulDataLength,
782 pxPublishData->xBuffer );
783
784 /* Only take an MQTT buffer if the Get operation succeeded. */
785 if( xResult == eShadowSuccess )
786 {
787 xReturn = pdTRUE;
788 }
789
790 break;
791
792 case eShadowOperationDelete:
793 prvShadowDeleteCallback( xShadowClientID,
794 xResult,
795 ( const char * ) pxPublishData->pvData,
796 pxPublishData->ulDataLength );
797 break;
798
799 default:
800 /* Should not fall here. */
801 break;
802 }
803 }
804 }
805 }
806
807 /* If the received topic doesn't match the current operation, it's
808 * still possible for it to match a registered callback. */
809 if( xOperationMatched == pdFALSE )
810 {
811 pxCallbackCatalogEntry = prvMatchCallbackTopic( pxShadowClient,
812 pxPublishData->pucTopic, pxPublishData->usTopicLength,
813 &xOperationName );
814
815 if( pxCallbackCatalogEntry != NULL )
816 {
817 switch( xOperationName )
818 {
819 case eShadowOperationUpdateDocuments:
820 xReturn = pxCallbackCatalogEntry->xCallbackInfo.xShadowUpdatedCallback( pvUserData,
821 pxCallbackCatalogEntry->xCallbackInfo.pcThingName,
822 ( const char * ) pxPublishData->pvData,
823 pxPublishData->ulDataLength,
824 pxPublishData->xBuffer );
825 break;
826
827 case eShadowOperationUpdateDelta:
828 xReturn = pxCallbackCatalogEntry->xCallbackInfo.xShadowDeltaCallback( pvUserData,
829 pxCallbackCatalogEntry->xCallbackInfo.pcThingName,
830 ( const char * ) pxPublishData->pvData,
831 pxPublishData->ulDataLength,
832 pxPublishData->xBuffer );
833 break;
834
835 case eShadowOperationDeletedByAnother:
836 pxCallbackCatalogEntry->xCallbackInfo.xShadowDeletedCallback( pvUserData,
837 pxCallbackCatalogEntry->xCallbackInfo.pcThingName );
838 break;
839
840 default:
841 /* Should not fall here. */
842 break;
843 }
844 }
845 }
846 }
847 /* The Shadow Client assumes all subscriptions are lost on disconnect. */
848 else
849 {
850 if( pxCallbackParams->xMQTTEvent == eMQTTAgentDisconnect )
851 {
852 Shadow_debug_printf( ( "[Shadow %d] Warning: got an MQTT disconnect"
853 " message.\r\n", xShadowClientID ) );
854
855 prvSetSubscribedFlag( pxShadowClient, eShadowOperationUpdate, 0 );
856 prvSetSubscribedFlag( pxShadowClient, eShadowOperationGet, 0 );
857 prvSetSubscribedFlag( pxShadowClient, eShadowOperationDelete, 0 );
858
859 /*_RB_ TODO below. */
860 /* TODO: resubscribe to all callback topics. */
861 }
862 }
863
864 return xReturn;
865}
866
867/*-----------------------------------------------------------*/
868
869static ShadowReturnCode_t prvParseShadowOperationStatus( const uint8_t * const
870 pucTopic,
871 uint16_t usTopicLength )
872{
873 ShadowReturnCode_t xResult = eShadowUnknown;
874 const uint8_t * pucTopicStatus;
875 size_t xCompareLength;
876
877 xCompareLength = strlen( shadowTOPIC_SUFFIX_ACCEPTED );
878 pucTopicStatus = pucTopic + usTopicLength - xCompareLength;
879
880 if( strncmp( ( const char * ) pucTopicStatus,
881 shadowTOPIC_SUFFIX_ACCEPTED,
882 xCompareLength ) == 0 )
883 {
884 xResult = eShadowSuccess;
885 }
886 else
887 {
888 xCompareLength = strlen( shadowTOPIC_SUFFIX_REJECTED );
889 pucTopicStatus = pucTopic + usTopicLength - xCompareLength;
890
891 if( strncmp( ( const char * ) pucTopicStatus,
892 shadowTOPIC_SUFFIX_REJECTED,
893 xCompareLength ) == 0 )
894 {
895 xResult = eShadowFailure;
896 }
897 }
898
899 return xResult;
900}
901
902/*-----------------------------------------------------------*/
903
904static const CallbackCatalogEntry_t * prvMatchCallbackTopic( const ShadowClient_t * const pxShadowClient,
905 const uint8_t * const pucTopic,
906 uint16_t usTopicLength,
907 ShadowOperationName_t * const pxOperationName )
908{
909 const CallbackCatalogEntry_t * pxReturn = NULL;
910 BaseType_t xIterator, xTopicFound = pdFALSE;
911 uint8_t pucTopicBuffer[ shadowTOPIC_BUFFER_LENGTH ];
912 size_t xCompareLength;
913
914 for( xIterator = 0; xIterator < shadowconfigMAX_THINGS_WITH_CALLBACKS; xIterator++ )
915 {
916 pxReturn = &( pxShadowClient->pxCallbackCatalog[ xIterator ] );
917
918 if( pxReturn->xInUse == pdTRUE )
919 {
920 xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
921 shadowTOPIC_BUFFER_LENGTH,
922 shadowTOPIC_PREFIX, pxReturn->xCallbackInfo.pcThingName );
923
924 if( strncmp( ( const char * ) pucTopicBuffer,
925 ( const char * ) pucTopic,
926 xCompareLength ) == 0 )
927 {
928 xTopicFound = pdTRUE;
929
930 xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
931 shadowTOPIC_BUFFER_LENGTH, shadowTOPIC_UPDATE_DOCUMENTS,
932 pxReturn->xCallbackInfo.pcThingName );
933
934 xCompareLength = configMAX( xCompareLength, usTopicLength );
935
936 if( pxOperationName != NULL )
937 {
938 if( strncmp( ( const char * ) pucTopicBuffer,
939 ( const char * ) pucTopic,
940 xCompareLength ) == 0 )
941 {
942 *pxOperationName = eShadowOperationUpdateDocuments;
943 }
944 else
945 {
946 xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
947 shadowTOPIC_BUFFER_LENGTH, shadowTOPIC_UPDATE_DELTA,
948 pxReturn->xCallbackInfo.pcThingName );
949
950 xCompareLength = configMAX( xCompareLength, usTopicLength );
951
952 if( strncmp( ( const char * ) pucTopicBuffer,
953 ( const char * ) pucTopic,
954 xCompareLength ) == 0 )
955 {
956 *pxOperationName = eShadowOperationUpdateDelta;
957 }
958 else
959 {
960 xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
961 shadowTOPIC_BUFFER_LENGTH,
962 shadowTOPIC_DELETE_ACCEPTED,
963 pxReturn->xCallbackInfo.pcThingName );
964
965 xCompareLength = configMAX( xCompareLength, usTopicLength );
966
967 if( strncmp( ( const char * ) pucTopicBuffer,
968 ( const char * ) pucTopic,
969 xCompareLength ) == 0 )
970 {
971 *pxOperationName = eShadowOperationDeletedByAnother;
972 }
973 else
974 {
975 *pxOperationName = eShadowOperationOther;
976 }
977 }
978 }
979 }
980
981 break;
982 }
983 }
984 }
985
986 if( xTopicFound == pdFALSE )
987 {
988 pxReturn = NULL;
989 }
990
991 return pxReturn;
992}
993
994/*-----------------------------------------------------------*/
995
996static void prvShadowUpdateCallback( BaseType_t xShadowClientID,
997 ShadowReturnCode_t xResult,
998 const ShadowOperationParams_t * const pxParams,
999 const char * const pcData,
1000 uint32_t ulDataLength )
1001{
1002 /* Remove warning about unused parameters. */
1003 ( void ) pxParams;
1004
1005 ShadowClient_t * pxShadowClient;
1006
1007 BaseType_t xReturn;
1008
1009 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
1010
1011 /* Verify client token match. */
1012 xReturn = SHADOW_JSONDocClientTokenMatch( pxParams->pcData,
1013 pxParams->ulDataLength,
1014 pcData,
1015 ulDataLength );
1016
1017 if( xReturn == pdPASS )
1018 {
1019 pxShadowClient->xOperationResult = xResult;
1020
1021 /* For failures, get the code and message. */
1022 if( xResult == eShadowFailure )
1023 {
1024 pxShadowClient->xOperationResult = prvGetErrorCodeAndMessage( pcData,
1025 ulDataLength,
1026 xShadowClientID,
1027 shadowTOPIC_OPERATION_UPDATE );
1028 }
1029
1030 configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
1031 }
1032}
1033
1034/*-----------------------------------------------------------*/
1035
1036static void prvShadowGetCallback( BaseType_t xShadowClientID,
1037 ShadowReturnCode_t xResult,
1038 ShadowOperationParams_t * const pxParams,
1039 const char * const pcData,
1040 uint32_t ulDataLength,
1041 MQTTBufferHandle_t xBuffer )
1042{
1043 ShadowClient_t * pxShadowClient;
1044
1045 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
1046 pxShadowClient->xOperationResult = xResult;
1047
1048/* For successes, fill the user's buffer with the Shadow document. */
1049 if( xResult == eShadowSuccess )
1050 {
1051 pxParams->pcData = pcData;
1052 pxParams->ulDataLength = ulDataLength;
1053 pxParams->xBuffer = xBuffer;
1054 }
1055/* For failures , get the code and message. */
1056 else
1057 {
1058 pxShadowClient->xOperationResult = prvGetErrorCodeAndMessage( pcData,
1059 ulDataLength,
1060 xShadowClientID,
1061 shadowTOPIC_OPERATION_GET );
1062 pxParams->pcData = NULL;
1063 pxParams->ulDataLength = 0;
1064 }
1065
1066 configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
1067}
1068/*-----------------------------------------------------------*/
1069
1070static void prvShadowDeleteCallback( BaseType_t xShadowClientID,
1071 ShadowReturnCode_t xResult,
1072 const char * const pcData,
1073 uint32_t ulDataLength )
1074{
1075 ShadowClient_t * pxShadowClient;
1076
1077 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
1078 pxShadowClient->xOperationResult = xResult;
1079
1080 if( xResult == eShadowFailure )
1081 {
1082 pxShadowClient->xOperationResult = prvGetErrorCodeAndMessage( pcData,
1083 ulDataLength,
1084 xShadowClientID,
1085 shadowTOPIC_OPERATION_DELETE );
1086 }
1087
1088 configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
1089}
1090/*-----------------------------------------------------------*/
1091
1092static ShadowReturnCode_t prvGetErrorCodeAndMessage( const char * const pcData,
1093 uint32_t ulDataLength,
1094 BaseType_t xShadowClientID,
1095 const char * const pcOperationName )
1096{
1097 ShadowReturnCode_t xErrorCode;
1098 char * pcErrorMessage;
1099 uint16_t usErrorMessageLength;
1100
1101 xErrorCode = ( ShadowReturnCode_t ) SHADOW_JSONGetErrorCodeAndMessage( pcData,
1102 ulDataLength,
1103 &pcErrorMessage,
1104 &usErrorMessageLength );
1105
1106 if( xErrorCode > 0 )
1107 {
1108 Shadow_debug_printf( ( "[Shadow %d] %s rejected, code %d: %.*s.\r\n",
1109 xShadowClientID, pcOperationName,
1110 xErrorCode,
1111 usErrorMessageLength,
1112 pcErrorMessage ) );
1113 }
1114 else
1115 {
1116 Shadow_debug_printf( ( "[Shadow %d] JSON parse error while parsing"
1117 " error code and message.\r\n", xShadowClientID ) );
1118 }
1119
1120 /* Remove compiler warnings in the case that Shadow_debug_printf() is not
1121 * defined. */
1122 ( void ) pcOperationName;
1123 ( void ) xShadowClientID;
1124
1125 /* SHADOW_JSONGetErrorCodeAndMessage may return 0 for bad pointer arguments.
1126 * Convert this to a JSON parse error, as 0 is eShadowSuccess. */
1127 if( xErrorCode == 0 )
1128 {
1129 xErrorCode = eShadowJSMNInval;
1130 }
1131
1132 return xErrorCode;
1133}
1134
1135/*-----------------------------------------------------------*/
1136
1137static ShadowReturnCode_t prvShadowOperation( ShadowOperationCallParams_t * pxParams )
1138{
1139 ShadowReturnCode_t xReturn = eShadowFailure;
1140 MQTTAgentPublishParams_t xPublishParams;
1141 ShadowClient_t * pxShadowClient;
1142 TimeOutData_t xTimeOutData;
1143 ShadowOperationData_t xOperationData;
1144 MQTTAgentReturnCode_t xMQTTReturn;
1145
1146 /* Initialize timeout data. */
1147 xTimeOutData.xTicksRemaining = pxParams->xTimeoutTicks;
1148
1149 /* Identify the relevant Shadow Client, then lock that client's operation mutex.
1150 * This allows only one operation to be in progress. */
1151 pxShadowClient = &( pxShadowClients[ ( pxParams->xShadowClientID ) ] );
1152
1153 if( xSemaphoreTake( pxShadowClient->xOperationMutex,
1154 xTimeOutData.xTicksRemaining ) == pdPASS )
1155 {
1156 /* Subscribe to accepted/rejected if necessary. */
1157 if( ( BaseType_t ) prvGetSubscribedFlag( pxShadowClient,
1158 pxParams->xOperationName ) == pdFALSE )
1159 {
1160 xReturn = prvShadowSubscribeToAcceptedRejected( pxParams->xShadowClientID,
1161 ( pxParams->pxOperationParams )->pcThingName,
1162 pxParams->pcOperationAcceptedTopic,
1163 pxParams->pcOperationRejectedTopic,
1164 &xTimeOutData );
1165 }
1166 else
1167 {
1168 xReturn = eShadowSuccess;
1169 }
1170
1171 if( xReturn == eShadowSuccess )
1172 {
1173 /* The subscribe to update/accepted and update/rejected succeeded,
1174 * so set the appropriate flag. */
1175 prvSetSubscribedFlag( pxShadowClient, pxParams->xOperationName, 1 );
1176
1177 /* This is guarded by xOperationMutex and should never fail. */
1178 configASSERT( xSemaphoreTake( pxShadowClient->xCallbackSemaphore,
1179 xTimeOutData.xTicksRemaining ) == pdPASS );
1180
1181 /* Fill pucTopicBuffer with the update topic. */
1182 xPublishParams.usTopicLength =
1183 prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
1184 shadowTOPIC_BUFFER_LENGTH,
1185 pxParams->pcOperationTopic,
1186 ( pxParams->pxOperationParams )->pcThingName );
1187
1188 /* Operation parameters. */
1189 xPublishParams.pucTopic = pxShadowClient->pucTopicBuffer;
1190 xPublishParams.pvData = pxParams->pcPublishMessage;
1191 xPublishParams.ulDataLength = pxParams->ulPublishMessageLength;
1192 xPublishParams.xQoS = ( pxParams->pxOperationParams )->xQoS;
1193
1194 /* Data to pass to the callback. */
1195 xOperationData.xOperationInProgress = pxParams->xOperationName;
1196 xOperationData.pxOperationParams = pxParams->pxOperationParams;
1197 pxShadowClient->pxOperationData = &xOperationData;
1198
1199 xMQTTReturn = MQTT_AGENT_Publish( pxShadowClient->xMQTTClient,
1200 &xPublishParams,
1201 xTimeOutData.xTicksRemaining );
1202
1203 /* Publish to operation topic. */
1204 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
1205 ( ShadowClientHandle_t ) ( pxParams->xShadowClientID ), /*lint !e923 Safe cast from pointer handle. */
1206 "Publish to operation topic" );
1207
1208 if( xReturn == eShadowSuccess )
1209 {
1210 /* Wait for the semaphore to become available again; it should be
1211 * released by the update callback. */
1212 if( xSemaphoreTake( pxShadowClient->xCallbackSemaphore,
1213 xTimeOutData.xTicksRemaining ) != pdPASS )
1214 {
1215 Shadow_debug_printf( ( "[Shadow %d] Timeout waiting on"
1216 " %s accepted/rejected.\r\n",
1217 pxParams->xShadowClientID,
1218 pxParams->pcOperationName ) );
1219 xReturn = eShadowTimeout;
1220 }
1221 else
1222 {
1223 /* The update callback reports its status as xOperationResult. */
1224 xReturn = pxShadowClient->xOperationResult;
1225 }
1226 }
1227
1228 configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
1229 }
1230
1231 /* Unsubscribe. */
1232 if( ( pxParams->pxOperationParams )->ucKeepSubscriptions == ( uint8_t ) 0 )
1233 {
1234 xTimeOutData.xTicksRemaining = configMAX( xTimeOutData.xTicksRemaining,
1235 pdMS_TO_TICKS( shadowconfigCLEANUP_TIME_MS ) );
1236
1237 /* If the Shadow client is subscribed to delete/accepted for this
1238 * Thing for a user notify callback, do not unsubscribe; that would
1239 * break callback notify. */
1240 if( pxParams->xOperationName == eShadowOperationDelete )
1241 {
1242 ( void ) prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
1243 shadowTOPIC_BUFFER_LENGTH,
1244 shadowTOPIC_DELETE_ACCEPTED,
1245 pxParams->pxOperationParams->pcThingName );
1246
1247 /* If there's a callback registered for delete/accepted, only
1248 * unsubscribe from delete/rejected. */
1249 if( prvMatchCallbackTopic( pxShadowClient,
1250 pxShadowClient->pucTopicBuffer,
1251 ( uint16_t )
1252 strlen( ( const char * ) pxShadowClient->pucTopicBuffer ),
1253 NULL ) == NULL )
1254 {
1255 if( prvShadowUnsubscribeFromAcceptedRejected( pxParams->xShadowClientID,
1256 pxParams->pxOperationParams->pcThingName,
1257 NULL,
1258 pxParams->pcOperationRejectedTopic,
1259 &xTimeOutData ) == eShadowSuccess )
1260 {
1261 prvSetSubscribedFlag( pxShadowClient,
1262 pxParams->xOperationName,
1263 0 );
1264 }
1265 }
1266 }
1267 else
1268 {
1269 if( prvShadowUnsubscribeFromAcceptedRejected( pxParams->xShadowClientID,
1270 pxParams->pxOperationParams->pcThingName,
1271 pxParams->pcOperationAcceptedTopic,
1272 pxParams->pcOperationRejectedTopic,
1273 &xTimeOutData ) == eShadowSuccess )
1274 {
1275 prvSetSubscribedFlag( pxShadowClient,
1276 pxParams->xOperationName,
1277 0 );
1278 }
1279 }
1280 }
1281
1282 /* Delete this operation's data so that the next operation has a clean Shadow
1283 * Client, then release the operation mutex. */
1284 pxShadowClient->pxOperationData = NULL;
1285 pxShadowClient->xOperationResult = eShadowSuccess;
1286 memset( pxShadowClient->pucTopicBuffer, 0, shadowTOPIC_BUFFER_LENGTH );
1287 configASSERT( xSemaphoreGive( pxShadowClient->xOperationMutex )
1288 == pdPASS );
1289 }
1290
1291 return xReturn;
1292}
1293
1294/*-----------------------------------------------------------*/
1295
1296static void prvSetSubscribedFlag( ShadowClient_t * const pxShadowClient,
1297 ShadowOperationName_t xOperationName,
1298 BaseType_t ucValue )
1299{
1300 switch( xOperationName )
1301 {
1302 case eShadowOperationUpdate:
1303 pxShadowClient->xUpdateSubscribed = ucValue;
1304 break;
1305
1306 case eShadowOperationGet:
1307 pxShadowClient->xGetSubscribed = ucValue;
1308 break;
1309
1310 case eShadowOperationDelete:
1311 pxShadowClient->xDeleteSubscribed = ucValue;
1312 break;
1313
1314 default:
1315 /* Should not fall here. */
1316 break;
1317 }
1318}
1319
1320/*-----------------------------------------------------------*/
1321
1322static uint8_t prvGetSubscribedFlag( const ShadowClient_t * const pxShadowClient,
1323 ShadowOperationName_t xOperationName )
1324{
1325 uint8_t ucReturn = 0;
1326
1327 switch( xOperationName )
1328 {
1329 case eShadowOperationUpdate:
1330 ucReturn = ( uint8_t ) pxShadowClient->xUpdateSubscribed;
1331 break;
1332
1333 case eShadowOperationGet:
1334 ucReturn = ( uint8_t ) pxShadowClient->xGetSubscribed;
1335 break;
1336
1337 case eShadowOperationDelete:
1338 ucReturn = ( uint8_t ) pxShadowClient->xDeleteSubscribed;
1339 break;
1340
1341 default:
1342 /* Should not fall here. */
1343 break;
1344 }
1345
1346 return ucReturn;
1347}
1348
1349/*-----------------------------------------------------------*/
1350
1351ShadowReturnCode_t SHADOW_ClientCreate( ShadowClientHandle_t * pxShadowClientHandle,
1352 const ShadowCreateParams_t * const pxShadowCreateParams )
1353{
1354 ShadowClient_t * pxShadowClient;
1355 BaseType_t xShadowClientID;
1356 ShadowReturnCode_t xReturn = eShadowFailure;
1357 MQTTAgentReturnCode_t xMQTTReturn;
1358
1359 configASSERT( ( pxShadowClientHandle != NULL ) );
1360 configASSERT( ( pxShadowCreateParams != NULL ) );
1361
1362 /* For now, only dedicated MQTT clients are supported. Remove this assert
1363 * once support for shared clients is added. */
1364 configASSERT( ( pxShadowCreateParams->xMQTTClientType == eDedicatedMQTTClient ) );
1365
1366 xShadowClientID = prvGetFreeShadowClient();
1367 configASSERT( xShadowClientID >= 0 );
1368
1369 if( xShadowClientID >= 0 )
1370 {
1371 pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
1372
1373 xMQTTReturn = MQTT_AGENT_Create( &( pxShadowClient->xMQTTClient ) );
1374
1375 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
1376 ( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
1377 "Creation of dedicated MQTT client" );
1378
1379 if( xReturn == eShadowSuccess )
1380 {
1381 /* Create synchronization mechanisms; these calls should never fail. */
1382 pxShadowClient->xCallbackSemaphore = xSemaphoreCreateBinaryStatic( &( pxShadowClient->xCallbackSemaphoreBuffer ) );
1383 pxShadowClient->xOperationMutex = xSemaphoreCreateMutexStatic( &( pxShadowClient->xOperationMutexBuffer ) );
1384
1385 configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
1386
1387 /* Set the output parameter. */
1388 *pxShadowClientHandle = ( ShadowClientHandle_t ) xShadowClientID; /*lint !e923 Safe cast from pointer handle. */
1389 }
1390 else
1391 {
1392 Shadow_debug_printf( ( "[Shadow Init] Failed to create dedicated"
1393 "client; deleting partially-initialized client.\r\n" ) );
1394
1395 /* Delete the partially-initialized client. */
1396 xReturn = SHADOW_ClientDelete( *( pxShadowClientHandle ) );
1397 }
1398 }
1399
1400 return xReturn;
1401}
1402
1403/*-----------------------------------------------------------*/
1404
1405ShadowReturnCode_t SHADOW_ClientConnect( ShadowClientHandle_t xShadowClientHandle,
1406 MQTTAgentConnectParams_t * const pxConnectParams,
1407 TickType_t xTimeoutTicks )
1408{
1409 ShadowClient_t * pxShadowClient;
1410 ShadowReturnCode_t xReturn;
1411 MQTTAgentReturnCode_t xMQTTReturn;
1412
1413 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1414 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1415 configASSERT( xShadowClientHandle == *( ( ShadowClientHandle_t * ) ( pxConnectParams->pvUserData ) ) ); /*lint !e9087 Safe cast from opaque context. */
1416
1417 pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
1418 configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
1419
1420 pxConnectParams->pxCallback = prvShadowMQTTCallback;
1421
1422 xMQTTReturn = MQTT_AGENT_Connect( pxShadowClient->xMQTTClient,
1423 pxConnectParams,
1424 xTimeoutTicks );
1425
1426 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
1427 xShadowClientHandle,
1428 "Connect" );
1429
1430 pxConnectParams->pxCallback = NULL;
1431
1432 return xReturn;
1433}
1434
1435/*-----------------------------------------------------------*/
1436
1437ShadowReturnCode_t SHADOW_ClientDisconnect( ShadowClientHandle_t xShadowClientHandle )
1438{
1439 ShadowClient_t * pxShadowClient;
1440 MQTTAgentReturnCode_t xMQTTReturn;
1441 TickType_t xTimeoutTicks;
1442 ShadowReturnCode_t xReturn;
1443
1444 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1445 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1446
1447 pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
1448 configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
1449
1450 xTimeoutTicks = pdMS_TO_TICKS( shadowconfigCLEANUP_TIME_MS );
1451
1452 xMQTTReturn = MQTT_AGENT_Disconnect( pxShadowClient->xMQTTClient,
1453 xTimeoutTicks );
1454
1455 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
1456 xShadowClientHandle,
1457 "Disconnect" );
1458
1459 return xReturn;
1460}
1461
1462/*-----------------------------------------------------------*/
1463
1464ShadowReturnCode_t SHADOW_ClientDelete( ShadowClientHandle_t xShadowClientHandle )
1465{
1466 ShadowClient_t * pxShadowClient;
1467 ShadowReturnCode_t xReturn = eShadowFailure;
1468 MQTTAgentReturnCode_t xMQTTReturn;
1469
1470 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1471 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1472
1473 pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
1474 configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
1475
1476 xMQTTReturn = MQTT_AGENT_Delete( pxShadowClient->xMQTTClient );
1477
1478 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
1479 xShadowClientHandle,
1480 "MQTT Client delete" );
1481
1482 if( xReturn == eShadowSuccess )
1483 {
1484 taskENTER_CRITICAL();
1485 memset( pxShadowClient, 0, sizeof( ShadowClient_t ) );
1486 taskEXIT_CRITICAL();
1487 }
1488
1489 return xReturn;
1490}
1491
1492/*-----------------------------------------------------------*/
1493
1494ShadowReturnCode_t SHADOW_Update( ShadowClientHandle_t xShadowClientHandle,
1495 ShadowOperationParams_t * const pxUpdateParams,
1496 TickType_t xTimeoutTicks )
1497{
1498 ShadowOperationCallParams_t xUpdateCallParams;
1499
1500 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1501 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1502
1503 configASSERT( ( pxUpdateParams != NULL ) );
1504 configASSERT( ( pxUpdateParams->pcThingName != NULL ) );
1505 configASSERT( ( pxUpdateParams->pcData != NULL ) );
1506 configASSERT( ( pxUpdateParams->xQoS == eMQTTQoS0 ||
1507 pxUpdateParams->xQoS == eMQTTQoS1 ) );
1508
1509 xUpdateCallParams.xShadowClientID = ( BaseType_t ) xShadowClientHandle; /*lint !e923 Safe cast from pointer handle. */
1510 xUpdateCallParams.xOperationName = eShadowOperationUpdate;
1511
1512 xUpdateCallParams.pcOperationName = shadowTOPIC_OPERATION_UPDATE;
1513
1514 xUpdateCallParams.pcOperationTopic = shadowTOPIC_UPDATE;
1515 xUpdateCallParams.pcOperationAcceptedTopic = shadowTOPIC_UPDATE_ACCEPTED;
1516 xUpdateCallParams.pcOperationRejectedTopic = shadowTOPIC_UPDATE_REJECTED;
1517
1518 xUpdateCallParams.pcPublishMessage = pxUpdateParams->pcData;
1519 xUpdateCallParams.ulPublishMessageLength = pxUpdateParams->ulDataLength;
1520 xUpdateCallParams.pxOperationParams = ( ShadowOperationParams_t * ) pxUpdateParams;
1521 xUpdateCallParams.xTimeoutTicks = xTimeoutTicks;
1522
1523 return prvShadowOperation( &xUpdateCallParams );
1524}
1525
1526/*-----------------------------------------------------------*/
1527
1528ShadowReturnCode_t SHADOW_Get( ShadowClientHandle_t xShadowClientHandle,
1529 ShadowOperationParams_t * const pxGetParams,
1530 TickType_t xTimeoutTicks )
1531{
1532 ShadowOperationCallParams_t xGetCallParams;
1533
1534 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1535 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1536
1537 configASSERT( ( pxGetParams != NULL ) );
1538 configASSERT( ( pxGetParams->pcThingName != NULL ) );
1539 configASSERT( ( pxGetParams->xQoS == eMQTTQoS0 ||
1540 pxGetParams->xQoS == eMQTTQoS1 ) );
1541
1542 xGetCallParams.xShadowClientID = ( BaseType_t ) xShadowClientHandle; /*lint !e923 Safe cast from pointer handle. */
1543 xGetCallParams.xOperationName = eShadowOperationGet;
1544
1545 xGetCallParams.pcOperationName = shadowTOPIC_OPERATION_GET;
1546
1547 xGetCallParams.pcOperationTopic = shadowTOPIC_GET;
1548 xGetCallParams.pcOperationAcceptedTopic = shadowTOPIC_GET_ACCEPTED;
1549 xGetCallParams.pcOperationRejectedTopic = shadowTOPIC_GET_REJECTED;
1550
1551 xGetCallParams.pcPublishMessage = "";
1552 xGetCallParams.ulPublishMessageLength = 0;
1553 xGetCallParams.pxOperationParams = pxGetParams;
1554 xGetCallParams.xTimeoutTicks = xTimeoutTicks;
1555
1556 return prvShadowOperation( &xGetCallParams );
1557}
1558
1559/*-----------------------------------------------------------*/
1560
1561ShadowReturnCode_t SHADOW_Delete( ShadowClientHandle_t xShadowClientHandle,
1562 ShadowOperationParams_t * const pxDeleteParams,
1563 TickType_t xTimeoutTicks )
1564{
1565 ShadowOperationCallParams_t xDeleteCallParams;
1566 ShadowReturnCode_t xStatus;
1567
1568 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1569 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1570
1571 configASSERT( ( pxDeleteParams != NULL ) );
1572 configASSERT( ( pxDeleteParams->pcThingName != NULL ) );
1573 configASSERT( ( pxDeleteParams->xQoS == eMQTTQoS0 ||
1574 pxDeleteParams->xQoS == eMQTTQoS1 ) );
1575
1576 xDeleteCallParams.xShadowClientID = ( BaseType_t ) xShadowClientHandle; /*lint !e923 Safe cast from pointer handle. */
1577 xDeleteCallParams.xOperationName = eShadowOperationDelete;
1578
1579 xDeleteCallParams.pcOperationName = shadowTOPIC_OPERATION_DELETE;
1580
1581 xDeleteCallParams.pcOperationTopic = shadowTOPIC_DELETE;
1582 xDeleteCallParams.pcOperationAcceptedTopic = shadowTOPIC_DELETE_ACCEPTED;
1583 xDeleteCallParams.pcOperationRejectedTopic = shadowTOPIC_DELETE_REJECTED;
1584
1585 xDeleteCallParams.pcPublishMessage = "";
1586 xDeleteCallParams.ulPublishMessageLength = 0;
1587 xDeleteCallParams.pxOperationParams = ( ShadowOperationParams_t * ) pxDeleteParams;
1588 xDeleteCallParams.xTimeoutTicks = xTimeoutTicks;
1589
1590 xStatus = prvShadowOperation( &xDeleteCallParams );
1591
1592 return xStatus;
1593}
1594
1595/*-----------------------------------------------------------*/
1596
1597ShadowReturnCode_t SHADOW_RegisterCallbacks( ShadowClientHandle_t xShadowClientHandle,
1598 ShadowCallbackParams_t * const pxCallbackParams,
1599 TickType_t xTimeoutTicks )
1600{
1601 ShadowClient_t * pxShadowClient;
1602 CallbackCatalogEntry_t * pxCallbackCatalogEntry;
1603 ShadowReturnCode_t xReturn = eShadowFailure;
1604 BaseType_t xCallbackCatalogIndex;
1605
1606 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1607 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1608
1609 configASSERT( ( pxCallbackParams != NULL ) );
1610 configASSERT( ( pxCallbackParams->pcThingName != NULL ) );
1611
1612 pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
1613 configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
1614
1615 xCallbackCatalogIndex = prvGetCallbackCatalogEntry( pxShadowClient->pxCallbackCatalog,
1616 pxCallbackParams->pcThingName );
1617 configASSERT( xCallbackCatalogIndex >= 0 );
1618
1619 /* Initialize timeout data. */
1620
1621 pxCallbackCatalogEntry = &( pxShadowClient->pxCallbackCatalog
1622 [ xCallbackCatalogIndex ] );
1623
1624 /*_RB_ Casting on these calls make the code unreadable. Types need changing to remove the need for the casts. */
1625 /* ToDo: sub manager. */
1626 xReturn = prvRegisterCallback( ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
1627 ( const void ** ) &( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowUpdatedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. No const is being cast away either.*/
1628 ( const void ** ) &( pxCallbackParams->xShadowUpdatedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. */
1629 pxCallbackParams->pcThingName,
1630 ( const uint8_t * ) shadowTOPIC_UPDATE_DOCUMENTS,
1631 xTimeoutTicks );
1632
1633 if( xReturn == eShadowSuccess )
1634 {
1635 xReturn = prvRegisterCallback( ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
1636 ( const void ** ) &( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeletedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. No const is being cast away either.*/
1637 ( const void ** ) &( pxCallbackParams->xShadowDeletedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. */
1638 pxCallbackParams->pcThingName,
1639 ( const uint8_t * ) shadowTOPIC_DELETE_ACCEPTED,
1640 xTimeoutTicks );
1641 }
1642
1643 if( xReturn == eShadowSuccess )
1644 {
1645 xReturn = prvRegisterCallback( ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
1646 ( const void ** ) &( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeltaCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. No const is being cast away either.*/
1647 ( const void ** ) &( pxCallbackParams->xShadowDeltaCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. */
1648 pxCallbackParams->pcThingName,
1649 ( const uint8_t * ) shadowTOPIC_UPDATE_DELTA,
1650 xTimeoutTicks );
1651 }
1652
1653 if( ( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowUpdatedCallback == NULL ) &&
1654 ( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeltaCallback == NULL ) &&
1655 ( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeletedCallback == NULL ) )
1656 {
1657 taskENTER_CRITICAL();
1658 {
1659 memset( pxCallbackCatalogEntry,
1660 0,
1661 sizeof( CallbackCatalogEntry_t ) );
1662 }
1663 taskEXIT_CRITICAL();
1664 }
1665
1666 return xReturn;
1667}
1668
1669/*-----------------------------------------------------------*/
1670
1671ShadowReturnCode_t SHADOW_ReturnMQTTBuffer( ShadowClientHandle_t xShadowClientHandle,
1672 MQTTBufferHandle_t xBufferHandle )
1673{
1674 ShadowClient_t * pxShadowClient;
1675 MQTTAgentReturnCode_t xMQTTReturn;
1676 ShadowReturnCode_t xReturn = eShadowFailure;
1677
1678 configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
1679 ( BaseType_t ) xShadowClientHandle < shadowconfigMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
1680
1681 pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
1682 configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
1683
1684 xMQTTReturn = MQTT_AGENT_ReturnBuffer( pxShadowClient->xMQTTClient,
1685 xBufferHandle );
1686
1687 xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
1688 xShadowClientHandle,
1689 "Return MQTT buffer" );
1690
1691 return xReturn;
1692}