mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-09-06 14:17:41 -04:00
Add the Labs projects provided in the V10.2.1_191129 zip file.
This commit is contained in:
parent
46e5937529
commit
e5708b38e9
801 changed files with 356576 additions and 0 deletions
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,925 @@
|
|||
/*
|
||||
* AWS IoT Shadow V2.1.0
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file aws_iot_shadow_operation.c
|
||||
* @brief Implements functions that process Shadow operations.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* Shadow internal include. */
|
||||
#include "private/aws_iot_shadow_internal.h"
|
||||
|
||||
/* Platform layer includes. */
|
||||
#include "platform/iot_threads.h"
|
||||
|
||||
/* MQTT include. */
|
||||
#include "iot_mqtt.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief First parameter to #_shadowOperation_match.
|
||||
*/
|
||||
typedef struct _operationMatchParams
|
||||
{
|
||||
_shadowOperationType_t type; /**< @brief DELETE, GET, or UPDATE. */
|
||||
const char * pThingName; /**< @brief Thing Name of Shadow operation. */
|
||||
size_t thingNameLength; /**< @brief Length of #_operationMatchParams_t.pThingName. */
|
||||
const char * pDocument; /**< @brief Shadow UPDATE response document. */
|
||||
size_t documentLength; /**< @brief Length of #_operationMatchParams_t.pDocument. */
|
||||
} _operationMatchParams_t;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Match a received Shadow response with a Shadow operation awaiting a
|
||||
* response.
|
||||
*
|
||||
* @param[in] pOperationLink Pointer to the link member of the #_shadowOperation_t
|
||||
* to check.
|
||||
* @param[in] pMatch Pointer to an #_operationMatchParams_t.
|
||||
*
|
||||
* @return `true` if `pMatch` matches the received response; `false` otherwise.
|
||||
*/
|
||||
static bool _shadowOperation_match( const IotLink_t * pOperationLink,
|
||||
void * pMatch );
|
||||
|
||||
/**
|
||||
* @brief Common function for processing received Shadow responses.
|
||||
*
|
||||
* @param[in] type DELETE, GET, or UPDATE.
|
||||
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _commonOperationCallback( _shadowOperationType_t type,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Shadow response is received for Shadow DELETE.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _deleteCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Shadow response is received for a Shadow GET.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _getCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Process an incoming Shadow document received when a Shadow GET is
|
||||
* accepted.
|
||||
*
|
||||
* @param[in] pOperation The GET operation associated with the incoming Shadow
|
||||
* document.
|
||||
* @param[in] pPublishInfo The received Shadow document (as an MQTT PUBLISH
|
||||
* message).
|
||||
*
|
||||
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY. Memory allocation
|
||||
* only happens for a waitable `pOperation`.
|
||||
*/
|
||||
static AwsIotShadowError_t _processAcceptedGet( _shadowOperation_t * pOperation,
|
||||
const IotMqttPublishInfo_t * pPublishInfo );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Shadow response is received for a Shadow UPDATE.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _updateCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Notify of a completed Shadow operation.
|
||||
*
|
||||
* @param[in] pOperation The operation which completed.
|
||||
*
|
||||
* Depending on the parameters passed to a user-facing Shadow function, the
|
||||
* notification will cause @ref shadow_function_wait to return or invoke a
|
||||
* user-provided callback.
|
||||
*/
|
||||
static void _notifyCompletion( _shadowOperation_t * pOperation );
|
||||
|
||||
/**
|
||||
* @brief Get a Shadow subscription to use with a Shadow operation.
|
||||
*
|
||||
* This function may use an existing Shadow subscription, or it may allocate a
|
||||
* new one.
|
||||
*
|
||||
* @param[in] pThingName Thing Name associated with operation.
|
||||
* @param[in] thingNameLength Length of `pThingName`.
|
||||
* @param[in] pTopicBuffer Contains the topic to use for subscribing.
|
||||
* @param[in] operationTopicLength The length of the base topic in `pTopicBuffer`.
|
||||
* @param[in] pOperation Shadow operation that needs a subscription.
|
||||
* @param[out] pFreeTopicBuffer Whether the caller may free `pTopicBuffer`
|
||||
* (which may be assigned to a subscription).
|
||||
*
|
||||
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY
|
||||
*/
|
||||
static AwsIotShadowError_t _findSubscription( const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
_shadowOperation_t * pOperation,
|
||||
bool * pFreeTopicBuffer );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
|
||||
|
||||
/**
|
||||
* @brief Printable names for each of the Shadow operations.
|
||||
*/
|
||||
const char * const _pAwsIotShadowOperationNames[] =
|
||||
{
|
||||
"DELETE",
|
||||
"GET",
|
||||
"UPDATE",
|
||||
"SET DELTA",
|
||||
"SET UPDATED"
|
||||
};
|
||||
#endif /* if LIBRARY_LOG_LEVEL > IOT_LOG_NONE */
|
||||
|
||||
/**
|
||||
* @brief List of active Shadow operations awaiting a response from the Shadow
|
||||
* service.
|
||||
*/
|
||||
IotListDouble_t _AwsIotShadowPendingOperations = { 0 };
|
||||
|
||||
/**
|
||||
* @brief Protects #_AwsIotShadowPendingOperations from concurrent access.
|
||||
*/
|
||||
IotMutex_t _AwsIotShadowPendingOperationsMutex;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _shadowOperation_match( const IotLink_t * pOperationLink,
|
||||
void * pMatch )
|
||||
{
|
||||
/* Because this function is called from a container function, the given link
|
||||
* must never be NULL. */
|
||||
AwsIotShadow_Assert( pOperationLink != NULL );
|
||||
|
||||
_shadowOperation_t * pOperation = IotLink_Container( _shadowOperation_t,
|
||||
pOperationLink,
|
||||
link );
|
||||
_operationMatchParams_t * pParam = ( _operationMatchParams_t * ) pMatch;
|
||||
_shadowSubscription_t * pSubscription = pOperation->pSubscription;
|
||||
const char * pClientToken = NULL;
|
||||
size_t clientTokenLength = 0;
|
||||
|
||||
/* Check for matching Thing Name and operation type. */
|
||||
bool match = ( pOperation->type == pParam->type ) &&
|
||||
( pParam->thingNameLength == pSubscription->thingNameLength ) &&
|
||||
( strncmp( pParam->pThingName,
|
||||
pSubscription->pThingName,
|
||||
pParam->thingNameLength ) == 0 );
|
||||
|
||||
/* For a Shadow UPDATE operation, compare the client tokens. */
|
||||
if( ( match == true ) && ( pOperation->type == SHADOW_UPDATE ) )
|
||||
{
|
||||
/* Check document pointers. */
|
||||
AwsIotShadow_Assert( pParam->pDocument != NULL );
|
||||
AwsIotShadow_Assert( pParam->documentLength > 0 );
|
||||
AwsIotShadow_Assert( pOperation->u.update.pClientToken != NULL );
|
||||
AwsIotShadow_Assert( pOperation->u.update.clientTokenLength > 0 );
|
||||
|
||||
IotLogDebug( "Verifying client tokens for Shadow UPDATE." );
|
||||
|
||||
/* Check for the client token in the UPDATE response document. */
|
||||
match = AwsIot_GetClientToken( pParam->pDocument,
|
||||
pParam->documentLength,
|
||||
&pClientToken,
|
||||
&clientTokenLength );
|
||||
|
||||
/* If the UPDATE response document has a client token, check that it
|
||||
* matches. */
|
||||
if( match == true )
|
||||
{
|
||||
match = ( clientTokenLength == pOperation->u.update.clientTokenLength ) &&
|
||||
( strncmp( pClientToken,
|
||||
pOperation->u.update.pClientToken,
|
||||
clientTokenLength ) == 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "Received a Shadow UPDATE response with no client token. "
|
||||
"This is possibly a response to a bad JSON document:\r\n%.*s",
|
||||
pParam->documentLength,
|
||||
pParam->pDocument );
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _commonOperationCallback( _shadowOperationType_t type,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
_shadowOperation_t * pOperation = NULL;
|
||||
IotLink_t * pOperationLink = NULL;
|
||||
AwsIotStatus_t status = AWS_IOT_UNKNOWN;
|
||||
_operationMatchParams_t param = { .type = ( _shadowOperationType_t ) 0 };
|
||||
uint32_t flags = 0;
|
||||
|
||||
/* Set operation type to search. */
|
||||
param.type = type;
|
||||
|
||||
/* Set the response document for a Shadow UPDATE. */
|
||||
if( type == SHADOW_UPDATE )
|
||||
{
|
||||
param.pDocument = pMessage->u.message.info.pPayload;
|
||||
param.documentLength = pMessage->u.message.info.payloadLength;
|
||||
}
|
||||
|
||||
/* Parse the Thing Name from the MQTT topic name. */
|
||||
if( AwsIot_ParseThingName( pMessage->u.message.info.pTopicName,
|
||||
pMessage->u.message.info.topicNameLength,
|
||||
&( param.pThingName ),
|
||||
&( param.thingNameLength ) ) == false )
|
||||
{
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Lock the pending operations list for exclusive access. */
|
||||
IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
|
||||
/* Search for a matching pending operation. */
|
||||
pOperationLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowPendingOperations ),
|
||||
NULL,
|
||||
_shadowOperation_match,
|
||||
¶m );
|
||||
|
||||
/* Find and remove the first Shadow operation of the given type. */
|
||||
if( pOperationLink == NULL )
|
||||
{
|
||||
/* Operation is not pending. It may have already been processed. Return
|
||||
* without doing anything */
|
||||
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
|
||||
IotLogWarn( "Shadow %s callback received an unknown operation.",
|
||||
_pAwsIotShadowOperationNames[ type ] );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
else
|
||||
{
|
||||
pOperation = IotLink_Container( _shadowOperation_t, pOperationLink, link );
|
||||
|
||||
/* Remove a non-waitable operation from the pending operation list.
|
||||
* Waitable operations are removed by the Wait function. */
|
||||
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == 0 )
|
||||
{
|
||||
IotListDouble_Remove( &( pOperation->link ) );
|
||||
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that the Shadow operation type and status. */
|
||||
AwsIotShadow_Assert( pOperation->type == type );
|
||||
AwsIotShadow_Assert( pOperation->status == AWS_IOT_SHADOW_STATUS_PENDING );
|
||||
|
||||
IotLogDebug( "Received Shadow response on topic %.*s",
|
||||
pMessage->u.message.info.topicNameLength,
|
||||
pMessage->u.message.info.pTopicName );
|
||||
|
||||
/* Parse the status from the topic name. */
|
||||
status = AwsIot_ParseStatus( pMessage->u.message.info.pTopicName,
|
||||
pMessage->u.message.info.topicNameLength );
|
||||
|
||||
switch( status )
|
||||
{
|
||||
case AWS_IOT_ACCEPTED:
|
||||
IotLogInfo( "Shadow %s of %.*s was ACCEPTED.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pOperation->pSubscription->thingNameLength,
|
||||
pOperation->pSubscription->pThingName );
|
||||
|
||||
/* Process the retrieved document for a Shadow GET. Otherwise, set
|
||||
* status to success. */
|
||||
if( type == SHADOW_GET )
|
||||
{
|
||||
pOperation->status = _processAcceptedGet( pOperation,
|
||||
&( pMessage->u.message.info ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
pOperation->status = AWS_IOT_SHADOW_SUCCESS;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AWS_IOT_REJECTED:
|
||||
IotLogWarn( "Shadow %s of %.*s was REJECTED.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pOperation->pSubscription->thingNameLength,
|
||||
pOperation->pSubscription->pThingName );
|
||||
|
||||
pOperation->status = _AwsIotShadow_ParseErrorDocument( pMessage->u.message.info.pPayload,
|
||||
pMessage->u.message.info.payloadLength );
|
||||
break;
|
||||
|
||||
default:
|
||||
IotLogWarn( "Unknown status for %s of %.*s Shadow. Ignoring message.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pOperation->pSubscription->thingNameLength,
|
||||
pOperation->pSubscription->pThingName );
|
||||
|
||||
pOperation->status = AWS_IOT_SHADOW_BAD_RESPONSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy the flags from the Shadow operation. The notify function may delete the operation. */
|
||||
flags = pOperation->flags;
|
||||
|
||||
/* Notify of operation completion. */
|
||||
_notifyCompletion( pOperation );
|
||||
|
||||
/* For waitable operations, unlock the pending operation list mutex to allow
|
||||
* the Wait function to run. */
|
||||
if( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
|
||||
{
|
||||
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
}
|
||||
|
||||
/* This function has no return value and no cleanup, but uses the cleanup
|
||||
* label to exit on error. */
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _deleteCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( SHADOW_DELETE, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _getCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( SHADOW_GET, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static AwsIotShadowError_t _processAcceptedGet( _shadowOperation_t * pOperation,
|
||||
const IotMqttPublishInfo_t * pPublishInfo )
|
||||
{
|
||||
AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;
|
||||
|
||||
/* A non-waitable operation can re-use the pointers from the publish info,
|
||||
* since those are guaranteed to be in-scope throughout the user callback.
|
||||
* But a waitable operation must copy the data from the publish info because
|
||||
* AwsIotShadow_Wait may be called after the MQTT library frees the publish
|
||||
* info. */
|
||||
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == 0 )
|
||||
{
|
||||
pOperation->u.get.pDocument = pPublishInfo->pPayload;
|
||||
pOperation->u.get.documentLength = pPublishInfo->payloadLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Allocating new buffer for waitable Shadow GET." );
|
||||
|
||||
/* Parameter validation should not have allowed a NULL malloc function. */
|
||||
AwsIotShadow_Assert( pOperation->u.get.mallocDocument != NULL );
|
||||
|
||||
/* Allocate a buffer for the retrieved document. */
|
||||
pOperation->u.get.pDocument = pOperation->u.get.mallocDocument( pPublishInfo->payloadLength );
|
||||
|
||||
if( pOperation->u.get.pDocument == NULL )
|
||||
{
|
||||
IotLogError( "Failed to allocate buffer for retrieved Shadow document." );
|
||||
|
||||
status = AWS_IOT_SHADOW_NO_MEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Copy the retrieved document. */
|
||||
( void ) memcpy( ( void * ) pOperation->u.get.pDocument,
|
||||
pPublishInfo->pPayload,
|
||||
pPublishInfo->payloadLength );
|
||||
pOperation->u.get.documentLength = pPublishInfo->payloadLength;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _updateCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( SHADOW_UPDATE, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _notifyCompletion( _shadowOperation_t * pOperation )
|
||||
{
|
||||
AwsIotShadowCallbackParam_t callbackParam = { .callbackType = ( AwsIotShadowCallbackType_t ) 0 };
|
||||
_shadowSubscription_t * pSubscription = pOperation->pSubscription,
|
||||
* pRemovedSubscription = NULL;
|
||||
|
||||
/* If the operation is waiting, post to its wait semaphore and return. */
|
||||
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
|
||||
{
|
||||
IotSemaphore_Post( &( pOperation->notify.waitSemaphore ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Decrement the reference count. This also removes subscriptions if the
|
||||
* count reaches 0. */
|
||||
IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );
|
||||
_AwsIotShadow_DecrementReferences( pOperation,
|
||||
pSubscription->pTopicBuffer,
|
||||
&pRemovedSubscription );
|
||||
IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );
|
||||
|
||||
/* Set the subscription pointer used for the user callback based on whether
|
||||
* a subscription was removed from the list. */
|
||||
if( pRemovedSubscription != NULL )
|
||||
{
|
||||
pSubscription = pRemovedSubscription;
|
||||
}
|
||||
|
||||
AwsIotShadow_Assert( pSubscription != NULL );
|
||||
|
||||
/* Invoke the user callback if provided. */
|
||||
if( pOperation->notify.callback.function != NULL )
|
||||
{
|
||||
/* Set the common members of the callback parameter. */
|
||||
callbackParam.callbackType = ( AwsIotShadowCallbackType_t ) pOperation->type;
|
||||
callbackParam.mqttConnection = pOperation->mqttConnection;
|
||||
callbackParam.u.operation.result = pOperation->status;
|
||||
callbackParam.u.operation.reference = pOperation;
|
||||
callbackParam.pThingName = pSubscription->pThingName;
|
||||
callbackParam.thingNameLength = pSubscription->thingNameLength;
|
||||
|
||||
/* Set the members of the callback parameter for a received document. */
|
||||
if( pOperation->type == SHADOW_GET )
|
||||
{
|
||||
callbackParam.u.operation.get.pDocument = pOperation->u.get.pDocument;
|
||||
callbackParam.u.operation.get.documentLength = pOperation->u.get.documentLength;
|
||||
}
|
||||
|
||||
pOperation->notify.callback.function( pOperation->notify.callback.pCallbackContext,
|
||||
&callbackParam );
|
||||
}
|
||||
|
||||
/* Destroy a removed subscription. */
|
||||
if( pRemovedSubscription != NULL )
|
||||
{
|
||||
_AwsIotShadow_DestroySubscription( pRemovedSubscription );
|
||||
}
|
||||
|
||||
_AwsIotShadow_DestroyOperation( pOperation );
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static AwsIotShadowError_t _findSubscription( const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
_shadowOperation_t * pOperation,
|
||||
bool * pFreeTopicBuffer )
|
||||
{
|
||||
AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;
|
||||
_shadowSubscription_t * pSubscription = NULL;
|
||||
|
||||
/* Lookup table for Shadow operation callbacks. */
|
||||
const AwsIotMqttCallbackFunction_t shadowCallbacks[ SHADOW_OPERATION_COUNT ] =
|
||||
{
|
||||
_deleteCallback,
|
||||
_getCallback,
|
||||
_updateCallback
|
||||
};
|
||||
|
||||
/* Lock the subscriptions mutex for exclusive access. */
|
||||
IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );
|
||||
|
||||
/* Check for an existing subscription. This function will attempt to allocate
|
||||
* a new subscription if not found. */
|
||||
pSubscription = _AwsIotShadow_FindSubscription( pThingName,
|
||||
thingNameLength,
|
||||
true );
|
||||
|
||||
if( pSubscription == NULL )
|
||||
{
|
||||
status = AWS_IOT_SHADOW_NO_MEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Ensure that the subscription Thing Name matches. */
|
||||
AwsIotShadow_Assert( pSubscription != NULL );
|
||||
AwsIotShadow_Assert( pSubscription->thingNameLength == thingNameLength );
|
||||
AwsIotShadow_Assert( strncmp( pSubscription->pThingName,
|
||||
pThingName,
|
||||
thingNameLength ) == 0 );
|
||||
|
||||
/* Set the subscription object for the Shadow operation. */
|
||||
pOperation->pSubscription = pSubscription;
|
||||
|
||||
/* Assign the topic buffer to the subscription to use for unsubscribing if
|
||||
* the subscription has no topic buffer. */
|
||||
if( pSubscription->pTopicBuffer == NULL )
|
||||
{
|
||||
pSubscription->pTopicBuffer = pTopicBuffer;
|
||||
|
||||
/* Don't free the topic buffer if it was allocated to the subscription. */
|
||||
*pFreeTopicBuffer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pFreeTopicBuffer = true;
|
||||
}
|
||||
|
||||
/* Increment the reference count for this Shadow operation's
|
||||
* subscriptions. */
|
||||
status = _AwsIotShadow_IncrementReferences( pOperation,
|
||||
pTopicBuffer,
|
||||
operationTopicLength,
|
||||
shadowCallbacks[ pOperation->type ] );
|
||||
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
/* Failed to add subscriptions for a Shadow operation. The reference
|
||||
* count was not incremented. Check if this subscription should be
|
||||
* deleted. */
|
||||
_AwsIotShadow_RemoveSubscription( pSubscription, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the Shadow subscription list mutex. */
|
||||
IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotShadowError_t _AwsIotShadow_CreateOperation( _shadowOperation_t ** pNewOperation,
|
||||
_shadowOperationType_t type,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pCallbackInfo )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );
|
||||
_shadowOperation_t * pOperation = NULL;
|
||||
|
||||
IotLogDebug( "Creating operation record for Shadow %s.",
|
||||
_pAwsIotShadowOperationNames[ type ] );
|
||||
|
||||
/* Allocate memory for a new Shadow operation. */
|
||||
pOperation = AwsIotShadow_MallocOperation( sizeof( _shadowOperation_t ) );
|
||||
|
||||
if( pOperation == NULL )
|
||||
{
|
||||
IotLogError( "Failed to allocate memory for Shadow %s.",
|
||||
_pAwsIotShadowOperationNames[ type ] );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );
|
||||
}
|
||||
|
||||
/* Clear the operation data. */
|
||||
( void ) memset( pOperation, 0x00, sizeof( _shadowOperation_t ) );
|
||||
|
||||
/* Check if the waitable flag is set. If it is, create a semaphore to
|
||||
* wait on. */
|
||||
if( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
|
||||
{
|
||||
if( IotSemaphore_Create( &( pOperation->notify.waitSemaphore ), 0, 1 ) == false )
|
||||
{
|
||||
IotLogError( "Failed to create semaphore for waitable Shadow %s.",
|
||||
_pAwsIotShadowOperationNames[ type ] );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the waitable flag isn't set but a callback is, copy the callback
|
||||
* information. */
|
||||
if( pCallbackInfo != NULL )
|
||||
{
|
||||
pOperation->notify.callback = *pCallbackInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the remaining common members of the Shadow operation. */
|
||||
pOperation->type = type;
|
||||
pOperation->flags = flags;
|
||||
pOperation->status = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
if( pOperation != NULL )
|
||||
{
|
||||
AwsIotShadow_FreeOperation( pOperation );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the output parameter. */
|
||||
*pNewOperation = pOperation;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotShadow_DestroyOperation( void * pData )
|
||||
{
|
||||
_shadowOperation_t * pOperation = ( _shadowOperation_t * ) pData;
|
||||
|
||||
/* The Shadow operation pointer must not be NULL. */
|
||||
AwsIotShadow_Assert( pOperation != NULL );
|
||||
|
||||
IotLogDebug( "Destroying Shadow operation %s.",
|
||||
_pAwsIotShadowOperationNames[ pOperation->type ] );
|
||||
|
||||
/* Check if a wait semaphore was created for this operation. */
|
||||
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
|
||||
{
|
||||
/* Destroy the wait semaphore */
|
||||
IotSemaphore_Destroy( &( pOperation->notify.waitSemaphore ) );
|
||||
}
|
||||
|
||||
/* If this is a Shadow update, free any allocated client token. */
|
||||
if( ( pOperation->type == SHADOW_UPDATE ) &&
|
||||
( pOperation->u.update.pClientToken != NULL ) )
|
||||
{
|
||||
AwsIotShadow_Assert( pOperation->u.update.clientTokenLength > 0 );
|
||||
|
||||
AwsIotShadow_FreeString( ( void * ) ( pOperation->u.update.pClientToken ) );
|
||||
}
|
||||
|
||||
/* Free the memory used to hold operation data. */
|
||||
AwsIotShadow_FreeOperation( pOperation );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotShadowError_t _AwsIotShadow_GenerateShadowTopic( _shadowOperationType_t type,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
char ** pTopicBuffer,
|
||||
uint16_t * pOperationTopicLength )
|
||||
{
|
||||
AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;
|
||||
AwsIotTopicInfo_t topicInfo = { 0 };
|
||||
|
||||
/* Lookup table for Shadow operation strings. */
|
||||
const char * const pOperationString[ SHADOW_OPERATION_COUNT ] =
|
||||
{
|
||||
SHADOW_DELETE_OPERATION_STRING, /* Shadow delete operation. */
|
||||
SHADOW_GET_OPERATION_STRING, /* Shadow get operation. */
|
||||
SHADOW_UPDATE_OPERATION_STRING /* Shadow update operation. */
|
||||
};
|
||||
|
||||
/* Lookup table for Shadow operation string lengths. */
|
||||
const uint16_t pOperationStringLength[ SHADOW_OPERATION_COUNT ] =
|
||||
{
|
||||
SHADOW_DELETE_OPERATION_STRING_LENGTH, /* Shadow delete operation. */
|
||||
SHADOW_GET_OPERATION_STRING_LENGTH, /* Shadow get operation. */
|
||||
SHADOW_UPDATE_OPERATION_STRING_LENGTH /* Shadow update operation. */
|
||||
};
|
||||
|
||||
/* Only Shadow delete, get, and update operation types should be passed to this
|
||||
* function. */
|
||||
AwsIotShadow_Assert( ( type == SHADOW_DELETE ) ||
|
||||
( type == SHADOW_GET ) ||
|
||||
( type == SHADOW_UPDATE ) );
|
||||
|
||||
/* Set the members needed to generate an operation topic. */
|
||||
topicInfo.pThingName = pThingName;
|
||||
topicInfo.thingNameLength = thingNameLength;
|
||||
topicInfo.pOperationName = pOperationString[ type ];
|
||||
topicInfo.operationNameLength = pOperationStringLength[ type ];
|
||||
topicInfo.longestSuffixLength = SHADOW_LONGEST_SUFFIX_LENGTH;
|
||||
topicInfo.mallocString = AwsIotShadow_MallocString;
|
||||
|
||||
if( AwsIot_GenerateOperationTopic( &topicInfo,
|
||||
pTopicBuffer,
|
||||
pOperationTopicLength ) == false )
|
||||
{
|
||||
status = AWS_IOT_SHADOW_NO_MEMORY;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotShadowError_t _AwsIotShadow_ProcessOperation( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
_shadowOperation_t * pOperation,
|
||||
const AwsIotShadowDocumentInfo_t * pDocumentInfo )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );
|
||||
IotMqttError_t publishStatus = IOT_MQTT_STATUS_PENDING;
|
||||
char * pTopicBuffer = NULL;
|
||||
uint16_t operationTopicLength = 0;
|
||||
bool freeTopicBuffer = true;
|
||||
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
|
||||
|
||||
IotLogDebug( "Processing Shadow operation %s for Thing %.*s.",
|
||||
_pAwsIotShadowOperationNames[ pOperation->type ],
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
|
||||
/* Set the operation's MQTT connection. */
|
||||
pOperation->mqttConnection = mqttConnection;
|
||||
|
||||
/* Generate the operation topic buffer. */
|
||||
status = _AwsIotShadow_GenerateShadowTopic( pOperation->type,
|
||||
pThingName,
|
||||
thingNameLength,
|
||||
&pTopicBuffer,
|
||||
&operationTopicLength );
|
||||
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
IotLogError( "No memory for Shadow operation topic buffer." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );
|
||||
}
|
||||
|
||||
/* Get a subscription object for this Shadow operation. */
|
||||
status = _findSubscription( pThingName,
|
||||
thingNameLength,
|
||||
pTopicBuffer,
|
||||
operationTopicLength,
|
||||
pOperation,
|
||||
&freeTopicBuffer );
|
||||
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
/* No subscription was found and no subscription could be allocated. */
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Set the operation topic name. */
|
||||
publishInfo.pTopicName = pTopicBuffer;
|
||||
publishInfo.topicNameLength = operationTopicLength;
|
||||
|
||||
IotLogDebug( "Shadow %s message will be published to topic %.*s",
|
||||
_pAwsIotShadowOperationNames[ pOperation->type ],
|
||||
publishInfo.topicNameLength,
|
||||
publishInfo.pTopicName );
|
||||
|
||||
/* Set the document info if this operation is not a Shadow DELETE. */
|
||||
if( pOperation->type != SHADOW_DELETE )
|
||||
{
|
||||
publishInfo.qos = pDocumentInfo->qos;
|
||||
publishInfo.retryLimit = pDocumentInfo->retryLimit;
|
||||
publishInfo.retryMs = pDocumentInfo->retryMs;
|
||||
|
||||
IotLogDebug( "Shadow %s message will be published at QoS %d with "
|
||||
"retryLimit %d and retryMs %llu.",
|
||||
_pAwsIotShadowOperationNames[ pOperation->type ],
|
||||
publishInfo.qos,
|
||||
publishInfo.retryLimit,
|
||||
publishInfo.retryMs );
|
||||
}
|
||||
|
||||
/* Set the PUBLISH payload to the update document for Shadow UPDATE. */
|
||||
if( pOperation->type == SHADOW_UPDATE )
|
||||
{
|
||||
publishInfo.pPayload = pDocumentInfo->u.update.pUpdateDocument;
|
||||
publishInfo.payloadLength = pDocumentInfo->u.update.updateDocumentLength;
|
||||
}
|
||||
|
||||
/* Set the PUBLISH payload to an empty string for Shadow DELETE and GET,
|
||||
* per the Shadow spec. */
|
||||
else
|
||||
{
|
||||
publishInfo.pPayload = "";
|
||||
publishInfo.payloadLength = 0;
|
||||
}
|
||||
|
||||
/* Add Shadow operation to the pending operations list. */
|
||||
IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
IotListDouble_InsertHead( &( _AwsIotShadowPendingOperations ),
|
||||
&( pOperation->link ) );
|
||||
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
|
||||
/* Publish to the Shadow topic name. */
|
||||
publishStatus = IotMqtt_PublishSync( pOperation->mqttConnection,
|
||||
&publishInfo,
|
||||
0,
|
||||
_AwsIotShadowMqttTimeoutMs );
|
||||
|
||||
/* Check for errors from the MQTT publish. */
|
||||
if( publishStatus != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IotLogError( "Failed to publish MQTT message to %s %.*s Shadow, error %s.",
|
||||
_pAwsIotShadowOperationNames[ pOperation->type ],
|
||||
thingNameLength,
|
||||
pThingName,
|
||||
IotMqtt_strerror( publishStatus ) );
|
||||
|
||||
/* Convert the MQTT "NO MEMORY" error to a Shadow "NO MEMORY" error. */
|
||||
status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( publishStatus );
|
||||
|
||||
/* If the "keep subscriptions" flag is not set, decrement the reference
|
||||
* count. */
|
||||
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
|
||||
{
|
||||
IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );
|
||||
_AwsIotShadow_DecrementReferences( pOperation,
|
||||
pTopicBuffer,
|
||||
NULL );
|
||||
IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );
|
||||
}
|
||||
|
||||
/* Remove Shadow operation from the pending operations list. */
|
||||
IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
IotListDouble_Remove( &( pOperation->link ) );
|
||||
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Shadow %s PUBLISH message successfully sent.",
|
||||
_pAwsIotShadowOperationNames[ pOperation->type ] );
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
/* Free the topic buffer used by this function if it was not assigned to a
|
||||
* subscription. */
|
||||
if( ( freeTopicBuffer == true ) && ( pTopicBuffer != NULL ) )
|
||||
{
|
||||
AwsIotShadow_FreeString( pTopicBuffer );
|
||||
}
|
||||
|
||||
/* Destroy the Shadow operation on failure. */
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
_AwsIotShadow_DestroyOperation( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert successful return code to "status pending", as the Shadow
|
||||
* library is now waiting for a response from the service. */
|
||||
status = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* AWS IoT Shadow V2.1.0
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file aws_iot_shadow_parser.c
|
||||
* @brief Implements JSON parsing functions of the Shadow library.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Shadow internal include. */
|
||||
#include "private/aws_iot_shadow_internal.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* AWS Parser include. */
|
||||
#include "aws_iot_doc_parser.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief The JSON key for the error code in a Shadow error document.
|
||||
*/
|
||||
#define ERROR_DOCUMENT_CODE_KEY "code"
|
||||
|
||||
/**
|
||||
* @brief The length of #ERROR_DOCUMENT_CODE_KEY.
|
||||
*/
|
||||
#define ERROR_DOCUMENT_CODE_KEY_LENGTH ( sizeof( ERROR_DOCUMENT_CODE_KEY ) - 1 )
|
||||
|
||||
/**
|
||||
* @brief The JSON key for the error message in a Shadow error document.
|
||||
*/
|
||||
#define ERROR_DOCUMENT_MESSAGE_KEY "message"
|
||||
|
||||
/**
|
||||
* @brief The length of #ERROR_DOCUMENT_MESSAGE_KEY.
|
||||
*/
|
||||
#define ERROR_DOCUMENT_MESSAGE_KEY_LENGTH ( sizeof( ERROR_DOCUMENT_MESSAGE_KEY ) - 1 )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Converts a `unsigned long` to an `AwsIotShadowError_t`.
|
||||
*
|
||||
* @param[in] code A value between 400 and 500 to convert.
|
||||
*
|
||||
* @return A corresponding #AwsIotShadowError_t; #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* if `code` is unknown.
|
||||
*/
|
||||
static AwsIotShadowError_t _codeToShadowStatus( uint32_t code );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static AwsIotShadowError_t _codeToShadowStatus( uint32_t code )
|
||||
{
|
||||
AwsIotShadowError_t errorCode = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
|
||||
/* Convert the Shadow response code to an AwsIotShadowError_t. */
|
||||
switch( code )
|
||||
{
|
||||
case 400UL:
|
||||
errorCode = AWS_IOT_SHADOW_BAD_REQUEST;
|
||||
break;
|
||||
|
||||
case 401UL:
|
||||
errorCode = AWS_IOT_SHADOW_UNAUTHORIZED;
|
||||
break;
|
||||
|
||||
case 403UL:
|
||||
errorCode = AWS_IOT_SHADOW_FORBIDDEN;
|
||||
break;
|
||||
|
||||
case 404UL:
|
||||
errorCode = AWS_IOT_SHADOW_NOT_FOUND;
|
||||
break;
|
||||
|
||||
case 409UL:
|
||||
errorCode = AWS_IOT_SHADOW_CONFLICT;
|
||||
break;
|
||||
|
||||
case 413UL:
|
||||
errorCode = AWS_IOT_SHADOW_TOO_LARGE;
|
||||
break;
|
||||
|
||||
case 415UL:
|
||||
errorCode = AWS_IOT_SHADOW_UNSUPPORTED;
|
||||
break;
|
||||
|
||||
case 429UL:
|
||||
errorCode = AWS_IOT_SHADOW_TOO_MANY_REQUESTS;
|
||||
break;
|
||||
|
||||
case 500UL:
|
||||
errorCode = AWS_IOT_SHADOW_SERVER_ERROR;
|
||||
break;
|
||||
|
||||
default:
|
||||
errorCode = AWS_IOT_SHADOW_BAD_RESPONSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotShadowError_t _AwsIotShadow_ParseErrorDocument( const char * pErrorDocument,
|
||||
size_t errorDocumentLength )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );
|
||||
const char * pCode = NULL, * pMessage = NULL;
|
||||
size_t codeLength = 0, messageLength = 0;
|
||||
uint32_t code = 0;
|
||||
|
||||
/* Parse the code from the error document. */
|
||||
if( AwsIotDocParser_FindValue( pErrorDocument,
|
||||
errorDocumentLength,
|
||||
ERROR_DOCUMENT_CODE_KEY,
|
||||
ERROR_DOCUMENT_CODE_KEY_LENGTH,
|
||||
&pCode,
|
||||
&codeLength ) == false )
|
||||
{
|
||||
/* Error parsing JSON document, or no "code" key was found. */
|
||||
IotLogWarn( "Failed to parse code from error document.\n%.*s",
|
||||
errorDocumentLength,
|
||||
pErrorDocument );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_RESPONSE );
|
||||
}
|
||||
|
||||
/* Code must be in error document. */
|
||||
AwsIotShadow_Assert( ( pCode > pErrorDocument ) &&
|
||||
( pCode + codeLength < pErrorDocument + errorDocumentLength ) );
|
||||
|
||||
/* Convert the code to an unsigned integer value. */
|
||||
code = ( uint32_t ) strtoul( pCode, NULL, 10 );
|
||||
|
||||
/* Parse the error message and print it. An error document must always contain
|
||||
* a message. */
|
||||
if( AwsIotDocParser_FindValue( pErrorDocument,
|
||||
errorDocumentLength,
|
||||
ERROR_DOCUMENT_MESSAGE_KEY,
|
||||
ERROR_DOCUMENT_MESSAGE_KEY_LENGTH,
|
||||
&pMessage,
|
||||
&messageLength ) == true )
|
||||
{
|
||||
IotLogWarn( "Code %lu: %.*s.",
|
||||
code,
|
||||
messageLength,
|
||||
pMessage );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "Code %lu; failed to parse message from error document.\n%.*s",
|
||||
code,
|
||||
errorDocumentLength,
|
||||
pErrorDocument );
|
||||
|
||||
/* An error document must contain a message; if it does not, then it is invalid. */
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_RESPONSE );
|
||||
}
|
||||
|
||||
/* Convert a successfully parsed JSON code to a Shadow status. */
|
||||
status = _codeToShadowStatus( code );
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* AWS IoT Shadow V2.1.0
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file aws_iot_shadow_static_memory.c
|
||||
* @brief Implementation of Shadow static memory functions.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* This file should only be compiled if dynamic memory allocation is forbidden. */
|
||||
#if IOT_STATIC_MEMORY_ONLY == 1
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Static memory include. */
|
||||
#include "iot_static_memory.h"
|
||||
|
||||
/* Shadow internal include. */
|
||||
#include "private/aws_iot_shadow_internal.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @cond DOXYGEN_IGNORE
|
||||
* Doxygen should ignore this section.
|
||||
*
|
||||
* Provide default values for undefined configuration constants.
|
||||
*/
|
||||
#ifndef AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS
|
||||
#define AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS ( 10 )
|
||||
#endif
|
||||
#ifndef AWS_IOT_SHADOW_SUBSCRIPTIONS
|
||||
#define AWS_IOT_SHADOW_SUBSCRIPTIONS ( 2 )
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
/* Validate static memory configuration settings. */
|
||||
#if AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS <= 0
|
||||
#error "AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS cannot be 0 or negative."
|
||||
#endif
|
||||
#if AWS_IOT_SHADOW_SUBSCRIPTIONS <= 0
|
||||
#error "AWS_IOT_SHADOW_SUBSCRIPTIONS cannot be 0 or negative."
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The size of a static memory Shadow subscription.
|
||||
*
|
||||
* Since the pThingName member of #_shadowSubscription_t is variable-length,
|
||||
* the constant `AWS_IOT_MAX_THING_NAME_LENGTH` is used for the length of
|
||||
* #_shadowSubscription_t.pThingName.
|
||||
*/
|
||||
#define SHADOW_SUBSCRIPTION_SIZE ( sizeof( _shadowSubscription_t ) + AWS_IOT_MAX_THING_NAME_LENGTH )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Static memory buffers and flags, allocated and zeroed at compile-time.
|
||||
*/
|
||||
static uint32_t _pInUseShadowOperations[ AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS ] = { 0U }; /**< @brief Shadow operation in-use flags. */
|
||||
static _shadowOperation_t _pShadowOperations[ AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS ] = { { .link = { 0 } } }; /**< @brief Shadow operations. */
|
||||
|
||||
static uint32_t _pInUseShadowSubscriptions[ AWS_IOT_SHADOW_SUBSCRIPTIONS ] = { 0U }; /**< @brief Shadow subscription in-use flags. */
|
||||
static char _pShadowSubscriptions[ AWS_IOT_SHADOW_SUBSCRIPTIONS ][ SHADOW_SUBSCRIPTION_SIZE ] = { { 0 } }; /**< @brief Shadow subscriptions. */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void * AwsIotShadow_MallocOperation( size_t size )
|
||||
{
|
||||
int32_t freeIndex = -1;
|
||||
void * pNewOperation = NULL;
|
||||
|
||||
/* Check size argument. */
|
||||
if( size == sizeof( _shadowOperation_t ) )
|
||||
{
|
||||
/* Find a free Shadow operation. */
|
||||
freeIndex = IotStaticMemory_FindFree( _pInUseShadowOperations,
|
||||
AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS );
|
||||
|
||||
if( freeIndex != -1 )
|
||||
{
|
||||
pNewOperation = &( _pShadowOperations[ freeIndex ] );
|
||||
}
|
||||
}
|
||||
|
||||
return pNewOperation;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void AwsIotShadow_FreeOperation( void * ptr )
|
||||
{
|
||||
/* Return the in-use Shadow operation. */
|
||||
IotStaticMemory_ReturnInUse( ptr,
|
||||
_pShadowOperations,
|
||||
_pInUseShadowOperations,
|
||||
AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS,
|
||||
sizeof( _shadowOperation_t ) );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void * AwsIotShadow_MallocSubscription( size_t size )
|
||||
{
|
||||
int32_t freeIndex = -1;
|
||||
void * pNewSubscription = NULL;
|
||||
|
||||
if( size <= SHADOW_SUBSCRIPTION_SIZE )
|
||||
{
|
||||
/* Get the index of a free Shadow subscription. */
|
||||
freeIndex = IotStaticMemory_FindFree( _pInUseShadowSubscriptions,
|
||||
AWS_IOT_SHADOW_SUBSCRIPTIONS );
|
||||
|
||||
if( freeIndex != -1 )
|
||||
{
|
||||
pNewSubscription = &( _pShadowSubscriptions[ freeIndex ][ 0 ] );
|
||||
}
|
||||
}
|
||||
|
||||
return pNewSubscription;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void AwsIotShadow_FreeSubscription( void * ptr )
|
||||
{
|
||||
/* Return the in-use Shadow subscription. */
|
||||
IotStaticMemory_ReturnInUse( ptr,
|
||||
_pShadowSubscriptions,
|
||||
_pInUseShadowSubscriptions,
|
||||
AWS_IOT_SHADOW_SUBSCRIPTIONS,
|
||||
SHADOW_SUBSCRIPTION_SIZE );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* AWS IoT Shadow V2.1.0
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file aws_iot_shadow_subscription.c
|
||||
* @brief Implements functions for interacting with the Shadow library's
|
||||
* subscription list.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* Shadow internal include. */
|
||||
#include "private/aws_iot_shadow_internal.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* Platform layer includes. */
|
||||
#include "platform/iot_threads.h"
|
||||
|
||||
/* MQTT include. */
|
||||
#include "iot_mqtt.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Match two #_shadowSubscription_t by Thing Name.
|
||||
*
|
||||
* @param[in] pSubscriptionLink Pointer to the link member of a #_shadowSubscription_t
|
||||
* containing the Thing Name to check.
|
||||
* @param[in] pMatch Pointer to an `AwsIotThingName_t`.
|
||||
*
|
||||
* @return `true` if the Thing Names match; `false` otherwise.
|
||||
*/
|
||||
static bool _shadowSubscription_match( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief List of active Shadow subscriptions objects.
|
||||
*/
|
||||
IotListDouble_t _AwsIotShadowSubscriptions = { 0 };
|
||||
|
||||
/**
|
||||
* @brief Protects #_AwsIotShadowSubscriptions from concurrent access.
|
||||
*/
|
||||
IotMutex_t _AwsIotShadowSubscriptionsMutex;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _shadowSubscription_match( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch )
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
/* Because this function is called from a container function, the given link
|
||||
* must never be NULL. */
|
||||
AwsIotShadow_Assert( pSubscriptionLink != NULL );
|
||||
|
||||
const _shadowSubscription_t * pSubscription = IotLink_Container( _shadowSubscription_t,
|
||||
pSubscriptionLink,
|
||||
link );
|
||||
const AwsIotThingName_t * pThingName = ( AwsIotThingName_t * ) pMatch;
|
||||
|
||||
if( pThingName->thingNameLength == pSubscription->thingNameLength )
|
||||
{
|
||||
/* Check for matching Thing Names. */
|
||||
match = ( strncmp( pThingName->pThingName,
|
||||
pSubscription->pThingName,
|
||||
pThingName->thingNameLength ) == 0 );
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
_shadowSubscription_t * _AwsIotShadow_FindSubscription( const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
bool createIfNotFound )
|
||||
{
|
||||
_shadowSubscription_t * pSubscription = NULL;
|
||||
IotLink_t * pSubscriptionLink = NULL;
|
||||
AwsIotThingName_t thingName = { 0 };
|
||||
|
||||
thingName.pThingName = pThingName;
|
||||
thingName.thingNameLength = thingNameLength;
|
||||
|
||||
/* Search the list for an existing subscription for Thing Name. */
|
||||
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowSubscriptions ),
|
||||
NULL,
|
||||
_shadowSubscription_match,
|
||||
&thingName );
|
||||
|
||||
/* Check if a subscription was found. */
|
||||
if( pSubscriptionLink == NULL )
|
||||
{
|
||||
if( createIfNotFound == true )
|
||||
{
|
||||
/* No subscription found. Allocate a new subscription. */
|
||||
pSubscription = AwsIotShadow_MallocSubscription( sizeof( _shadowSubscription_t ) + thingNameLength );
|
||||
|
||||
if( pSubscription != NULL )
|
||||
{
|
||||
/* Clear the new subscription. */
|
||||
( void ) memset( pSubscription, 0x00, sizeof( _shadowSubscription_t ) + thingNameLength );
|
||||
|
||||
/* Set the Thing Name length and copy the Thing Name into the new subscription. */
|
||||
pSubscription->thingNameLength = thingNameLength;
|
||||
( void ) memcpy( pSubscription->pThingName, pThingName, thingNameLength );
|
||||
|
||||
/* Add the new subscription to the subscription list. */
|
||||
IotListDouble_InsertHead( &( _AwsIotShadowSubscriptions ),
|
||||
&( pSubscription->link ) );
|
||||
|
||||
IotLogDebug( "Created new Shadow subscriptions object for %.*s.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogError( "Failed to allocate memory for %.*s Shadow subscriptions.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Found existing Shadow subscriptions object for %.*s.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
|
||||
pSubscription = IotLink_Container( _shadowSubscription_t, pSubscriptionLink, link );
|
||||
}
|
||||
|
||||
return pSubscription;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotShadow_RemoveSubscription( _shadowSubscription_t * pSubscription,
|
||||
_shadowSubscription_t ** pRemovedSubscription )
|
||||
{
|
||||
int32_t i = 0;
|
||||
bool removeSubscription = true;
|
||||
|
||||
IotLogDebug( "Checking if subscription object for %.*s can be removed.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
/* Check for active operations. If any Shadow operation's subscription
|
||||
* reference count is not 0, then the subscription cannot be removed. */
|
||||
for( i = 0; i < SHADOW_OPERATION_COUNT; i++ )
|
||||
{
|
||||
if( pSubscription->references[ i ] > 0 )
|
||||
{
|
||||
IotLogDebug( "Reference count %ld for %.*s subscription object. "
|
||||
"Subscription cannot be removed yet.",
|
||||
( long int ) pSubscription->references[ i ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
removeSubscription = false;
|
||||
}
|
||||
else if( pSubscription->references[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
IotLogDebug( "Subscription object for %.*s has persistent subscriptions. "
|
||||
"Subscription will not be removed.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
removeSubscription = false;
|
||||
}
|
||||
|
||||
if( removeSubscription == false )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for active subscriptions. If any Shadow callbacks are active, then the
|
||||
* subscription cannot be removed. */
|
||||
if( removeSubscription == true )
|
||||
{
|
||||
for( i = 0; i < SHADOW_CALLBACK_COUNT; i++ )
|
||||
{
|
||||
if( pSubscription->callbacks[ i ].function != NULL )
|
||||
{
|
||||
IotLogDebug( "Found active Shadow %s callback for %.*s subscription object. "
|
||||
"Subscription cannot be removed yet.",
|
||||
_pAwsIotShadowCallbackNames[ i ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
removeSubscription = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the subscription if unused. */
|
||||
if( removeSubscription == true )
|
||||
{
|
||||
/* No Shadow operation subscription references or active Shadow callbacks.
|
||||
* Remove the subscription object. */
|
||||
IotListDouble_Remove( &( pSubscription->link ) );
|
||||
|
||||
IotLogDebug( "Removed subscription object for %.*s.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
/* If the caller requested the removed subscription, set the output parameter.
|
||||
* Otherwise, free the memory used by the subscription. */
|
||||
if( pRemovedSubscription != NULL )
|
||||
{
|
||||
*pRemovedSubscription = pSubscription;
|
||||
}
|
||||
else
|
||||
{
|
||||
_AwsIotShadow_DestroySubscription( pSubscription );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotShadow_DestroySubscription( void * pData )
|
||||
{
|
||||
_shadowSubscription_t * pSubscription = ( _shadowSubscription_t * ) pData;
|
||||
|
||||
/* Free the topic buffer if allocated. */
|
||||
if( pSubscription->pTopicBuffer != NULL )
|
||||
{
|
||||
AwsIotShadow_FreeString( pSubscription->pTopicBuffer );
|
||||
}
|
||||
|
||||
/* Free memory used by subscription. */
|
||||
AwsIotShadow_FreeSubscription( pSubscription );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotShadowError_t _AwsIotShadow_IncrementReferences( _shadowOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
AwsIotMqttCallbackFunction_t callback )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );
|
||||
const _shadowOperationType_t type = pOperation->type;
|
||||
_shadowSubscription_t * pSubscription = pOperation->pSubscription;
|
||||
IotMqttError_t subscriptionStatus = IOT_MQTT_STATUS_PENDING;
|
||||
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
|
||||
|
||||
/* Do nothing if this operation has persistent subscriptions. */
|
||||
if( pSubscription->references[ type ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
IotLogDebug( "Shadow %s for %.*s has persistent subscriptions. Reference "
|
||||
"count will not be incremented.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* When persistent subscriptions are not present, the reference count must
|
||||
* not be negative. */
|
||||
AwsIotShadow_Assert( pSubscription->references[ type ] >= 0 );
|
||||
|
||||
/* Check if there are any existing references for this operation. */
|
||||
if( pSubscription->references[ type ] == 0 )
|
||||
{
|
||||
/* Set the parameters needed to add subscriptions. */
|
||||
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
|
||||
subscriptionInfo.callbackFunction = callback;
|
||||
subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;
|
||||
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
|
||||
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
|
||||
|
||||
subscriptionStatus = AwsIot_ModifySubscriptions( IotMqtt_SubscribeSync,
|
||||
&subscriptionInfo );
|
||||
|
||||
/* Convert MQTT return code to Shadow return code. */
|
||||
status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( subscriptionStatus );
|
||||
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the number of subscription references for this operation when
|
||||
* the keep subscriptions flag is not set. */
|
||||
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
|
||||
{
|
||||
( pSubscription->references[ type ] )++;
|
||||
|
||||
IotLogDebug( "Shadow %s subscriptions for %.*s now has count %d.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName,
|
||||
pSubscription->references[ type ] );
|
||||
}
|
||||
/* Otherwise, set the persistent subscriptions flag. */
|
||||
else
|
||||
{
|
||||
pSubscription->references[ type ] = AWS_IOT_PERSISTENT_SUBSCRIPTION;
|
||||
|
||||
IotLogDebug( "Set persistent subscriptions flag for Shadow %s of %.*s.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotShadow_DecrementReferences( _shadowOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
_shadowSubscription_t ** pRemovedSubscription )
|
||||
{
|
||||
const _shadowOperationType_t type = pOperation->type;
|
||||
_shadowSubscription_t * pSubscription = pOperation->pSubscription;
|
||||
uint16_t operationTopicLength = 0;
|
||||
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
|
||||
|
||||
/* Do nothing if this Shadow operation has persistent subscriptions. */
|
||||
if( pSubscription->references[ type ] != AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
/* Decrement the number of subscription references for this operation.
|
||||
* Ensure that it's positive. */
|
||||
( pSubscription->references[ type ] )--;
|
||||
AwsIotShadow_Assert( pSubscription->references[ type ] >= 0 );
|
||||
|
||||
/* Check if the number of references has reached 0. */
|
||||
if( pSubscription->references[ type ] == 0 )
|
||||
{
|
||||
IotLogDebug( "Reference count for %.*s %s is 0. Unsubscribing.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName,
|
||||
_pAwsIotShadowOperationNames[ type ] );
|
||||
|
||||
/* Subscription must have a topic buffer. */
|
||||
AwsIotShadow_Assert( pSubscription->pTopicBuffer != NULL );
|
||||
|
||||
/* Generate the prefix of the Shadow topic. This function will not
|
||||
* fail when given a buffer. */
|
||||
( void ) _AwsIotShadow_GenerateShadowTopic( ( _shadowOperationType_t ) type,
|
||||
pSubscription->pThingName,
|
||||
pSubscription->thingNameLength,
|
||||
&( pSubscription->pTopicBuffer ),
|
||||
&operationTopicLength );
|
||||
|
||||
/* Set the parameters needed to remove subscriptions. */
|
||||
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
|
||||
subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;
|
||||
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
|
||||
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
|
||||
|
||||
( void ) AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
|
||||
&subscriptionInfo );
|
||||
}
|
||||
|
||||
/* Check if this subscription should be deleted. */
|
||||
_AwsIotShadow_RemoveSubscription( pSubscription,
|
||||
pRemovedSubscription );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Shadow %s for %.*s has persistent subscriptions. Reference "
|
||||
"count will not be decremented.",
|
||||
_pAwsIotShadowOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotShadowError_t AwsIotShadow_RemovePersistentSubscriptions( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags )
|
||||
{
|
||||
int32_t i = 0;
|
||||
uint16_t operationTopicLength = 0;
|
||||
AwsIotShadowError_t status = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
IotMqttError_t unsubscribeStatus = IOT_MQTT_STATUS_PENDING;
|
||||
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
|
||||
_shadowSubscription_t * pSubscription = NULL;
|
||||
IotLink_t * pSubscriptionLink = NULL;
|
||||
AwsIotThingName_t thingName = { 0 };
|
||||
|
||||
thingName.pThingName = pThingName;
|
||||
thingName.thingNameLength = thingNameLength;
|
||||
|
||||
IotLogInfo( "Removing persistent subscriptions for %.*s.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
|
||||
IotMutex_Lock( &( _AwsIotShadowSubscriptionsMutex ) );
|
||||
|
||||
/* Search the list for an existing subscription for Thing Name. */
|
||||
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowSubscriptions ),
|
||||
NULL,
|
||||
_shadowSubscription_match,
|
||||
&thingName );
|
||||
|
||||
/* Unsubscribe from operation subscriptions if found. */
|
||||
if( pSubscriptionLink != NULL )
|
||||
{
|
||||
IotLogDebug( "Found subscription object for %.*s. Checking for persistent "
|
||||
"subscriptions to remove.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
|
||||
pSubscription = IotLink_Container( _shadowSubscription_t, pSubscriptionLink, link );
|
||||
|
||||
for( i = 0; i < SHADOW_OPERATION_COUNT; i++ )
|
||||
{
|
||||
if( ( flags & ( 0x1UL << i ) ) != 0 )
|
||||
{
|
||||
IotLogDebug( "Removing %.*s %s persistent subscriptions.",
|
||||
thingNameLength,
|
||||
pThingName,
|
||||
_pAwsIotShadowOperationNames[ i ] );
|
||||
|
||||
/* Subscription must have a topic buffer. */
|
||||
AwsIotShadow_Assert( pSubscription->pTopicBuffer != NULL );
|
||||
|
||||
if( pSubscription->references[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
/* Generate the prefix of the Shadow topic. This function will not
|
||||
* fail when given a buffer. */
|
||||
( void ) _AwsIotShadow_GenerateShadowTopic( ( _shadowOperationType_t ) i,
|
||||
pThingName,
|
||||
thingNameLength,
|
||||
&( pSubscription->pTopicBuffer ),
|
||||
&operationTopicLength );
|
||||
|
||||
/* Set the parameters needed to remove subscriptions. */
|
||||
subscriptionInfo.mqttConnection = mqttConnection;
|
||||
subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;
|
||||
subscriptionInfo.pTopicFilterBase = pSubscription->pTopicBuffer;
|
||||
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
|
||||
|
||||
unsubscribeStatus = AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
|
||||
&subscriptionInfo );
|
||||
|
||||
/* Convert MQTT return code to Shadow return code. */
|
||||
status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( unsubscribeStatus );
|
||||
|
||||
if( status != AWS_IOT_SHADOW_SUCCESS )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the persistent subscriptions flag and check if the
|
||||
* subscription can be removed. */
|
||||
pSubscription->references[ i ] = 0;
|
||||
_AwsIotShadow_RemoveSubscription( pSubscription, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "%.*s %s does not have persistent subscriptions.",
|
||||
thingNameLength,
|
||||
pThingName,
|
||||
_pAwsIotShadowOperationNames[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "No subscription object found for %.*s",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( _AwsIotShadowSubscriptionsMutex ) );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,565 @@
|
|||
/*
|
||||
* AWS IoT Shadow V2.1.0
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file aws_iot_shadow_internal.h
|
||||
* @brief Internal header of Shadow library. This header should not be included in
|
||||
* typical application code.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_SHADOW_INTERNAL_H_
|
||||
#define AWS_IOT_SHADOW_INTERNAL_H_
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Linear containers (lists and queues) include. */
|
||||
#include "iot_linear_containers.h"
|
||||
|
||||
/* Platform layer types include. */
|
||||
#include "types/iot_platform_types.h"
|
||||
|
||||
/* Shadow include. */
|
||||
#include "aws_iot_shadow.h"
|
||||
|
||||
/* AWS IoT include. */
|
||||
#include "aws_iot.h"
|
||||
|
||||
/**
|
||||
* @def AwsIotShadow_Assert( expression )
|
||||
* @brief Assertion macro for the Shadow library.
|
||||
*
|
||||
* Set @ref AWS_IOT_SHADOW_ENABLE_ASSERTS to `1` to enable assertions in the Shadow
|
||||
* library.
|
||||
*
|
||||
* @param[in] expression Expression to be evaluated.
|
||||
*/
|
||||
#if AWS_IOT_SHADOW_ENABLE_ASSERTS == 1
|
||||
#ifndef AwsIotShadow_Assert
|
||||
#ifdef Iot_DefaultAssert
|
||||
#define AwsIotShadow_Assert( expression ) Iot_DefaultAssert( expression )
|
||||
#else
|
||||
#error "Asserts are enabled for Shadow, but AwsIotShadow_Assert is not defined"
|
||||
#endif
|
||||
#endif
|
||||
#else /* if AWS_IOT_SHADOW_ENABLE_ASSERTS == 1 */
|
||||
#define AwsIotShadow_Assert( expression )
|
||||
#endif /* if AWS_IOT_SHADOW_ENABLE_ASSERTS == 1 */
|
||||
|
||||
/* Configure logs for Shadow functions. */
|
||||
#ifdef AWS_IOT_LOG_LEVEL_SHADOW
|
||||
#define LIBRARY_LOG_LEVEL AWS_IOT_LOG_LEVEL_SHADOW
|
||||
#else
|
||||
#ifdef IOT_LOG_LEVEL_GLOBAL
|
||||
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
|
||||
#else
|
||||
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define LIBRARY_LOG_NAME ( "Shadow" )
|
||||
#include "iot_logging_setup.h"
|
||||
|
||||
/*
|
||||
* Provide default values for undefined memory allocation functions based on
|
||||
* the usage of dynamic memory allocation.
|
||||
*/
|
||||
#if IOT_STATIC_MEMORY_ONLY == 1
|
||||
#include "iot_static_memory.h"
|
||||
|
||||
/**
|
||||
* @brief Allocate a #_shadowOperation_t. This function should have the same
|
||||
* signature as [malloc]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
|
||||
*/
|
||||
void * AwsIotShadow_MallocOperation( size_t size );
|
||||
|
||||
/**
|
||||
* @brief Free a #_shadowOperation_t. This function should have the same
|
||||
* signature as [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
void AwsIotShadow_FreeOperation( void * ptr );
|
||||
|
||||
/**
|
||||
* @brief Allocate a buffer for a short string, used for topic names or client
|
||||
* tokens. This function should have the same signature as [malloc]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
|
||||
*/
|
||||
#define AwsIotShadow_MallocString Iot_MallocMessageBuffer
|
||||
|
||||
/**
|
||||
* @brief Free a string. This function should have the same signature as
|
||||
* [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
#define AwsIotShadow_FreeString Iot_FreeMessageBuffer
|
||||
|
||||
/**
|
||||
* @brief Allocate a #_shadowSubscription_t. This function should have the
|
||||
* same signature as [malloc]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
|
||||
*/
|
||||
void * AwsIotShadow_MallocSubscription( size_t size );
|
||||
|
||||
/**
|
||||
* @brief Free a #_shadowSubscription_t. This function should have the same
|
||||
* signature as [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
void AwsIotShadow_FreeSubscription( void * ptr );
|
||||
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
|
||||
#ifndef AwsIotShadow_MallocOperation
|
||||
#ifdef Iot_DefaultMalloc
|
||||
#define AwsIotShadow_MallocOperation Iot_DefaultMalloc
|
||||
#else
|
||||
#error "No malloc function defined for AwsIotShadow_MallocOperation"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotShadow_FreeOperation
|
||||
#ifdef Iot_DefaultFree
|
||||
#define AwsIotShadow_FreeOperation Iot_DefaultFree
|
||||
#else
|
||||
#error "No free function defined for AwsIotShadow_FreeOperation"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotShadow_MallocString
|
||||
#ifdef Iot_DefaultMalloc
|
||||
#define AwsIotShadow_MallocString Iot_DefaultMalloc
|
||||
#else
|
||||
#error "No malloc function defined for AwsIotShadow_MallocString"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotShadow_FreeString
|
||||
#ifdef Iot_DefaultFree
|
||||
#define AwsIotShadow_FreeString Iot_DefaultFree
|
||||
#else
|
||||
#error "No free function defined for AwsIotShadow_FreeString"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotShadow_MallocSubscription
|
||||
#ifdef Iot_DefaultMalloc
|
||||
#define AwsIotShadow_MallocSubscription Iot_DefaultMalloc
|
||||
#else
|
||||
#error "No malloc function defined for AwsIotShadow_MallocSubscription"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotShadow_FreeSubscription
|
||||
#ifdef Iot_DefaultFree
|
||||
#define AwsIotShadow_FreeSubscription Iot_DefaultFree
|
||||
#else
|
||||
#error "No free function defined for AwsIotShadow_FreeSubscription"
|
||||
#endif
|
||||
#endif
|
||||
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
|
||||
|
||||
/**
|
||||
* @cond DOXYGEN_IGNORE
|
||||
* Doxygen should ignore this section.
|
||||
*
|
||||
* Provide default values for undefined configuration constants.
|
||||
*/
|
||||
#ifndef AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS
|
||||
#define AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS ( 5000 )
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief The number of currently available Shadow operations.
|
||||
*
|
||||
* The 3 Shadow operations are DELETE, GET, and UPDATE.
|
||||
*/
|
||||
#define SHADOW_OPERATION_COUNT ( 3 )
|
||||
|
||||
/**
|
||||
* @brief The number of currently available Shadow callbacks.
|
||||
*
|
||||
* The 2 Shadow callbacks are `update/delta` (AKA "Delta") and `/update/documents`
|
||||
* (AKA "Updated").
|
||||
*/
|
||||
#define SHADOW_CALLBACK_COUNT ( 2 )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Shadow DELETE operation in a Shadow MQTT topic.
|
||||
*/
|
||||
#define SHADOW_DELETE_OPERATION_STRING "/shadow/delete"
|
||||
|
||||
/**
|
||||
* @brief The length of #SHADOW_DELETE_OPERATION_STRING.
|
||||
*/
|
||||
#define SHADOW_DELETE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_DELETE_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Shadow GET operation in a Shadow MQTT topic.
|
||||
*/
|
||||
#define SHADOW_GET_OPERATION_STRING "/shadow/get"
|
||||
|
||||
/**
|
||||
* @brief The length of #SHADOW_GET_OPERATION_STRING.
|
||||
*/
|
||||
#define SHADOW_GET_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_GET_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Shadow UPDATE operation in a Shadow MQTT topic.
|
||||
*/
|
||||
#define SHADOW_UPDATE_OPERATION_STRING "/shadow/update"
|
||||
|
||||
/**
|
||||
* @brief The length of #SHADOW_UPDATE_OPERATION_STRING.
|
||||
*/
|
||||
#define SHADOW_UPDATE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_UPDATE_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The suffix for a Shadow delta topic.
|
||||
*/
|
||||
#define SHADOW_DELTA_SUFFIX "/delta"
|
||||
|
||||
/**
|
||||
* @brief The length of #SHADOW_DELTA_SUFFIX.
|
||||
*/
|
||||
#define SHADOW_DELTA_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_DELTA_SUFFIX ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The suffix for a Shadow updated topic.
|
||||
*/
|
||||
#define SHADOW_UPDATED_SUFFIX "/documents"
|
||||
|
||||
/**
|
||||
* @brief The length of #SHADOW_UPDATED_SUFFIX.
|
||||
*/
|
||||
#define SHADOW_UPDATED_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_UPDATED_SUFFIX ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The length of the longest Shadow suffix.
|
||||
*/
|
||||
#define SHADOW_LONGEST_SUFFIX_LENGTH SHADOW_UPDATED_SUFFIX_LENGTH
|
||||
|
||||
/**
|
||||
* @brief The macro to convert MQTT error codes to Shadow error codes.
|
||||
* Below are the conversions happening.
|
||||
* IOT_MQTT_SUCCESS to AWS_IOT_SHADOW_SUCCESS
|
||||
* IOT_MQTT_NO_MEMORY to AWS_IOT_SHADOW_NO_MEMORY
|
||||
* all other error codes to AWS_IOT_SHADOW_MQTT_ERROR
|
||||
*/
|
||||
#define SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( X ) \
|
||||
( ( X ) == IOT_MQTT_SUCCESS ) ? AWS_IOT_SHADOW_SUCCESS : \
|
||||
( ( X ) == IOT_MQTT_NO_MEMORY ) ? AWS_IOT_SHADOW_NO_MEMORY : \
|
||||
AWS_IOT_SHADOW_MQTT_ERROR
|
||||
|
||||
/*----------------------- Shadow internal data types ------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Enumerations representing each of the Shadow library's API functions.
|
||||
*/
|
||||
typedef enum _shadowOperationType
|
||||
{
|
||||
/* Shadow operations. */
|
||||
SHADOW_DELETE = 0, /**< @ref shadow_function_deleteasync */
|
||||
SHADOW_GET = 1, /**< @ref shadow_function_getasync */
|
||||
SHADOW_UPDATE = 2, /**< @ref shadow_function_updateasync */
|
||||
|
||||
/* Shadow callbacks. */
|
||||
SET_DELTA_CALLBACK = 3, /**< @ref shadow_function_setdeltacallback */
|
||||
SET_UPDATED_CALLBACK = 4 /**< @ref shadow_function_setupdatedcallback */
|
||||
} _shadowOperationType_t;
|
||||
|
||||
/**
|
||||
* @brief Enumerations representing each of the Shadow callback functions.
|
||||
*/
|
||||
typedef enum _shadowCallbackType
|
||||
{
|
||||
DELTA_CALLBACK = 0, /**< Delta callback. */
|
||||
UPDATED_CALLBACK = 1 /**< Updated callback. */
|
||||
} _shadowCallbackType_t;
|
||||
|
||||
/**
|
||||
* @brief Represents a Shadow subscriptions object.
|
||||
*
|
||||
* These structures are stored in a list.
|
||||
*/
|
||||
typedef struct _shadowSubscription
|
||||
{
|
||||
IotLink_t link; /**< @brief List link member. */
|
||||
|
||||
int32_t references[ SHADOW_OPERATION_COUNT ]; /**< @brief Reference counter for Shadow operation topics. */
|
||||
AwsIotShadowCallbackInfo_t callbacks[ SHADOW_CALLBACK_COUNT ]; /**< @brief Shadow callbacks for this Thing. */
|
||||
|
||||
/**
|
||||
* @brief Buffer allocated for removing Shadow topics.
|
||||
*
|
||||
* This buffer is pre-allocated to ensure that memory is available when
|
||||
* unsubscribing.
|
||||
*/
|
||||
char * pTopicBuffer;
|
||||
|
||||
size_t thingNameLength; /**< @brief Length of Thing Name. */
|
||||
char pThingName[]; /**< @brief Thing Name associated with this subscriptions object. */
|
||||
} _shadowSubscription_t;
|
||||
|
||||
/**
|
||||
* @brief Internal structure representing a single Shadow operation (DELETE,
|
||||
* GET, or UPDATE).
|
||||
*
|
||||
* A list of these structures keeps track of all in-progress Shadow operations.
|
||||
*/
|
||||
typedef struct _shadowOperation
|
||||
{
|
||||
IotLink_t link; /**< @brief List link member. */
|
||||
|
||||
/* Basic operation information. */
|
||||
_shadowOperationType_t type; /**< @brief Operation type. */
|
||||
uint32_t flags; /**< @brief Flags passed to operation API function. */
|
||||
AwsIotShadowError_t status; /**< @brief Status of operation. */
|
||||
|
||||
IotMqttConnection_t mqttConnection; /**< @brief MQTT connection associated with this operation. */
|
||||
_shadowSubscription_t * pSubscription; /**< @brief Shadow subscriptions object associated with this operation. */
|
||||
|
||||
union
|
||||
{
|
||||
/* Members valid only for a GET operation. */
|
||||
struct
|
||||
{
|
||||
/**
|
||||
* @brief Function to allocate memory for an incoming Shadow document.
|
||||
*
|
||||
* Only used when the flag #AWS_IOT_SHADOW_FLAG_WAITABLE is set.
|
||||
*/
|
||||
void *( *mallocDocument )( size_t );
|
||||
|
||||
const char * pDocument; /**< @brief Retrieved Shadow document. */
|
||||
size_t documentLength; /**< @brief Length of retrieved Shadow document. */
|
||||
} get;
|
||||
|
||||
/* Members valid only for an UPDATE operation. */
|
||||
struct
|
||||
{
|
||||
const char * pClientToken; /**< @brief Client token in update document. */
|
||||
size_t clientTokenLength; /**< @brief Length of client token. */
|
||||
} update;
|
||||
} u; /**< @brief Valid member depends on _shadowOperation_t.type. */
|
||||
|
||||
/* How to notify of an operation's completion. */
|
||||
union
|
||||
{
|
||||
IotSemaphore_t waitSemaphore; /**< @brief Semaphore to be used with @ref shadow_function_wait. */
|
||||
AwsIotShadowCallbackInfo_t callback; /**< @brief User-provided callback function and parameter. */
|
||||
} notify; /**< @brief How to notify of an operation's completion. */
|
||||
} _shadowOperation_t;
|
||||
|
||||
/* Declarations of names printed in logs. */
|
||||
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
|
||||
extern const char * const _pAwsIotShadowOperationNames[];
|
||||
extern const char * const _pAwsIotShadowCallbackNames[];
|
||||
#endif
|
||||
|
||||
/* Declarations of variables for internal Shadow files. */
|
||||
extern uint32_t _AwsIotShadowMqttTimeoutMs;
|
||||
extern IotListDouble_t _AwsIotShadowPendingOperations;
|
||||
extern IotListDouble_t _AwsIotShadowSubscriptions;
|
||||
extern IotMutex_t _AwsIotShadowPendingOperationsMutex;
|
||||
extern IotMutex_t _AwsIotShadowSubscriptionsMutex;
|
||||
|
||||
/*----------------------- Shadow operation functions ------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Create a record for a new in-progress Shadow operation.
|
||||
*
|
||||
* @param[out] pNewOperation Set to point to the new operation on success.
|
||||
* @param[in] operation The type of Shadow operation.
|
||||
* @param[in] flags Flags variables passed to a user-facing Shadow function.
|
||||
* @param[in] pCallbackInfo User-provided callback function and parameter.
|
||||
*
|
||||
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY
|
||||
*/
|
||||
AwsIotShadowError_t _AwsIotShadow_CreateOperation( _shadowOperation_t ** pNewOperation,
|
||||
_shadowOperationType_t operation,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pCallbackInfo );
|
||||
|
||||
/**
|
||||
* @brief Free resources used to record a Shadow operation. This is called when
|
||||
* the operation completes.
|
||||
*
|
||||
* @param[in] pData The operation which completed. This parameter is of type
|
||||
* `void*` to match the signature of [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
void _AwsIotShadow_DestroyOperation( void * pData );
|
||||
|
||||
/**
|
||||
* @brief Fill a buffer with a Shadow topic.
|
||||
*
|
||||
* @param[in] type One of: DELETE, GET, UPDATE.
|
||||
* @param[in] pThingName Thing Name to place in the topic.
|
||||
* @param[in] thingNameLength Length of `pThingName`.
|
||||
* @param[out] pTopicBuffer Address of the buffer for the Shadow topic. If the
|
||||
* pointer at this address is `NULL`, this function will allocate a new buffer;
|
||||
* otherwise, it will use the provided buffer.
|
||||
* @param[out] pOperationTopicLength Length of the Shadow operation topic (excluding
|
||||
* any suffix) placed in `pTopicBuffer`.
|
||||
*
|
||||
* @warning This function does not check the length of `pTopicBuffer`! Any provided
|
||||
* buffer must be large enough to accommodate the full Shadow topic, plus
|
||||
* #SHADOW_LONGEST_SUFFIX_LENGTH.
|
||||
*
|
||||
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY. This function
|
||||
* will not return #AWS_IOT_SHADOW_NO_MEMORY when a buffer is provided.
|
||||
*/
|
||||
AwsIotShadowError_t _AwsIotShadow_GenerateShadowTopic( _shadowOperationType_t type,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
char ** pTopicBuffer,
|
||||
uint16_t * pOperationTopicLength );
|
||||
|
||||
/**
|
||||
* @brief Process a Shadow operation by sending the necessary MQTT packets.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use.
|
||||
* @param[in] pThingName Thing Name for the Shadow operation.
|
||||
* @param[in] thingNameLength Length of `pThingName`.
|
||||
* @param[in] pOperation Operation data to process.
|
||||
* @param[in] pDocumentInfo Information on the Shadow document for GET or UPDATE
|
||||
* operations.
|
||||
*
|
||||
* @return #AWS_IOT_SHADOW_STATUS_PENDING on success. On error, one of
|
||||
* #AWS_IOT_SHADOW_NO_MEMORY or #AWS_IOT_SHADOW_MQTT_ERROR.
|
||||
*/
|
||||
AwsIotShadowError_t _AwsIotShadow_ProcessOperation( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
_shadowOperation_t * pOperation,
|
||||
const AwsIotShadowDocumentInfo_t * pDocumentInfo );
|
||||
|
||||
/*---------------------- Shadow subscription functions ----------------------*/
|
||||
|
||||
/**
|
||||
* @brief Find a Shadow subscription object. May create a new subscription object
|
||||
* and adds it to the subscription list if not found.
|
||||
*
|
||||
* @param[in] pThingName Thing Name in the subscription object.
|
||||
* @param[in] thingNameLength Length of `pThingName`.
|
||||
* @param[in] createIfNotFound If `true`, attempt to create a new subscription
|
||||
* object if no match is found.
|
||||
*
|
||||
* @return Pointer to a Shadow subscription object, either found or newly
|
||||
* allocated. Returns `NULL` if no subscription object is found and a new
|
||||
* subscription object could not be allocated.
|
||||
*
|
||||
* @note This function should be called with the subscription list mutex locked.
|
||||
*/
|
||||
_shadowSubscription_t * _AwsIotShadow_FindSubscription( const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
bool createIfNotFound );
|
||||
|
||||
/**
|
||||
* @brief Remove a Shadow subscription object from the subscription list if
|
||||
* unreferenced.
|
||||
*
|
||||
* @param[in] pSubscription Subscription object to check. If this object has no
|
||||
* active references, it is removed from the subscription list.
|
||||
* @param[out] pRemovedSubscription Removed subscription object, if any. Optional;
|
||||
* pass `NULL` to ignore. If not `NULL`, this parameter will be set to the removed
|
||||
* subscription and that subscription will not be destroyed.
|
||||
*
|
||||
* @note This function should be called with the subscription list mutex locked.
|
||||
*/
|
||||
void _AwsIotShadow_RemoveSubscription( _shadowSubscription_t * pSubscription,
|
||||
_shadowSubscription_t ** pRemovedSubscription );
|
||||
|
||||
/**
|
||||
* @brief Free resources used for a Shadow subscription object.
|
||||
*
|
||||
* @param[in] pData The subscription object to destroy. This parameter is of type
|
||||
* `void*` to match the signature of [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
void _AwsIotShadow_DestroySubscription( void * pData );
|
||||
|
||||
/**
|
||||
* @brief Increment the reference count of a Shadow subscriptions object.
|
||||
*
|
||||
* Also adds MQTT subscriptions if necessary.
|
||||
*
|
||||
* @param[in] pOperation The operation for which the reference count should be
|
||||
* incremented.
|
||||
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
|
||||
* subscriptions need to be added.
|
||||
* @param[in] operationTopicLength The length of the operation topic in `pTopicBuffer`.
|
||||
* @param[in] callback MQTT callback function for when this operation completes.
|
||||
*
|
||||
* @warning This function does not check the length of `pTopicBuffer`! Any provided
|
||||
* buffer must already contain the Shadow operation topic, plus enough space for the
|
||||
* status suffix.
|
||||
*
|
||||
* @return #AWS_IOT_SHADOW_SUCCESS on success. On error, one of
|
||||
* #AWS_IOT_SHADOW_NO_MEMORY or #AWS_IOT_SHADOW_MQTT_ERROR.
|
||||
*
|
||||
* @note This function should be called with the subscription list mutex locked.
|
||||
*/
|
||||
AwsIotShadowError_t _AwsIotShadow_IncrementReferences( _shadowOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
AwsIotMqttCallbackFunction_t callback );
|
||||
|
||||
/**
|
||||
* @brief Decrement the reference count of a Shadow subscriptions object.
|
||||
*
|
||||
* Also removed MQTT subscriptions and deletes the subscription object if necessary.
|
||||
*
|
||||
* @param[in] pOperation The operation for which the reference count should be
|
||||
* decremented.
|
||||
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
|
||||
* subscriptions need to be removed.
|
||||
* @param[out] pRemovedSubscription Set to point to a removed subscription.
|
||||
* Optional; pass `NULL` to ignore. If not `NULL`, this function will not destroy
|
||||
* a removed subscription.
|
||||
*
|
||||
* @warning This function does not check the length of `pTopicBuffer`! Any provided
|
||||
* buffer must be large enough to accommodate the full Shadow topic, plus
|
||||
* #SHADOW_LONGEST_SUFFIX_LENGTH.
|
||||
*
|
||||
* @note This function should be called with the subscription list mutex locked.
|
||||
*/
|
||||
void _AwsIotShadow_DecrementReferences( _shadowOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
_shadowSubscription_t ** pRemovedSubscription );
|
||||
|
||||
/*------------------------- Shadow parser functions -------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Parse a Shadow error document.
|
||||
*
|
||||
* @param[in] pErrorDocument The error document to parse.
|
||||
* @param[in] errorDocumentLength The length of `pErrorDocument`.
|
||||
*
|
||||
* @return One of the #AwsIotShadowError_t ranging from 400 to 500 on success.
|
||||
* #AWS_IOT_SHADOW_BAD_RESPONSE on error.
|
||||
*/
|
||||
AwsIotShadowError_t _AwsIotShadow_ParseErrorDocument( const char * pErrorDocument,
|
||||
size_t errorDocumentLength );
|
||||
|
||||
#endif /* ifndef AWS_IOT_SHADOW_INTERNAL_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue