wolfssl-freertos/FreeRTOS-AWS/lib/shadow/aws_shadow.c

1689 lines
72 KiB
C
Executable File

/*
* Amazon FreeRTOS Shadow V1.0.2
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file aws_shadow.c
* @brief Shadow API. Provide simple function to modify/create/delete Things shadows.
*/
/* C library includes. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* AWS includes. */
#include "aws_shadow_config.h"
#include "aws_shadow_config_defaults.h"
#include "aws_shadow.h"
#include "aws_shadow_json.h"
/**
* @brief Format strings for the AWS IoT Shadow MQTT topics.
* Refer to http://docs.aws.amazon.com/iot/latest/developerguide/thing-shadow-mqtt.html
* If the MQTT topics change, update them here. Use %s for 'thingName'. */
/** @{ */
#define shadowTOPIC_PREFIX "$aws/things/%s/shadow/" /* All shadow topics begin with this. */
#define shadowTOPIC_OPERATION_UPDATE "update"
#define shadowTOPIC_OPERATION_GET "get"
#define shadowTOPIC_OPERATION_DELETE "delete"
#define shadowTOPIC_SUFFIX_ACCEPTED "/accepted" /* All accepted topics end with this. */
#define shadowTOPIC_SUFFIX_REJECTED "/rejected" /* All rejected topics end with this. */
#define shadowTOPIC_UPDATE shadowTOPIC_PREFIX shadowTOPIC_OPERATION_UPDATE
#define shadowTOPIC_UPDATE_ACCEPTED shadowTOPIC_UPDATE shadowTOPIC_SUFFIX_ACCEPTED
#define shadowTOPIC_UPDATE_REJECTED shadowTOPIC_UPDATE shadowTOPIC_SUFFIX_REJECTED
#define shadowTOPIC_UPDATE_DOCUMENTS shadowTOPIC_UPDATE "/documents"
#define shadowTOPIC_UPDATE_DELTA shadowTOPIC_UPDATE "/delta"
#define shadowTOPIC_GET shadowTOPIC_PREFIX shadowTOPIC_OPERATION_GET
#define shadowTOPIC_GET_ACCEPTED shadowTOPIC_GET shadowTOPIC_SUFFIX_ACCEPTED
#define shadowTOPIC_GET_REJECTED shadowTOPIC_GET shadowTOPIC_SUFFIX_REJECTED
#define shadowTOPIC_DELETE shadowTOPIC_PREFIX shadowTOPIC_OPERATION_DELETE
#define shadowTOPIC_DELETE_ACCEPTED shadowTOPIC_DELETE shadowTOPIC_SUFFIX_ACCEPTED
#define shadowTOPIC_DELETE_REJECTED shadowTOPIC_DELETE shadowTOPIC_SUFFIX_REJECTED
/** @} */
/** Maximum length of a Shadow MQTT topic. 128 is currently the longest Thing
* Name that AWS IoT accepts. shadowTOPIC_UPDATE_DOCUMENTS is currently the
* longest topic. All Shadow MQTT topics should be shorter than this value. */
#define configMAX_THING_NAME_LENGTH 128
#define shadowTOPIC_BUFFER_LENGTH ( configMAX_THING_NAME_LENGTH + ( int16_t ) sizeof( shadowTOPIC_UPDATE_DOCUMENTS ) )
#if ( shadowENABLE_DEBUG_LOGS == 1 )
#define Shadow_debug_printf( X ) configPRINTF( X )
#else
#define Shadow_debug_printf( X )
#endif
/**
* @brief Shadow operation that can be parsed from MQTT topics.
*
*/
typedef enum ShadowOperationName
{
eShadowOperationUpdate,
eShadowOperationGet,
eShadowOperationDelete,
eShadowOperationUpdateDocuments,
eShadowOperationUpdateDelta,
eShadowOperationDeletedByAnother,
eShadowOperationOther
} ShadowOperationName_t;
/**
* @brief Data on the Shadow operation currently in progress.
*
*/
typedef struct ShadowOperationData
{
ShadowOperationName_t xOperationInProgress;
ShadowOperationParams_t * pxOperationParams;
} ShadowOperationData_t;
/**
* @brief Data on the timeout by which a function needs to complete.
*
*/
typedef struct TimeOutData
{
TimeOut_t xTimeOut;
TickType_t xTicksRemaining;
} TimeOutData_t;
/**
* @brief Data passed to prvShadowOperation.
*
*/
typedef struct ShadowOperationCallParams
{
BaseType_t xShadowClientID;
ShadowOperationName_t xOperationName;
const char * pcOperationName;
/* Operation MQTT topics. */
const char * pcOperationTopic;
const char * pcOperationAcceptedTopic;
const char * pcOperationRejectedTopic;
/* The message to publish to MQTT topics. */
const char * pcPublishMessage;
uint32_t ulPublishMessageLength;
ShadowOperationParams_t * pxOperationParams;
TickType_t xTimeoutTicks;
} ShadowOperationCallParams_t;
/**
* @brief An entry of the callback catalog.
*
* The callback catalog is the member of the Shadow Client that stores the
* callback functions associated with Thing Names.
*/
typedef struct CallbackCatalogEntry
{
ShadowCallbackParams_t xCallbackInfo;
BaseType_t xInUse;
} CallbackCatalogEntry_t;
/**
* @brief The Shadow Client.
*
*/
typedef struct ShadowClient
{
/* MQTT Client handle. */
MQTTAgentHandle_t xMQTTClient;
/* Shadow Client flags. */
BaseType_t xInUse;
BaseType_t xUpdateSubscribed;
BaseType_t xGetSubscribed;
BaseType_t xDeleteSubscribed;
/* Synchronization mechanisms. */
SemaphoreHandle_t xOperationMutex; /* Allows only one in-progress operation. */
SemaphoreHandle_t xCallbackSemaphore; /* Communication with callbacks. */
StaticSemaphore_t xOperationMutexBuffer;
StaticSemaphore_t xCallbackSemaphoreBuffer;
/* Data shared between blocking function and MQTT callback. */
ShadowOperationData_t * pxOperationData;
/* The callback functions pass data to the API calls by setting xOperationResult.
* This value is volatile to ensure that the compiler knows the value can change
* without anything happening in the API call. */
volatile ShadowReturnCode_t xOperationResult;
/* Callback catalog stores Thing Names and registered callbacks. */
CallbackCatalogEntry_t pxCallbackCatalog[ shadowCLIENT_MAX_THINGS_WITH_CALLBACKS ];
/* Stores the topic of the in-progress operation. Some of the Shadow API's
* static functions depend on this buffer remaining the same throughout a
* Shadow API operation. Therefore, only prvShadowOperation (and the static
* functions called by prvShadowOperation) should modify the contents of this
* buffer. */
uint8_t pucTopicBuffer[ shadowTOPIC_BUFFER_LENGTH ];
} ShadowClient_t;
/**
* @brief Searches memory for a not-in-use Shadow Client.
*
*/
static BaseType_t prvGetFreeShadowClient( void );
/**
* @brief Returns the slot index of callback catalog for matching thing name, if thing name not found
* returns the next available unused slot index in catalog.
*
*/
static BaseType_t prvGetCallbackCatalogEntry( CallbackCatalogEntry_t * const pxCallbackCatalog,
const char * const pcThingName );
/**
* @brief Wrapper function for MQTT API calls; converts MQTTAgentReturnCode_t to
* ShadowReturnCode_t and optionally prints debug messages.
*
*/
static ShadowReturnCode_t prvConvertMQTTReturnCode( MQTTAgentReturnCode_t xMQTTReturn,
ShadowClientHandle_t xShadowClientHandle,
const char * const pcDebugMessageSubject );
/**
* @brief Function to register a callback by subscribing to Shadow MQTT topics or removes a callback
* Function by unsubscribing to Shadow MQTT topics
*
*/
static ShadowReturnCode_t prvRegisterCallback( BaseType_t xShadowClientID,
const void ** const ppvOldCallback,
const void ** const ppvNewCallback,
const char * const pcThingName,
const uint8_t * const pucTopicFormat,
TickType_t xTimeoutTicks );
/**
* @brief Subscribes to an accepted and rejected topic
*
*/
static ShadowReturnCode_t prvShadowSubscribeToAcceptedRejected( BaseType_t
xShadowClientID,
const char * const pcThingName,
const char * const pcAcceptedTopic,
const char * const pcRejectedTopic,
TimeOutData_t * const pxTimeOutData );
/**
* @brief Unsubscribe to an accepted and rejected topic.
*
*/
static ShadowReturnCode_t prvShadowUnsubscribeFromAcceptedRejected( BaseType_t xShadowClientID,
const char * const pcThingName,
const char * const pcAcceptedTopic,
const char * const pcRejectedTopic,
TimeOutData_t * const pxTimeOutData );
/**
* @brief Universal MQTT callback; parses topics for Thing Name and operation matches.
*
*/
static BaseType_t prvShadowMQTTCallback( void * pvUserData,
const MQTTAgentCallbackParams_t * const pxCallbackParams );
/**
* @brief Parses "accepted" or "rejected" from MQTT topic.
*
*/
static ShadowReturnCode_t prvParseShadowOperationStatus( const uint8_t * const
pucTopic,
uint16_t usTopicLength );
/**
* @briefMatch topic with registered callback and return reference to catalog entry found or NULL in not found.
*/
static const CallbackCatalogEntry_t * prvMatchCallbackTopic( const ShadowClient_t *
const pxShadowClient,
const uint8_t * const pucTopic,
uint16_t usTopicLength,
ShadowOperationName_t * const pxOperationName );
/**
* @brief Update callback for Shadow Operations.
*/
static void prvShadowUpdateCallback( BaseType_t xShadowClientID,
ShadowReturnCode_t xResult,
const ShadowOperationParams_t * const pxParams,
const char * const pcData,
uint32_t ulDataLength );
/**
* @brief Get callback for shadow operations
*/
static void prvShadowGetCallback( BaseType_t xShadowClientID,
ShadowReturnCode_t xResult,
ShadowOperationParams_t * const pxParams,
const char * const pcData,
uint32_t ulDataLength,
MQTTBufferHandle_t xBuffer );
/**
* @briefDelete callback for shadow operations
*/
static void prvShadowDeleteCallback( BaseType_t xShadowClientID,
ShadowReturnCode_t xResult,
const char * const pcData,
uint32_t ulDataLength );
/**
* @brief Handles error codes and messages in callbacks.
*/
static ShadowReturnCode_t prvGetErrorCodeAndMessage( const char * const pcData,
uint32_t ulDataLength,
BaseType_t xShadowClientID,
const char * const pcOperationName );
/**
* @briefShadow Operation common code.
*/
static ShadowReturnCode_t prvShadowOperation( ShadowOperationCallParams_t * pxParams );
static void prvSetSubscribedFlag( ShadowClient_t * const pxShadowClient,
ShadowOperationName_t xOperationName,
BaseType_t ucValue );
static uint8_t prvGetSubscribedFlag( const ShadowClient_t * const pxShadowClient,
ShadowOperationName_t xOperationName );
/**
* @brief Memory allocated to store Shadow Clients.
*/
static ShadowClient_t pxShadowClients[ shadowMAX_CLIENTS ];
/**
* @brief Custom prvCreateTopic function since prvCreateTopic is not MISRA 2012 compliant (rule 21.6) .
*/
static uint16_t prvCreateTopic( char * pcTopicString,
const uint16_t usBufferLength,
const char * pcTopicFormat,
const char * pcthingName );
/*-----------------------------------------------------------*/
static BaseType_t prvGetFreeShadowClient( void )
{
BaseType_t xIterator, xReturn = -1;
taskENTER_CRITICAL();
{
for( xIterator = 0; xIterator < shadowMAX_CLIENTS; xIterator++ )
{
if( pxShadowClients[ xIterator ].xInUse == pdFALSE )
{
pxShadowClients[ xIterator ].xInUse = pdTRUE;
xReturn = xIterator;
break;
}
}
}
taskEXIT_CRITICAL();
return xReturn;
}
/*-----------------------------------------------------------*/
static BaseType_t prvGetCallbackCatalogEntry( CallbackCatalogEntry_t * const pxCallbackCatalog,
const char * const pcThingName ) /*_RB_ I find the name of this function confusing compared to what it is actually doing. */
{ /* Todo: sub manager. */
CallbackCatalogEntry_t * pxCallbackCatalogEntry;
BaseType_t xIterator, xReturn = -1, xThingNameFound = pdFALSE;
size_t xThingNameLengh;
size_t xThingName_cb_Lengh;
taskENTER_CRITICAL();
{
for( xIterator = 0; xIterator < shadowCLIENT_MAX_THINGS_WITH_CALLBACKS; xIterator++ )
{
pxCallbackCatalogEntry = &( pxCallbackCatalog[ xIterator ] );
if( pxCallbackCatalogEntry->xInUse == pdFALSE )
{
xReturn = xIterator;
}
else
{
xThingNameLengh = strlen( pxCallbackCatalogEntry->xCallbackInfo.pcThingName );
xThingName_cb_Lengh = strlen( pcThingName );
if( xThingNameLengh == xThingName_cb_Lengh )
{
if( strncmp( pcThingName,
pxCallbackCatalogEntry->xCallbackInfo.pcThingName,
xThingNameLengh ) == 0 )
{
xReturn = xIterator;
xThingNameFound = pdTRUE;
break;
}
}
}
}
}
taskEXIT_CRITICAL();
if( xThingNameFound == pdFALSE )
{
pxCallbackCatalog[ xReturn ].xInUse = pdTRUE;
pxCallbackCatalog[ xReturn ].xCallbackInfo.pcThingName = pcThingName;
}
return xReturn;
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvConvertMQTTReturnCode( MQTTAgentReturnCode_t xMQTTReturn,
ShadowClientHandle_t xShadowClientHandle,
const char * const pcDebugMessageSubject )
{
ShadowReturnCode_t xReturn = eShadowUnknown;
switch( xMQTTReturn )
{
case eMQTTAgentSuccess:
Shadow_debug_printf( ( "[Shadow %d] MQTT: %s succeeded.\r\n",
( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
pcDebugMessageSubject ) );
xReturn = eShadowSuccess;
break;
case eMQTTAgentTimeout:
Shadow_debug_printf( ( "[Shadow %d] MQTT: %s timed out.\r\n",
( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
pcDebugMessageSubject ) );
xReturn = eShadowTimeout;
break;
case eMQTTAgentFailure:
default:
Shadow_debug_printf( ( "[Shadow %d] MQTT: %s failed.\r\n",
( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
pcDebugMessageSubject ) );
xReturn = eShadowFailure;
break;
}
/* Prevent compiler warning in case Shadow_debug_printf() is not defined. */
( void ) xShadowClientHandle;
( void ) pcDebugMessageSubject;
return xReturn;
}
/*-----------------------------------------------------------*/
static uint16_t prvCreateTopic( char * pcTopicString,
const uint16_t usBufferLength,
const char * pcTopicFormat,
const char * pcthingName )
{
uint16_t usTopicFormatIdx;
uint16_t usThingIdx;
uint16_t usTopicFormatSize;
uint16_t usThingNameSize;
uint16_t usTopicSize;
uint16_t usTopicIdx = 0;
char cCurrentChar;
char cPrevChar = '0';
usTopicFormatSize = ( uint16_t ) strlen( pcTopicFormat );
usThingNameSize = ( uint16_t ) strlen( pcthingName );
/* Remove 2 because %s character is not printed. */
usTopicSize = usTopicFormatSize + usThingNameSize - ( uint16_t ) 2;
configASSERT( usTopicSize < usBufferLength );
for( usTopicFormatIdx = 0;
usTopicFormatIdx < usTopicFormatSize;
usTopicFormatIdx++ )
{
cCurrentChar = pcTopicFormat[ usTopicFormatIdx ];
if( ( cPrevChar == '%' ) && ( cCurrentChar == 's' ) )
{
for( usThingIdx = 0;
usThingIdx < usThingNameSize;
usThingIdx++ )
{
pcTopicString[ usThingIdx + usTopicFormatIdx - ( uint16_t ) 1 ]
= ( char ) pcthingName[ usThingIdx ];
}
usTopicIdx += usThingNameSize - ( uint16_t ) 1; /* Remove 1 because %s characters are not printed*/
}
else
{
pcTopicString[ usTopicIdx ] = cCurrentChar;
usTopicIdx++;
}
cPrevChar = cCurrentChar;
}
pcTopicString[ usTopicIdx ] = '\0';
return usTopicSize;
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvRegisterCallback( BaseType_t xShadowClientID,
const void ** const ppvOldCallback,
const void ** const ppvNewCallback,
const char * const pcThingName,
const uint8_t * const pucTopicFormat,
TickType_t xTimeoutTicks )
{
uint8_t pucTopicString[ shadowTOPIC_BUFFER_LENGTH ];
ShadowReturnCode_t xReturn = eShadowSuccess;
MQTTAgentSubscribeParams_t xSubscribeParams;
MQTTAgentUnsubscribeParams_t xUnsubscribeParams;
ShadowClient_t * pxShadowClient;
MQTTAgentReturnCode_t xMQTTReturn;
uint16_t usTopicLength;
usTopicLength = prvCreateTopic( ( char * ) pucTopicString, shadowTOPIC_BUFFER_LENGTH,
( const char * ) pucTopicFormat, pcThingName );
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
if( *ppvOldCallback != NULL )
{
xUnsubscribeParams.usTopicLength = usTopicLength;
xUnsubscribeParams.pucTopic = pucTopicString;
xMQTTReturn = MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
&xUnsubscribeParams,
xTimeoutTicks );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Unsubscribe from callback topic" );
}
/* Registering a new callback; subscribe to topic. */
if( *ppvNewCallback != NULL )
{
xSubscribeParams.usTopicLength = usTopicLength;
xSubscribeParams.pucTopic = pucTopicString;
#ifdef mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT
xSubscribeParams.pvPublishCallbackContext = NULL;
xSubscribeParams.pxPublishCallback = NULL;
#endif /* mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT */
/* Shadow service always publishes QoS 1, regardless of the value below. */
xSubscribeParams.xQoS = eMQTTQoS1;
xMQTTReturn = MQTT_AGENT_Subscribe( pxShadowClient->xMQTTClient,
&xSubscribeParams,
xTimeoutTicks );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Subscribe to callback topic" );
}
/* Change the callback. */
if( xReturn == eShadowSuccess )
{
*ppvOldCallback = ( *ppvNewCallback );
}
return xReturn;
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvShadowSubscribeToAcceptedRejected( BaseType_t
xShadowClientID,
const char * const pcThingName,
const char * const
pcAcceptedTopic,
const char * const pcRejectedTopic,
TimeOutData_t * const pxTimeOutData )
{
ShadowReturnCode_t xReturn;
ShadowClient_t * pxShadowClient;
MQTTAgentSubscribeParams_t xSubscribeParams;
MQTTAgentUnsubscribeParams_t xUnsubscribeParams;
MQTTAgentReturnCode_t xMQTTReturn;
TickType_t xTimeoutTicks;
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
/* MQTT subscription parameters. */
xSubscribeParams.pucTopic = pxShadowClient->pucTopicBuffer;
/* Shadow service always publishes QoS 1, regardless of the value below. */
xSubscribeParams.xQoS = eMQTTQoS1;
#ifdef mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT
xSubscribeParams.pvPublishCallbackContext = NULL;
xSubscribeParams.pxPublishCallback = NULL;
#endif /* mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT */
/* Fill the accepted topic. */
xSubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
pcAcceptedTopic,
pcThingName );
xMQTTReturn = MQTT_AGENT_Subscribe( pxShadowClient->xMQTTClient,
&xSubscribeParams,
pxTimeOutData->xTicksRemaining );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Subscribe to accepted topic" );
if( xReturn == eShadowSuccess )
{
/* Fill the rejected topic. */
xSubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
pcRejectedTopic, pcThingName );
xMQTTReturn = MQTT_AGENT_Subscribe( pxShadowClient->xMQTTClient,
&xSubscribeParams,
pxTimeOutData->xTicksRemaining );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Subscribe to rejected topic" );
if( xReturn != eShadowSuccess )
{
xUnsubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
pcAcceptedTopic,
pcThingName );
xUnsubscribeParams.pucTopic = pxShadowClient->pucTopicBuffer;
xTimeoutTicks = pdMS_TO_TICKS( shadowCLEANUP_TIME_MS );
( void ) MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
&xUnsubscribeParams,
xTimeoutTicks );
( void ) prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Cleanup: Unsubscribe from accepted topic" );
}
}
return xReturn;
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvShadowUnsubscribeFromAcceptedRejected( BaseType_t
xShadowClientID,
const char * const pcThingName,
const char * const
pcAcceptedTopic,
const char * const pcRejectedTopic,
TimeOutData_t * const pxTimeOutData )
{
ShadowReturnCode_t xReturn = eShadowFailure;
ShadowClient_t * pxShadowClient;
MQTTAgentUnsubscribeParams_t xUnsubscribeParams;
MQTTAgentReturnCode_t xMQTTReturn;
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
/* MQTT unsubscribe parameters. */
xUnsubscribeParams.pucTopic = pxShadowClient->pucTopicBuffer;
if( pcAcceptedTopic != NULL )
{
/* Fill the accepted topic. */
xUnsubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
pcAcceptedTopic,
pcThingName );
xMQTTReturn = MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
&xUnsubscribeParams,
pxTimeOutData->xTicksRemaining );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Unsubscribe from accepted topic" );
}
if( pcRejectedTopic != NULL )
{
/* Fill the rejected topic. */
xUnsubscribeParams.usTopicLength = prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
pcRejectedTopic,
pcThingName );
xMQTTReturn = MQTT_AGENT_Unsubscribe( pxShadowClient->xMQTTClient,
&xUnsubscribeParams,
pxTimeOutData->xTicksRemaining );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Unsubscribe from rejected topic" );
}
return xReturn;
}
/*-----------------------------------------------------------*/
static BaseType_t prvShadowMQTTCallback( void * pvUserData,
const MQTTAgentCallbackParams_t * const pxCallbackParams )
{
BaseType_t xOperationMatched = pdFALSE;
ShadowClient_t * pxShadowClient;
const MQTTPublishData_t * pxPublishData;
ShadowOperationName_t xOperationName;
ShadowReturnCode_t xResult;
const CallbackCatalogEntry_t * pxCallbackCatalogEntry;
BaseType_t xReturn = pdFALSE;
BaseType_t xCompareLen;
BaseType_t xShadowClientID;
xShadowClientID = *( ( BaseType_t * ) pvUserData ); /*lint !e9087 Safe cast from pointer handle. */
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
if( pxCallbackParams->xMQTTEvent == eMQTTAgentPublish )
{
pxPublishData = ( &( pxCallbackParams->u.xPublishData ) );
/* If xOperationMutex is locked, the client is waiting on the acceptance
* or rejection of a publish. Publish results take priority over user notify
* callbacks. This also means that the client will not be notified of gets or
* deletes performed by itself in a user notify callback. However, the client
* will still be notified of updates performed by itself if it has registered
* a callback for /update/documents or update/delta. */
if( ( uint16_t ) uxSemaphoreGetCount( pxShadowClient->xOperationMutex ) == ( uint16_t ) 0 )
{
/* Verify Thing Name and operation by comparing the received topic with
* the current operation's topic. */
xCompareLen = ( BaseType_t ) configMIN( ( BaseType_t ) strlen( ( const char * ) pxShadowClient->pucTopicBuffer ),
( BaseType_t ) pxPublishData->usTopicLength );
if( strncmp( ( const char * ) pxPublishData->pucTopic,
( const char * ) pxShadowClient->pucTopicBuffer,
( size_t ) xCompareLen ) == 0 )
{
/* Parse the in-progress operation and result. */
xOperationName = ( pxShadowClient->pxOperationData )->xOperationInProgress;
xResult = prvParseShadowOperationStatus( pxPublishData->pucTopic,
pxPublishData->usTopicLength );
/* Both an operation and result were identified, and both match
* the operation this Shadow Client is waiting on; call the
* operation-specific callback. */
if( ( xResult != eShadowUnknown ) && ( xOperationName != eShadowOperationOther ) )
{
xOperationMatched = pdTRUE;
switch( xOperationName )
{
case eShadowOperationUpdate:
prvShadowUpdateCallback( xShadowClientID,
xResult,
( pxShadowClient->pxOperationData )->pxOperationParams,
( const char * ) pxPublishData->pvData,
pxPublishData->ulDataLength );
break;
case eShadowOperationGet:
prvShadowGetCallback( xShadowClientID,
xResult,
( pxShadowClient->pxOperationData )->pxOperationParams,
( const char * ) pxPublishData->pvData,
pxPublishData->ulDataLength,
pxPublishData->xBuffer );
/* Only take an MQTT buffer if the Get operation succeeded. */
if( xResult == eShadowSuccess )
{
xReturn = pdTRUE;
}
break;
case eShadowOperationDelete:
prvShadowDeleteCallback( xShadowClientID,
xResult,
( const char * ) pxPublishData->pvData,
pxPublishData->ulDataLength );
break;
default:
/* Should not fall here. */
break;
}
}
}
}
/* If the received topic doesn't match the current operation, it's
* still possible for it to match a registered callback. */
if( xOperationMatched == pdFALSE )
{
pxCallbackCatalogEntry = prvMatchCallbackTopic( pxShadowClient,
pxPublishData->pucTopic, pxPublishData->usTopicLength,
&xOperationName );
if( pxCallbackCatalogEntry != NULL )
{
switch( xOperationName )
{
case eShadowOperationUpdateDocuments:
xReturn = pxCallbackCatalogEntry->xCallbackInfo.xShadowUpdatedCallback( pvUserData,
pxCallbackCatalogEntry->xCallbackInfo.pcThingName,
( const char * ) pxPublishData->pvData,
pxPublishData->ulDataLength,
pxPublishData->xBuffer );
break;
case eShadowOperationUpdateDelta:
xReturn = pxCallbackCatalogEntry->xCallbackInfo.xShadowDeltaCallback( pvUserData,
pxCallbackCatalogEntry->xCallbackInfo.pcThingName,
( const char * ) pxPublishData->pvData,
pxPublishData->ulDataLength,
pxPublishData->xBuffer );
break;
case eShadowOperationDeletedByAnother:
pxCallbackCatalogEntry->xCallbackInfo.xShadowDeletedCallback( pvUserData,
pxCallbackCatalogEntry->xCallbackInfo.pcThingName );
break;
default:
/* Should not fall here. */
break;
}
}
}
}
/* The Shadow Client assumes all subscriptions are lost on disconnect. */
else
{
if( pxCallbackParams->xMQTTEvent == eMQTTAgentDisconnect )
{
Shadow_debug_printf( ( "[Shadow %d] Warning: got an MQTT disconnect"
" message.\r\n", xShadowClientID ) );
prvSetSubscribedFlag( pxShadowClient, eShadowOperationUpdate, 0 );
prvSetSubscribedFlag( pxShadowClient, eShadowOperationGet, 0 );
prvSetSubscribedFlag( pxShadowClient, eShadowOperationDelete, 0 );
/*_RB_ TODO below. */
/* TODO: resubscribe to all callback topics. */
}
}
return xReturn;
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvParseShadowOperationStatus( const uint8_t * const
pucTopic,
uint16_t usTopicLength )
{
ShadowReturnCode_t xResult = eShadowUnknown;
const uint8_t * pucTopicStatus;
size_t xCompareLength;
xCompareLength = strlen( shadowTOPIC_SUFFIX_ACCEPTED );
pucTopicStatus = pucTopic + usTopicLength - xCompareLength;
if( strncmp( ( const char * ) pucTopicStatus,
shadowTOPIC_SUFFIX_ACCEPTED,
xCompareLength ) == 0 )
{
xResult = eShadowSuccess;
}
else
{
xCompareLength = strlen( shadowTOPIC_SUFFIX_REJECTED );
pucTopicStatus = pucTopic + usTopicLength - xCompareLength;
if( strncmp( ( const char * ) pucTopicStatus,
shadowTOPIC_SUFFIX_REJECTED,
xCompareLength ) == 0 )
{
xResult = eShadowFailure;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
static const CallbackCatalogEntry_t * prvMatchCallbackTopic( const ShadowClient_t * const pxShadowClient,
const uint8_t * const pucTopic,
uint16_t usTopicLength,
ShadowOperationName_t * const pxOperationName )
{
const CallbackCatalogEntry_t * pxReturn = NULL;
BaseType_t xIterator, xTopicFound = pdFALSE;
uint8_t pucTopicBuffer[ shadowTOPIC_BUFFER_LENGTH ];
size_t xCompareLength;
for( xIterator = 0; xIterator < shadowCLIENT_MAX_THINGS_WITH_CALLBACKS; xIterator++ )
{
pxReturn = &( pxShadowClient->pxCallbackCatalog[ xIterator ] );
if( pxReturn->xInUse == pdTRUE )
{
xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
shadowTOPIC_PREFIX, pxReturn->xCallbackInfo.pcThingName );
if( strncmp( ( const char * ) pucTopicBuffer,
( const char * ) pucTopic,
xCompareLength ) == 0 )
{
xTopicFound = pdTRUE;
xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH, shadowTOPIC_UPDATE_DOCUMENTS,
pxReturn->xCallbackInfo.pcThingName );
xCompareLength = configMAX( xCompareLength, usTopicLength );
if( pxOperationName != NULL )
{
if( strncmp( ( const char * ) pucTopicBuffer,
( const char * ) pucTopic,
xCompareLength ) == 0 )
{
*pxOperationName = eShadowOperationUpdateDocuments;
}
else
{
xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH, shadowTOPIC_UPDATE_DELTA,
pxReturn->xCallbackInfo.pcThingName );
xCompareLength = configMAX( xCompareLength, usTopicLength );
if( strncmp( ( const char * ) pucTopicBuffer,
( const char * ) pucTopic,
xCompareLength ) == 0 )
{
*pxOperationName = eShadowOperationUpdateDelta;
}
else
{
xCompareLength = prvCreateTopic( ( char * ) pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
shadowTOPIC_DELETE_ACCEPTED,
pxReturn->xCallbackInfo.pcThingName );
xCompareLength = configMAX( xCompareLength, usTopicLength );
if( strncmp( ( const char * ) pucTopicBuffer,
( const char * ) pucTopic,
xCompareLength ) == 0 )
{
*pxOperationName = eShadowOperationDeletedByAnother;
}
else
{
*pxOperationName = eShadowOperationOther;
}
}
}
}
break;
}
}
}
if( xTopicFound == pdFALSE )
{
pxReturn = NULL;
}
return pxReturn;
}
/*-----------------------------------------------------------*/
static void prvShadowUpdateCallback( BaseType_t xShadowClientID,
ShadowReturnCode_t xResult,
const ShadowOperationParams_t * const pxParams,
const char * const pcData,
uint32_t ulDataLength )
{
/* Remove warning about unused parameters. */
( void ) pxParams;
ShadowClient_t * pxShadowClient;
BaseType_t xReturn;
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
/* Verify client token match. */
xReturn = SHADOW_JSONDocClientTokenMatch( pxParams->pcData,
pxParams->ulDataLength,
pcData,
ulDataLength );
if( xReturn == pdPASS )
{
pxShadowClient->xOperationResult = xResult;
/* For failures, get the code and message. */
if( xResult == eShadowFailure )
{
pxShadowClient->xOperationResult = prvGetErrorCodeAndMessage( pcData,
ulDataLength,
xShadowClientID,
shadowTOPIC_OPERATION_UPDATE );
}
configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
}
}
/*-----------------------------------------------------------*/
static void prvShadowGetCallback( BaseType_t xShadowClientID,
ShadowReturnCode_t xResult,
ShadowOperationParams_t * const pxParams,
const char * const pcData,
uint32_t ulDataLength,
MQTTBufferHandle_t xBuffer )
{
ShadowClient_t * pxShadowClient;
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
pxShadowClient->xOperationResult = xResult;
/* For successes, fill the user's buffer with the Shadow document. */
if( xResult == eShadowSuccess )
{
pxParams->pcData = pcData;
pxParams->ulDataLength = ulDataLength;
pxParams->xBuffer = xBuffer;
}
/* For failures , get the code and message. */
else
{
pxShadowClient->xOperationResult = prvGetErrorCodeAndMessage( pcData,
ulDataLength,
xShadowClientID,
shadowTOPIC_OPERATION_GET );
pxParams->pcData = NULL;
pxParams->ulDataLength = 0;
}
configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
}
/*-----------------------------------------------------------*/
static void prvShadowDeleteCallback( BaseType_t xShadowClientID,
ShadowReturnCode_t xResult,
const char * const pcData,
uint32_t ulDataLength )
{
ShadowClient_t * pxShadowClient;
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
pxShadowClient->xOperationResult = xResult;
if( xResult == eShadowFailure )
{
pxShadowClient->xOperationResult = prvGetErrorCodeAndMessage( pcData,
ulDataLength,
xShadowClientID,
shadowTOPIC_OPERATION_DELETE );
}
configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvGetErrorCodeAndMessage( const char * const pcData,
uint32_t ulDataLength,
BaseType_t xShadowClientID,
const char * const pcOperationName )
{
ShadowReturnCode_t xErrorCode;
char * pcErrorMessage;
uint16_t usErrorMessageLength;
xErrorCode = ( ShadowReturnCode_t ) SHADOW_JSONGetErrorCodeAndMessage( pcData,
ulDataLength,
&pcErrorMessage,
&usErrorMessageLength );
if( xErrorCode > 0 )
{
Shadow_debug_printf( ( "[Shadow %d] %s rejected, code %d: %.*s.\r\n",
xShadowClientID, pcOperationName,
xErrorCode,
usErrorMessageLength,
pcErrorMessage ) );
}
else
{
Shadow_debug_printf( ( "[Shadow %d] JSON parse error while parsing"
" error code and message.\r\n", xShadowClientID ) );
}
/* Remove compiler warnings in the case that Shadow_debug_printf() is not
* defined. */
( void ) pcOperationName;
( void ) xShadowClientID;
/* SHADOW_JSONGetErrorCodeAndMessage may return 0 for bad pointer arguments.
* Convert this to a JSON parse error, as 0 is eShadowSuccess. */
if( xErrorCode == 0 )
{
xErrorCode = eShadowJSMNInval;
}
return xErrorCode;
}
/*-----------------------------------------------------------*/
static ShadowReturnCode_t prvShadowOperation( ShadowOperationCallParams_t * pxParams )
{
ShadowReturnCode_t xReturn = eShadowFailure;
MQTTAgentPublishParams_t xPublishParams;
ShadowClient_t * pxShadowClient;
TimeOutData_t xTimeOutData;
ShadowOperationData_t xOperationData;
MQTTAgentReturnCode_t xMQTTReturn;
/* Initialize timeout data. */
xTimeOutData.xTicksRemaining = pxParams->xTimeoutTicks;
/* Identify the relevant Shadow Client, then lock that client's operation mutex.
* This allows only one operation to be in progress. */
pxShadowClient = &( pxShadowClients[ ( pxParams->xShadowClientID ) ] );
if( xSemaphoreTake( pxShadowClient->xOperationMutex,
xTimeOutData.xTicksRemaining ) == pdPASS )
{
/* Subscribe to accepted/rejected if necessary. */
if( ( BaseType_t ) prvGetSubscribedFlag( pxShadowClient,
pxParams->xOperationName ) == pdFALSE )
{
xReturn = prvShadowSubscribeToAcceptedRejected( pxParams->xShadowClientID,
( pxParams->pxOperationParams )->pcThingName,
pxParams->pcOperationAcceptedTopic,
pxParams->pcOperationRejectedTopic,
&xTimeOutData );
}
else
{
xReturn = eShadowSuccess;
}
if( xReturn == eShadowSuccess )
{
/* The subscribe to update/accepted and update/rejected succeeded,
* so set the appropriate flag. */
prvSetSubscribedFlag( pxShadowClient, pxParams->xOperationName, 1 );
/* This is guarded by xOperationMutex and should never fail. */
configASSERT( xSemaphoreTake( pxShadowClient->xCallbackSemaphore,
xTimeOutData.xTicksRemaining ) == pdPASS );
/* Fill pucTopicBuffer with the update topic. */
xPublishParams.usTopicLength =
prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
pxParams->pcOperationTopic,
( pxParams->pxOperationParams )->pcThingName );
/* Operation parameters. */
xPublishParams.pucTopic = pxShadowClient->pucTopicBuffer;
xPublishParams.pvData = pxParams->pcPublishMessage;
xPublishParams.ulDataLength = pxParams->ulPublishMessageLength;
xPublishParams.xQoS = ( pxParams->pxOperationParams )->xQoS;
/* Data to pass to the callback. */
xOperationData.xOperationInProgress = pxParams->xOperationName;
xOperationData.pxOperationParams = pxParams->pxOperationParams;
pxShadowClient->pxOperationData = &xOperationData;
xMQTTReturn = MQTT_AGENT_Publish( pxShadowClient->xMQTTClient,
&xPublishParams,
xTimeOutData.xTicksRemaining );
/* Publish to operation topic. */
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) ( pxParams->xShadowClientID ), /*lint !e923 Safe cast from pointer handle. */
"Publish to operation topic" );
if( xReturn == eShadowSuccess )
{
/* Wait for the semaphore to become available again; it should be
* released by the update callback. */
if( xSemaphoreTake( pxShadowClient->xCallbackSemaphore,
xTimeOutData.xTicksRemaining ) != pdPASS )
{
Shadow_debug_printf( ( "[Shadow %d] Timeout waiting on"
" %s accepted/rejected.\r\n",
pxParams->xShadowClientID,
pxParams->pcOperationName ) );
xReturn = eShadowTimeout;
}
else
{
/* The update callback reports its status as xOperationResult. */
xReturn = pxShadowClient->xOperationResult;
}
}
configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
}
/* Unsubscribe. */
if( ( pxParams->pxOperationParams )->ucKeepSubscriptions == ( uint8_t ) 0 )
{
xTimeOutData.xTicksRemaining = configMAX( xTimeOutData.xTicksRemaining,
pdMS_TO_TICKS( shadowCLEANUP_TIME_MS ) );
/* If the Shadow client is subscribed to delete/accepted for this
* Thing for a user notify callback, do not unsubscribe; that would
* break callback notify. */
if( pxParams->xOperationName == eShadowOperationDelete )
{
( void ) prvCreateTopic( ( char * ) pxShadowClient->pucTopicBuffer,
shadowTOPIC_BUFFER_LENGTH,
shadowTOPIC_DELETE_ACCEPTED,
pxParams->pxOperationParams->pcThingName );
/* If there's a callback registered for delete/accepted, only
* unsubscribe from delete/rejected. */
if( prvMatchCallbackTopic( pxShadowClient,
pxShadowClient->pucTopicBuffer,
( uint16_t )
strlen( ( const char * ) pxShadowClient->pucTopicBuffer ),
NULL ) == NULL )
{
if( prvShadowUnsubscribeFromAcceptedRejected( pxParams->xShadowClientID,
pxParams->pxOperationParams->pcThingName,
NULL,
pxParams->pcOperationRejectedTopic,
&xTimeOutData ) == eShadowSuccess )
{
prvSetSubscribedFlag( pxShadowClient,
pxParams->xOperationName,
0 );
}
}
}
else
{
if( prvShadowUnsubscribeFromAcceptedRejected( pxParams->xShadowClientID,
pxParams->pxOperationParams->pcThingName,
pxParams->pcOperationAcceptedTopic,
pxParams->pcOperationRejectedTopic,
&xTimeOutData ) == eShadowSuccess )
{
prvSetSubscribedFlag( pxShadowClient,
pxParams->xOperationName,
0 );
}
}
}
/* Delete this operation's data so that the next operation has a clean Shadow
* Client, then release the operation mutex. */
pxShadowClient->pxOperationData = NULL;
pxShadowClient->xOperationResult = eShadowSuccess;
memset( pxShadowClient->pucTopicBuffer, 0, shadowTOPIC_BUFFER_LENGTH );
configASSERT( xSemaphoreGive( pxShadowClient->xOperationMutex )
== pdPASS );
}
return xReturn;
}
/*-----------------------------------------------------------*/
static void prvSetSubscribedFlag( ShadowClient_t * const pxShadowClient,
ShadowOperationName_t xOperationName,
BaseType_t ucValue )
{
switch( xOperationName )
{
case eShadowOperationUpdate:
pxShadowClient->xUpdateSubscribed = ucValue;
break;
case eShadowOperationGet:
pxShadowClient->xGetSubscribed = ucValue;
break;
case eShadowOperationDelete:
pxShadowClient->xDeleteSubscribed = ucValue;
break;
default:
/* Should not fall here. */
break;
}
}
/*-----------------------------------------------------------*/
static uint8_t prvGetSubscribedFlag( const ShadowClient_t * const pxShadowClient,
ShadowOperationName_t xOperationName )
{
uint8_t ucReturn = 0;
switch( xOperationName )
{
case eShadowOperationUpdate:
ucReturn = ( uint8_t ) pxShadowClient->xUpdateSubscribed;
break;
case eShadowOperationGet:
ucReturn = ( uint8_t ) pxShadowClient->xGetSubscribed;
break;
case eShadowOperationDelete:
ucReturn = ( uint8_t ) pxShadowClient->xDeleteSubscribed;
break;
default:
/* Should not fall here. */
break;
}
return ucReturn;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_ClientCreate( ShadowClientHandle_t * pxShadowClientHandle,
const ShadowCreateParams_t * const pxShadowCreateParams )
{
ShadowClient_t * pxShadowClient;
BaseType_t xShadowClientID;
ShadowReturnCode_t xReturn = eShadowFailure;
MQTTAgentReturnCode_t xMQTTReturn;
configASSERT( ( pxShadowClientHandle != NULL ) );
configASSERT( ( pxShadowCreateParams != NULL ) );
/* For now, only dedicated MQTT clients are supported. Remove this assert
* once support for shared clients is added. */
configASSERT( ( pxShadowCreateParams->xMQTTClientType == eDedicatedMQTTClient ) );
xShadowClientID = prvGetFreeShadowClient();
configASSERT( xShadowClientID >= 0 );
if( xShadowClientID >= 0 )
{
pxShadowClient = &( pxShadowClients[ xShadowClientID ] );
xMQTTReturn = MQTT_AGENT_Create( &( pxShadowClient->xMQTTClient ) );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
( ShadowClientHandle_t ) xShadowClientID, /*lint !e923 Safe cast from pointer handle. */
"Creation of dedicated MQTT client" );
if( xReturn == eShadowSuccess )
{
/* Create synchronization mechanisms; these calls should never fail. */
pxShadowClient->xCallbackSemaphore = xSemaphoreCreateBinaryStatic( &( pxShadowClient->xCallbackSemaphoreBuffer ) );
pxShadowClient->xOperationMutex = xSemaphoreCreateMutexStatic( &( pxShadowClient->xOperationMutexBuffer ) );
configASSERT( xSemaphoreGive( pxShadowClient->xCallbackSemaphore ) == pdPASS );
/* Set the output parameter. */
*pxShadowClientHandle = ( ShadowClientHandle_t ) xShadowClientID; /*lint !e923 Safe cast from pointer handle. */
}
else
{
Shadow_debug_printf( ( "[Shadow Init] Failed to create dedicated"
"client; deleting partially-initialized client.\r\n" ) );
/* Delete the partially-initialized client. */
xReturn = SHADOW_ClientDelete( *( pxShadowClientHandle ) );
}
}
return xReturn;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_ClientConnect( ShadowClientHandle_t xShadowClientHandle,
MQTTAgentConnectParams_t * const pxConnectParams,
TickType_t xTimeoutTicks )
{
ShadowClient_t * pxShadowClient;
ShadowReturnCode_t xReturn;
MQTTAgentReturnCode_t xMQTTReturn;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( xShadowClientHandle == *( ( ShadowClientHandle_t * ) ( pxConnectParams->pvUserData ) ) ); /*lint !e9087 Safe cast from opaque context. */
pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
pxConnectParams->pxCallback = prvShadowMQTTCallback;
xMQTTReturn = MQTT_AGENT_Connect( pxShadowClient->xMQTTClient,
pxConnectParams,
xTimeoutTicks );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
xShadowClientHandle,
"Connect" );
pxConnectParams->pxCallback = NULL;
return xReturn;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_ClientDisconnect( ShadowClientHandle_t xShadowClientHandle )
{
ShadowClient_t * pxShadowClient;
MQTTAgentReturnCode_t xMQTTReturn;
TickType_t xTimeoutTicks;
ShadowReturnCode_t xReturn;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
xTimeoutTicks = pdMS_TO_TICKS( shadowCLEANUP_TIME_MS );
xMQTTReturn = MQTT_AGENT_Disconnect( pxShadowClient->xMQTTClient,
xTimeoutTicks );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
xShadowClientHandle,
"Disconnect" );
return xReturn;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_ClientDelete( ShadowClientHandle_t xShadowClientHandle )
{
ShadowClient_t * pxShadowClient;
ShadowReturnCode_t xReturn = eShadowFailure;
MQTTAgentReturnCode_t xMQTTReturn;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
xMQTTReturn = MQTT_AGENT_Delete( pxShadowClient->xMQTTClient );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
xShadowClientHandle,
"MQTT Client delete" );
if( xReturn == eShadowSuccess )
{
taskENTER_CRITICAL();
memset( pxShadowClient, 0, sizeof( ShadowClient_t ) );
taskEXIT_CRITICAL();
}
return xReturn;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_Update( ShadowClientHandle_t xShadowClientHandle,
ShadowOperationParams_t * const pxUpdateParams,
TickType_t xTimeoutTicks )
{
ShadowOperationCallParams_t xUpdateCallParams;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxUpdateParams != NULL ) );
configASSERT( ( pxUpdateParams->pcThingName != NULL ) );
configASSERT( ( pxUpdateParams->pcData != NULL ) );
configASSERT( ( pxUpdateParams->xQoS == eMQTTQoS0 ||
pxUpdateParams->xQoS == eMQTTQoS1 ) );
xUpdateCallParams.xShadowClientID = ( BaseType_t ) xShadowClientHandle; /*lint !e923 Safe cast from pointer handle. */
xUpdateCallParams.xOperationName = eShadowOperationUpdate;
xUpdateCallParams.pcOperationName = shadowTOPIC_OPERATION_UPDATE;
xUpdateCallParams.pcOperationTopic = shadowTOPIC_UPDATE;
xUpdateCallParams.pcOperationAcceptedTopic = shadowTOPIC_UPDATE_ACCEPTED;
xUpdateCallParams.pcOperationRejectedTopic = shadowTOPIC_UPDATE_REJECTED;
xUpdateCallParams.pcPublishMessage = pxUpdateParams->pcData;
xUpdateCallParams.ulPublishMessageLength = pxUpdateParams->ulDataLength;
xUpdateCallParams.pxOperationParams = ( ShadowOperationParams_t * ) pxUpdateParams;
xUpdateCallParams.xTimeoutTicks = xTimeoutTicks;
return prvShadowOperation( &xUpdateCallParams );
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_Get( ShadowClientHandle_t xShadowClientHandle,
ShadowOperationParams_t * const pxGetParams,
TickType_t xTimeoutTicks )
{
ShadowOperationCallParams_t xGetCallParams;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxGetParams != NULL ) );
configASSERT( ( pxGetParams->pcThingName != NULL ) );
configASSERT( ( pxGetParams->xQoS == eMQTTQoS0 ||
pxGetParams->xQoS == eMQTTQoS1 ) );
xGetCallParams.xShadowClientID = ( BaseType_t ) xShadowClientHandle; /*lint !e923 Safe cast from pointer handle. */
xGetCallParams.xOperationName = eShadowOperationGet;
xGetCallParams.pcOperationName = shadowTOPIC_OPERATION_GET;
xGetCallParams.pcOperationTopic = shadowTOPIC_GET;
xGetCallParams.pcOperationAcceptedTopic = shadowTOPIC_GET_ACCEPTED;
xGetCallParams.pcOperationRejectedTopic = shadowTOPIC_GET_REJECTED;
xGetCallParams.pcPublishMessage = "";
xGetCallParams.ulPublishMessageLength = 0;
xGetCallParams.pxOperationParams = pxGetParams;
xGetCallParams.xTimeoutTicks = xTimeoutTicks;
return prvShadowOperation( &xGetCallParams );
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_Delete( ShadowClientHandle_t xShadowClientHandle,
ShadowOperationParams_t * const pxDeleteParams,
TickType_t xTimeoutTicks )
{
ShadowOperationCallParams_t xDeleteCallParams;
ShadowReturnCode_t xStatus;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxDeleteParams != NULL ) );
configASSERT( ( pxDeleteParams->pcThingName != NULL ) );
configASSERT( ( pxDeleteParams->xQoS == eMQTTQoS0 ||
pxDeleteParams->xQoS == eMQTTQoS1 ) );
xDeleteCallParams.xShadowClientID = ( BaseType_t ) xShadowClientHandle; /*lint !e923 Safe cast from pointer handle. */
xDeleteCallParams.xOperationName = eShadowOperationDelete;
xDeleteCallParams.pcOperationName = shadowTOPIC_OPERATION_DELETE;
xDeleteCallParams.pcOperationTopic = shadowTOPIC_DELETE;
xDeleteCallParams.pcOperationAcceptedTopic = shadowTOPIC_DELETE_ACCEPTED;
xDeleteCallParams.pcOperationRejectedTopic = shadowTOPIC_DELETE_REJECTED;
xDeleteCallParams.pcPublishMessage = "";
xDeleteCallParams.ulPublishMessageLength = 0;
xDeleteCallParams.pxOperationParams = ( ShadowOperationParams_t * ) pxDeleteParams;
xDeleteCallParams.xTimeoutTicks = xTimeoutTicks;
xStatus = prvShadowOperation( &xDeleteCallParams );
return xStatus;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_RegisterCallbacks( ShadowClientHandle_t xShadowClientHandle,
ShadowCallbackParams_t * const pxCallbackParams,
TickType_t xTimeoutTicks )
{
ShadowClient_t * pxShadowClient;
CallbackCatalogEntry_t * pxCallbackCatalogEntry;
ShadowReturnCode_t xReturn = eShadowFailure;
BaseType_t xCallbackCatalogIndex;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxCallbackParams != NULL ) );
configASSERT( ( pxCallbackParams->pcThingName != NULL ) );
pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
xCallbackCatalogIndex = prvGetCallbackCatalogEntry( pxShadowClient->pxCallbackCatalog,
pxCallbackParams->pcThingName );
configASSERT( xCallbackCatalogIndex >= 0 );
/* Initialize timeout data. */
pxCallbackCatalogEntry = &( pxShadowClient->pxCallbackCatalog
[ xCallbackCatalogIndex ] );
/*_RB_ Casting on these calls make the code unreadable. Types need changing to remove the need for the casts. */
/* ToDo: sub manager. */
xReturn = prvRegisterCallback( ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
( const void ** ) &( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowUpdatedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. No const is being cast away either.*/
( const void ** ) &( pxCallbackParams->xShadowUpdatedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. */
pxCallbackParams->pcThingName,
( const uint8_t * ) shadowTOPIC_UPDATE_DOCUMENTS,
xTimeoutTicks );
if( xReturn == eShadowSuccess )
{
xReturn = prvRegisterCallback( ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
( const void ** ) &( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeletedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. No const is being cast away either.*/
( const void ** ) &( pxCallbackParams->xShadowDeletedCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. */
pxCallbackParams->pcThingName,
( const uint8_t * ) shadowTOPIC_DELETE_ACCEPTED,
xTimeoutTicks );
}
if( xReturn == eShadowSuccess )
{
xReturn = prvRegisterCallback( ( BaseType_t ) xShadowClientHandle, /*lint !e923 Safe cast from pointer handle. */
( const void ** ) &( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeltaCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. No const is being cast away either.*/
( const void ** ) &( pxCallbackParams->xShadowDeltaCallback ), /*lint !e9087 !e9005 cast is opaque and recast correctly inside the function. */
pxCallbackParams->pcThingName,
( const uint8_t * ) shadowTOPIC_UPDATE_DELTA,
xTimeoutTicks );
}
if( ( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowUpdatedCallback == NULL ) &&
( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeltaCallback == NULL ) &&
( ( pxCallbackCatalogEntry->xCallbackInfo ).xShadowDeletedCallback == NULL ) )
{
taskENTER_CRITICAL();
{
memset( pxCallbackCatalogEntry,
0,
sizeof( CallbackCatalogEntry_t ) );
}
taskEXIT_CRITICAL();
}
return xReturn;
}
/*-----------------------------------------------------------*/
ShadowReturnCode_t SHADOW_ReturnMQTTBuffer( ShadowClientHandle_t xShadowClientHandle,
MQTTBufferHandle_t xBufferHandle )
{
ShadowClient_t * pxShadowClient;
MQTTAgentReturnCode_t xMQTTReturn;
ShadowReturnCode_t xReturn = eShadowFailure;
configASSERT( ( ( BaseType_t ) xShadowClientHandle >= 0 &&
( BaseType_t ) xShadowClientHandle < shadowMAX_CLIENTS ) ); /*lint !e923 Safe cast from pointer handle. */
pxShadowClient = &( pxShadowClients[ ( BaseType_t ) xShadowClientHandle ] ); /*lint !e923 Safe cast from pointer handle. */
configASSERT( ( pxShadowClient->xInUse == pdTRUE ) );
xMQTTReturn = MQTT_AGENT_ReturnBuffer( pxShadowClient->xMQTTClient,
xBufferHandle );
xReturn = prvConvertMQTTReturnCode( xMQTTReturn,
xShadowClientHandle,
"Return MQTT buffer" );
return xReturn;
}