mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-08-29 22:48:37 -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
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.0
|
||||
* Copyright (C) 2019 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.h
|
||||
* @brief Provides routines and constants that are common to AWS IoT libraries.
|
||||
* This header should not be included in typical application code.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_H_
|
||||
#define AWS_IOT_H_
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Platform types include. */
|
||||
#include "types/iot_platform_types.h"
|
||||
|
||||
/* MQTT types include. */
|
||||
#include "types/iot_mqtt_types.h"
|
||||
|
||||
/**
|
||||
* @brief The longest Thing Name accepted by AWS IoT, per the [AWS IoT
|
||||
* Service Limits](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#limits_iot).
|
||||
*/
|
||||
#define AWS_IOT_MAX_THING_NAME_LENGTH ( 128 )
|
||||
|
||||
/**
|
||||
* @brief The common prefix of all AWS IoT MQTT topics.
|
||||
*/
|
||||
#define AWS_IOT_TOPIC_PREFIX "$aws/things/"
|
||||
|
||||
/**
|
||||
* @brief The length of #AWS_IOT_TOPIC_PREFIX.
|
||||
*/
|
||||
#define AWS_IOT_TOPIC_PREFIX_LENGTH ( ( uint16_t ) ( sizeof( AWS_IOT_TOPIC_PREFIX ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The suffix for an AWS IoT operation "accepted" topic.
|
||||
*/
|
||||
#define AWS_IOT_ACCEPTED_SUFFIX "/accepted"
|
||||
|
||||
/**
|
||||
* @brief The length of #AWS_IOT_ACCEPTED_SUFFIX.
|
||||
*/
|
||||
#define AWS_IOT_ACCEPTED_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( AWS_IOT_ACCEPTED_SUFFIX ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The suffix for an AWS IoT operation "rejected" topic.
|
||||
*/
|
||||
#define AWS_IOT_REJECTED_SUFFIX "/rejected"
|
||||
|
||||
/**
|
||||
* @brief The length of #AWS_IOT_REJECTED_SUFFIX.
|
||||
*/
|
||||
#define AWS_IOT_REJECTED_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( AWS_IOT_REJECTED_SUFFIX ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The JSON key used to represent client tokens for AWS IoT.
|
||||
*/
|
||||
#define AWS_IOT_CLIENT_TOKEN_KEY "clientToken"
|
||||
|
||||
/**
|
||||
* @brief The length of #AWS_IOT_CLIENT_TOKEN_KEY.
|
||||
*/
|
||||
#define AWS_IOT_CLIENT_TOKEN_KEY_LENGTH ( sizeof( AWS_IOT_CLIENT_TOKEN_KEY ) - 1 )
|
||||
|
||||
/**
|
||||
* @brief The length of the longest client token allowed by AWS IoT.
|
||||
*/
|
||||
#define AWS_IOT_CLIENT_TOKEN_MAX_LENGTH ( 64 )
|
||||
|
||||
/**
|
||||
* @brief A flag to represent persistent subscriptions in a subscriptions
|
||||
* object.
|
||||
*
|
||||
* Its value is negative to distinguish it from valid subscription counts, which
|
||||
* are 0 or positive.
|
||||
*/
|
||||
#define AWS_IOT_PERSISTENT_SUBSCRIPTION ( -1 )
|
||||
|
||||
/**
|
||||
* @brief Function pointer representing an MQTT blocking operation.
|
||||
*
|
||||
* Currently, this is used to represent @ref mqtt_function_subscribesync or
|
||||
* @ref mqtt_function_unsubscribesync.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for the subscription.
|
||||
* @param[in] pSubscriptionList Pointer to the first element in the array of
|
||||
* subscriptions.
|
||||
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
|
||||
* Currently, flags are ignored by this function; this parameter is for
|
||||
* future-compatibility.
|
||||
* @param[in] timeoutMs If the MQTT server does not acknowledge the subscriptions within
|
||||
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #IOT_MQTT_SUCCESS
|
||||
* - #IOT_MQTT_NOT_INITIALIZED
|
||||
* - #IOT_MQTT_BAD_PARAMETER
|
||||
* - #IOT_MQTT_NO_MEMORY
|
||||
* - #IOT_MQTT_NETWORK_ERROR
|
||||
* - #IOT_MQTT_SCHEDULING_ERROR
|
||||
* - #IOT_MQTT_BAD_RESPONSE
|
||||
* - #IOT_MQTT_TIMEOUT
|
||||
* - #IOT_MQTT_SERVER_REFUSED
|
||||
*/
|
||||
typedef IotMqttError_t ( * AwsIotMqttFunction_t )( IotMqttConnection_t mqttConnection,
|
||||
const IotMqttSubscription_t * pSubscriptionList,
|
||||
size_t subscriptionCount,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs );
|
||||
|
||||
/**
|
||||
* @brief Function pointer representing an MQTT library callback function.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage The received DELTA document (as an MQTT PUBLISH message).
|
||||
*/
|
||||
typedef void ( * AwsIotMqttCallbackFunction_t )( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Enumerations representing each of the statuses that may be parsed
|
||||
* from a topic.
|
||||
*/
|
||||
typedef enum AwsIotStatus
|
||||
{
|
||||
AWS_IOT_ACCEPTED = 0, /**< Operation accepted. */
|
||||
AWS_IOT_REJECTED = 1, /**< Operation rejected. */
|
||||
AWS_IOT_UNKNOWN = 2 /**< Unknown status (neither accepted nor rejected). */
|
||||
} AwsIotStatus_t;
|
||||
|
||||
/**
|
||||
* @brief Information required to generate a topic for AWS IoT.
|
||||
*/
|
||||
typedef struct AwsIotTopicInfo
|
||||
{
|
||||
const char * pThingName; /**< @brief The Thing Name associated with the operation. */
|
||||
size_t thingNameLength; /**< @brief The length of `pThingName`. */
|
||||
const char * pOperationName; /**< @brief The operation name to place in the topic. */
|
||||
uint16_t operationNameLength; /**< @brief The length of `pOperationName`. */
|
||||
uint16_t longestSuffixLength; /**< @brief The length of longest suffix that will be placed at the end of the topic. */
|
||||
void * ( *mallocString )( size_t size ); /**< @brief Function used to allocate a string, if needed. */
|
||||
} AwsIotTopicInfo_t;
|
||||
|
||||
/**
|
||||
* @brief Information needed to modify AWS IoT subscription topics.
|
||||
*
|
||||
* @warning The buffer passed as `pTopicFilterBase` must be large enough to
|
||||
* accommodate the "/accepted" and "/rejected" suffixes.
|
||||
*/
|
||||
typedef struct AwsIotSubscriptionInfo_t
|
||||
{
|
||||
IotMqttConnection_t mqttConnection; /**< @brief The MQTT connection to use. */
|
||||
AwsIotMqttCallbackFunction_t callbackFunction; /**< @brief Callback function for MQTT subscribe. */
|
||||
uint32_t timeout; /**< @brief Timeout for MQTT function. */
|
||||
|
||||
/* Topic filter. */
|
||||
char * pTopicFilterBase; /**< @brief Contains the base topic filter, without "/accepted" or "/rejected". */
|
||||
uint16_t topicFilterBaseLength; /**< @brief Length of the base topic filter. */
|
||||
} AwsIotSubscriptionInfo_t;
|
||||
|
||||
/**
|
||||
* @brief Thing Name and length, used to match subscriptions.
|
||||
*/
|
||||
typedef struct AwsIotThingName
|
||||
{
|
||||
const char * pThingName; /**< @brief Thing Name to compare. */
|
||||
size_t thingNameLength; /**< @brief Length of `pThingName`. */
|
||||
} AwsIotThingName_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes the lists used by AWS IoT operations.
|
||||
*
|
||||
* @param[in] pPendingOperationsList The list that holds pending operations for
|
||||
* a library.
|
||||
* @param[in] pSubscriptionsList The list that holds subscriptions for a library.
|
||||
* @param[in] pPendingOperationsMutex The mutex that guards the pending operations
|
||||
* list.
|
||||
* @param[in] pSubscriptionsMutex The mutex that guards the subscriptions list.
|
||||
*
|
||||
* @return `true` if all initialization succeeded; `false` otherwise.
|
||||
*/
|
||||
bool AwsIot_InitLists( IotListDouble_t * pPendingOperationsList,
|
||||
IotListDouble_t * pSubscriptionsList,
|
||||
IotMutex_t * pPendingOperationsMutex,
|
||||
IotMutex_t * pSubscriptionsMutex );
|
||||
|
||||
/**
|
||||
* @brief Checks that a Thing Name is valid for AWS IoT.
|
||||
*
|
||||
* @param[in] pThingName Thing Name to validate.
|
||||
* @param[in] thingNameLength Length of `pThingName`.
|
||||
*
|
||||
* @return `true` if `pThingName` is valid; `false` otherwise.
|
||||
*/
|
||||
bool AwsIot_ValidateThingName( const char * pThingName,
|
||||
size_t thingNameLength );
|
||||
|
||||
/**
|
||||
* @brief Extracts the client token from a JSON document.
|
||||
*
|
||||
* The client token is used to differentiate AWS IoT operations. It is unique per
|
||||
* operation.
|
||||
*
|
||||
* @param[in] pJsonDocument The JSON document to parse.
|
||||
* @param[in] jsonDocumentLength The length of `pJsonDocument`.
|
||||
* @param[out] pClientToken Set to point to the client token in `pJsonDocument`.
|
||||
* @param[out] pClientTokenLength Set to the length of the client token.
|
||||
*
|
||||
* @return `true` if the client token was found; `false` otherwise. The output
|
||||
* parameters are only valid if this function returns `true`.
|
||||
*/
|
||||
bool AwsIot_GetClientToken( const char * pJsonDocument,
|
||||
size_t jsonDocumentLength,
|
||||
const char ** pClientToken,
|
||||
size_t * pClientTokenLength );
|
||||
|
||||
/**
|
||||
* @brief Parse the Thing Name from an MQTT topic.
|
||||
*
|
||||
* @param[in] pTopicName The topic to parse.
|
||||
* @param[in] topicNameLength The length of `pTopicName`.
|
||||
* @param[out] pThingName Set to point to the Thing Name.
|
||||
* @param[out] pThingNameLength Set to the length of the Thing Name.
|
||||
*
|
||||
* @return `true` if a Thing Name was successfully parsed; `false` otherwise. The output
|
||||
* parameters are only valid if this function returns `true`.
|
||||
*/
|
||||
bool AwsIot_ParseThingName( const char * pTopicName,
|
||||
uint16_t topicNameLength,
|
||||
const char ** pThingName,
|
||||
size_t * pThingNameLength );
|
||||
|
||||
/**
|
||||
* @brief Parse the operation status (accepted or rejected) from an MQTT topic.
|
||||
*
|
||||
* @param[in] pTopicName The topic to parse.
|
||||
* @param[in] topicNameLength The length of `pTopicName`.
|
||||
*
|
||||
* @return Any #AwsIotStatus_t.
|
||||
*/
|
||||
AwsIotStatus_t AwsIot_ParseStatus( const char * pTopicName,
|
||||
uint16_t topicNameLength );
|
||||
|
||||
/**
|
||||
* @brief Generate a topic to use for an AWS IoT operation.
|
||||
*
|
||||
* @param[in] pTopicInfo Information needed to generate an AWS IoT topic.
|
||||
* @param[in,out] pTopicBuffer Where to place the generated topic. An existing
|
||||
* buffer may be passed in. If `NULL`, this function will attempt to allocate a
|
||||
* new buffer.
|
||||
* @param[out] pOperationTopicLength Set to the length of the generated topic.
|
||||
*
|
||||
* @warning This function does not check the length of `pTopicBuffer`! Any provided
|
||||
* buffer must be long enough to accommodate the Thing Name, operation name, and
|
||||
* any other suffixes.
|
||||
*
|
||||
* @return `true` if the topic was successfully generated; `false` otherwise.
|
||||
* This function will always succeed when an input buffer is provided.
|
||||
*/
|
||||
bool AwsIot_GenerateOperationTopic( const AwsIotTopicInfo_t * pTopicInfo,
|
||||
char ** pTopicBuffer,
|
||||
uint16_t * pOperationTopicLength );
|
||||
|
||||
/**
|
||||
* @brief Add or remove subscriptions for AWS IoT operations.
|
||||
*
|
||||
* @param[in] mqttOperation Either @ref mqtt_function_subscribesync or
|
||||
* @ref mqtt_function_unsubscribesync.
|
||||
* @param[in] pSubscriptionInfo Information needed to process an MQTT
|
||||
* operation.
|
||||
*
|
||||
* @return See the return values of @ref mqtt_function_subscribesync or
|
||||
* @ref mqtt_function_unsubscribesync.
|
||||
*/
|
||||
IotMqttError_t AwsIot_ModifySubscriptions( AwsIotMqttFunction_t mqttOperation,
|
||||
const AwsIotSubscriptionInfo_t * pSubscriptionInfo );
|
||||
|
||||
#endif /* ifndef AWS_IOT_H_ */
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.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_doc_parser.h
|
||||
* @brief Parser for AWS IoT Services Documents. This is a JSON parser
|
||||
* specifically designed to process and retrieve a value from a AWS IoT JSON
|
||||
* document, used in AWS IoT libraries such as Shadow and Jobs. Given a key and
|
||||
* a JSON document, AwsIotDocParser_FindValue() will find the first occurrence
|
||||
* of the key and return its respective value. The design goal of this parser
|
||||
* is to be light weight and to be of low memory footprint. However, it does
|
||||
* not check the correctness of the JSON documents. Hence, this parser is not
|
||||
* meant to be used for general purpose JSON parsing.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_DOC_PARSER_H_
|
||||
#define AWS_IOT_DOC_PARSER_H_
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief Find a value for a key from a AWS IoT service JSON document.
|
||||
*
|
||||
* @warning The parsing will not check for the correctness of the JSON document.
|
||||
* It is designed to be light weight and to be of low memory footprint rather
|
||||
* than checking for the correctness of the JSON document. Hence this is not
|
||||
* meant to be used for a general purpose JSON parsing. This is recommended to
|
||||
* be used only with mutually authenticated AWS IoT services such as Shadow and
|
||||
* Jobs where the document will always be a well formatted JSON.
|
||||
*
|
||||
* @param[in] pAwsIotJsonDocument Pointer to AWS IoT Service JSON document.
|
||||
* @param[in] awsIotJsonDocumentLength Length of AWS IoT Service JSON document.
|
||||
* @param[in] pAwsIotJsonKey JSON key for finding the associated value.
|
||||
* @param[in] awsIotJsonKeyLength Length of the JSON key.
|
||||
* @param[out] pAwsIotJsonValue Pointer to the pointer of value found.
|
||||
* @param[out] pAwsIotJsonValueLength Pointer to the length of the value found.
|
||||
*
|
||||
* @returns `true` if a value is found, `false` if a value cannot be found. If
|
||||
* returns `false`, the values in out pointers will not be valid.
|
||||
*/
|
||||
bool AwsIotDocParser_FindValue( const char * pAwsIotJsonDocument,
|
||||
size_t awsIotJsonDocumentLength,
|
||||
const char * pAwsIotJsonKey,
|
||||
size_t awsIotJsonKeyLength,
|
||||
const char ** pAwsIotJsonValue,
|
||||
size_t * pAwsIotJsonValueLength );
|
||||
|
||||
#endif /* ifndef AWS_IOT_DOC_PARSER_H_ */
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.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_doc_parser.c
|
||||
* @brief Implements the functions in aws_iot_doc_parser.h
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* JSON utilities include. */
|
||||
#include "aws_iot_doc_parser.h"
|
||||
|
||||
#define IS_QUOTE( str, idx ) \
|
||||
( ( str )[ ( idx ) ] == '"' && ( ( idx ) == 0 || ( str )[ ( idx ) - 1 ] != '\\' ) )
|
||||
#define IS_WHITESPACE( str, idx ) \
|
||||
( ( str )[ ( idx ) ] == ' ' || ( str )[ ( idx ) ] == '\n' || ( str )[ ( idx ) ] == '\r' || ( str )[ ( idx ) ] == '\t' )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool AwsIotDocParser_FindValue( const char * pAwsIotJsonDocument,
|
||||
size_t awsIotJsonDocumentLength,
|
||||
const char * pAwsIotJsonKey,
|
||||
size_t awsIotJsonKeyLength,
|
||||
const char ** pAwsIotJsonValue,
|
||||
size_t * pAwsIotJsonValueLength )
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t jsonValueLength = 0;
|
||||
char openCharacter = '\0', closeCharacter = '\0';
|
||||
int nestingLevel = 0;
|
||||
bool isWithinQuotes = false;
|
||||
|
||||
/* Validate all the arguments.*/
|
||||
if( ( pAwsIotJsonDocument == NULL ) || ( pAwsIotJsonKey == NULL ) ||
|
||||
( awsIotJsonDocumentLength == 0 ) || ( awsIotJsonKeyLength == 0 ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure the JSON document is long enough to contain the key/value pair. At
|
||||
* the very least, a JSON key/value pair must contain the key and the 6
|
||||
* characters {":""} */
|
||||
if( awsIotJsonDocumentLength < awsIotJsonKeyLength + 6 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Search the characters in the JSON document for the key. The end of the JSON
|
||||
* document does not have to be searched once too few characters remain to hold a
|
||||
* value. */
|
||||
while( i < awsIotJsonDocumentLength - awsIotJsonKeyLength - 3 )
|
||||
{
|
||||
/* If the first character in the key is found and there's an unescaped double
|
||||
* quote after the key length, do a string compare for the key. */
|
||||
if( ( IS_QUOTE( pAwsIotJsonDocument, i ) ) &&
|
||||
( IS_QUOTE( pAwsIotJsonDocument, i + 1 + awsIotJsonKeyLength ) ) &&
|
||||
( pAwsIotJsonDocument[ i + 1 ] == pAwsIotJsonKey[ 0 ] ) &&
|
||||
( strncmp( pAwsIotJsonDocument + i + 1,
|
||||
pAwsIotJsonKey,
|
||||
awsIotJsonKeyLength ) == 0 ) )
|
||||
{
|
||||
/* Key found; this is a potential match. */
|
||||
|
||||
/* Skip the characters in the JSON key and closing double quote. */
|
||||
/* While loop guarantees that i < awsIotJsonDocumentLength - 1 */
|
||||
i += awsIotJsonKeyLength + 2;
|
||||
|
||||
/* Skip all whitespace characters between the closing " and the : */
|
||||
while( IS_WHITESPACE( pAwsIotJsonDocument, i ) )
|
||||
{
|
||||
i++;
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* The character immediately following a key (and any whitespace) must be a :
|
||||
* If it's another character, then this string is a JSON value; skip it. */
|
||||
if( pAwsIotJsonDocument[ i ] != ':' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Skip the : */
|
||||
i++;
|
||||
}
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Skip all whitespace characters between : and the first character in the value. */
|
||||
while( IS_WHITESPACE( pAwsIotJsonDocument, i ) )
|
||||
{
|
||||
i++;
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Value found. Set the output parameter. */
|
||||
if( pAwsIotJsonValue != NULL )
|
||||
{
|
||||
*pAwsIotJsonValue = pAwsIotJsonDocument + i;
|
||||
}
|
||||
|
||||
/* Calculate the value's length. */
|
||||
switch( pAwsIotJsonDocument[ i ] )
|
||||
{
|
||||
/* Calculate length of a JSON string. */
|
||||
case '\"':
|
||||
/* Include the length of the opening and closing double quotes. */
|
||||
jsonValueLength = 2;
|
||||
|
||||
/* Skip the opening double quote. */
|
||||
i++;
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add the length of all characters in the JSON string. */
|
||||
while( pAwsIotJsonDocument[ i ] != '\"' )
|
||||
{
|
||||
/* Ignore escaped double quotes. */
|
||||
if( ( pAwsIotJsonDocument[ i ] == '\\' ) &&
|
||||
( i + 1 < awsIotJsonDocumentLength ) &&
|
||||
( pAwsIotJsonDocument[ i + 1 ] == '\"' ) )
|
||||
{
|
||||
/* Skip the characters \" */
|
||||
i += 2;
|
||||
jsonValueLength += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add the length of a single character. */
|
||||
i++;
|
||||
jsonValueLength++;
|
||||
}
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/* Set the matching opening and closing characters of a JSON object or array.
|
||||
* The length calculation is performed below. */
|
||||
case '{':
|
||||
openCharacter = '{';
|
||||
closeCharacter = '}';
|
||||
break;
|
||||
|
||||
case '[':
|
||||
openCharacter = '[';
|
||||
closeCharacter = ']';
|
||||
break;
|
||||
|
||||
/* Calculate the length of a JSON primitive. */
|
||||
default:
|
||||
|
||||
/* Skip the characters in the JSON value. The JSON value ends with a , or } */
|
||||
while( pAwsIotJsonDocument[ i ] != ',' &&
|
||||
pAwsIotJsonDocument[ i ] != '}' )
|
||||
{
|
||||
/* Any whitespace before a , or } means the JSON document is invalid. */
|
||||
if( IS_WHITESPACE( pAwsIotJsonDocument, i ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
i++;
|
||||
jsonValueLength++;
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate the length of a JSON object or array. */
|
||||
if( ( openCharacter != '\0' ) && ( closeCharacter != '\0' ) )
|
||||
{
|
||||
/* Include the length of the opening and closing characters. */
|
||||
jsonValueLength = 2;
|
||||
|
||||
/* Skip the opening character. */
|
||||
i++;
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add the length of all characters in the JSON object or array. This
|
||||
* includes the length of nested objects. */
|
||||
while( pAwsIotJsonDocument[ i ] != closeCharacter ||
|
||||
( pAwsIotJsonDocument[ i ] == closeCharacter && ( nestingLevel != 0 || isWithinQuotes ) ) )
|
||||
{
|
||||
/* Check if its a quote so as to avoid considering the
|
||||
* nested levels for opening and closing characters within
|
||||
* quotes.
|
||||
*/
|
||||
if( IS_QUOTE( pAwsIotJsonDocument, i ) )
|
||||
{
|
||||
/*Toggle the flag*/
|
||||
isWithinQuotes = !isWithinQuotes;
|
||||
}
|
||||
|
||||
/* Calculate the nesting levels only if not in quotes. */
|
||||
if( !isWithinQuotes )
|
||||
{
|
||||
/* An opening character starts a nested object. */
|
||||
if( pAwsIotJsonDocument[ i ] == openCharacter )
|
||||
{
|
||||
nestingLevel++;
|
||||
}
|
||||
/* A closing character ends a nested object. */
|
||||
else if( pAwsIotJsonDocument[ i ] == closeCharacter )
|
||||
{
|
||||
nestingLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
jsonValueLength++;
|
||||
|
||||
/* If the end of the document is reached, this isn't a match. */
|
||||
if( i >= awsIotJsonDocumentLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* JSON value length calculated; set the output parameter. */
|
||||
if( pAwsIotJsonValueLength != NULL )
|
||||
{
|
||||
*pAwsIotJsonValueLength = jsonValueLength;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.0
|
||||
* Copyright (C) 2019 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_operation.c
|
||||
* @brief Functions for common AWS IoT operations.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* Platform threads include. */
|
||||
#include "platform/iot_threads.h"
|
||||
|
||||
/* AWS IoT include. */
|
||||
#include "aws_iot.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool AwsIot_InitLists( IotListDouble_t * pPendingOperationsList,
|
||||
IotListDouble_t * pSubscriptionsList,
|
||||
IotMutex_t * pPendingOperationsMutex,
|
||||
IotMutex_t * pSubscriptionsMutex )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
|
||||
/* Flags to track cleanup. */
|
||||
bool operationsMutexCreated = false;
|
||||
bool subscriptionsMutexCreated = false;
|
||||
|
||||
/* Create the mutex guarding the pending operations list. */
|
||||
operationsMutexCreated = IotMutex_Create( pPendingOperationsMutex, false );
|
||||
|
||||
if( operationsMutexCreated == false )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
/* Create the mutex guarding the subscriptions list. */
|
||||
subscriptionsMutexCreated = IotMutex_Create( pSubscriptionsMutex, false );
|
||||
|
||||
if( subscriptionsMutexCreated == false )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
/* Initialize lists. */
|
||||
IotListDouble_Create( pPendingOperationsList );
|
||||
IotListDouble_Create( pSubscriptionsList );
|
||||
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
/* Clean up on error. */
|
||||
if( status == false )
|
||||
{
|
||||
if( operationsMutexCreated == true )
|
||||
{
|
||||
IotMutex_Destroy( pPendingOperationsMutex );
|
||||
}
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool AwsIot_GenerateOperationTopic( const AwsIotTopicInfo_t * pTopicInfo,
|
||||
char ** pTopicBuffer,
|
||||
uint16_t * pOperationTopicLength )
|
||||
{
|
||||
bool status = true;
|
||||
uint16_t bufferLength = 0;
|
||||
uint16_t operationTopicLength = 0;
|
||||
char * pBuffer = NULL;
|
||||
|
||||
/* Calculate the required topic buffer length. */
|
||||
bufferLength = ( uint16_t ) ( AWS_IOT_TOPIC_PREFIX_LENGTH +
|
||||
pTopicInfo->thingNameLength +
|
||||
pTopicInfo->operationNameLength +
|
||||
pTopicInfo->longestSuffixLength );
|
||||
|
||||
/* Allocate memory for the topic buffer if no topic buffer is given. */
|
||||
if( *pTopicBuffer == NULL )
|
||||
{
|
||||
pBuffer = pTopicInfo->mallocString( ( size_t ) bufferLength );
|
||||
|
||||
if( pBuffer == NULL )
|
||||
{
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
/* Otherwise, use the given topic buffer. */
|
||||
else
|
||||
{
|
||||
pBuffer = *pTopicBuffer;
|
||||
}
|
||||
|
||||
if( status == true )
|
||||
{
|
||||
/* Copy the AWS IoT topic prefix into the topic buffer. */
|
||||
( void ) memcpy( pBuffer, AWS_IOT_TOPIC_PREFIX, AWS_IOT_TOPIC_PREFIX_LENGTH );
|
||||
operationTopicLength = ( uint16_t ) ( operationTopicLength + AWS_IOT_TOPIC_PREFIX_LENGTH );
|
||||
|
||||
/* Copy the Thing Name into the topic buffer. */
|
||||
( void ) memcpy( pBuffer + operationTopicLength,
|
||||
pTopicInfo->pThingName,
|
||||
pTopicInfo->thingNameLength );
|
||||
operationTopicLength = ( uint16_t ) ( operationTopicLength + pTopicInfo->thingNameLength );
|
||||
|
||||
/* Copy the Shadow operation string into the topic buffer. */
|
||||
( void ) memcpy( pBuffer + operationTopicLength,
|
||||
pTopicInfo->pOperationName,
|
||||
pTopicInfo->operationNameLength );
|
||||
operationTopicLength = ( uint16_t ) ( operationTopicLength + pTopicInfo->operationNameLength );
|
||||
|
||||
/* Set the output parameters. */
|
||||
if( *pTopicBuffer == NULL )
|
||||
{
|
||||
*pTopicBuffer = pBuffer;
|
||||
}
|
||||
|
||||
*pOperationTopicLength = operationTopicLength;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.0
|
||||
* Copyright (C) 2019 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_parser.c
|
||||
* @brief Parses topics for Thing Name and status.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* AWS IoT include. */
|
||||
#include "aws_iot.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* AWS Parser include. */
|
||||
#include "aws_iot_doc_parser.h"
|
||||
|
||||
/**
|
||||
* @brief Minimum allowed topic length for an AWS IoT status topic.
|
||||
*
|
||||
* Topics must contain at least:
|
||||
* - The common prefix
|
||||
* - The suffix "/accepted" or "/rejected"
|
||||
* - 1 character for the Thing Name
|
||||
* - 2 characters for the operation name and the enclosing slashes
|
||||
*/
|
||||
#define MINIMUM_TOPIC_NAME_LENGTH \
|
||||
( uint16_t ) ( AWS_IOT_TOPIC_PREFIX_LENGTH + \
|
||||
AWS_IOT_ACCEPTED_SUFFIX_LENGTH + \
|
||||
1 + 2 )
|
||||
|
||||
/**
|
||||
* @brief The longest client token accepted by AWS IoT service, per AWS IoT
|
||||
* service limits.
|
||||
*/
|
||||
#define MAX_CLIENT_TOKEN_LENGTH ( 64 )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool AwsIot_GetClientToken( const char * pJsonDocument,
|
||||
size_t jsonDocumentLength,
|
||||
const char ** pClientToken,
|
||||
size_t * pClientTokenLength )
|
||||
{
|
||||
/* Extract the client token from the JSON document. */
|
||||
bool status = AwsIotDocParser_FindValue( pJsonDocument,
|
||||
jsonDocumentLength,
|
||||
AWS_IOT_CLIENT_TOKEN_KEY,
|
||||
AWS_IOT_CLIENT_TOKEN_KEY_LENGTH,
|
||||
pClientToken,
|
||||
pClientTokenLength );
|
||||
|
||||
if( status == true )
|
||||
{
|
||||
/* Check that the length of the client token is valid. */
|
||||
if( ( *pClientTokenLength < 2 ) ||
|
||||
( *pClientTokenLength > MAX_CLIENT_TOKEN_LENGTH ) )
|
||||
{
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool AwsIot_ParseThingName( const char * pTopicName,
|
||||
uint16_t topicNameLength,
|
||||
const char ** pThingName,
|
||||
size_t * pThingNameLength )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
const char * pThingNameStart = NULL;
|
||||
size_t thingNameLength = 0;
|
||||
|
||||
/* Check that the topic name is at least as long as the minimum allowed. */
|
||||
if( topicNameLength < MINIMUM_TOPIC_NAME_LENGTH )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
/* Check that the given topic starts with the common prefix. */
|
||||
if( strncmp( AWS_IOT_TOPIC_PREFIX,
|
||||
pTopicName,
|
||||
AWS_IOT_TOPIC_PREFIX_LENGTH ) != 0 )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
/* The Thing Name starts immediately after the topic prefix. */
|
||||
pThingNameStart = pTopicName + AWS_IOT_TOPIC_PREFIX_LENGTH;
|
||||
|
||||
/* Calculate the length of the Thing Name, which is terminated with a '/'. */
|
||||
while( ( thingNameLength + AWS_IOT_TOPIC_PREFIX_LENGTH < ( size_t ) topicNameLength ) &&
|
||||
( pThingNameStart[ thingNameLength ] != '/' ) )
|
||||
{
|
||||
thingNameLength++;
|
||||
}
|
||||
|
||||
/* The end of the topic name was reached without finding a '/'. The topic
|
||||
* name is invalid. */
|
||||
if( thingNameLength + AWS_IOT_TOPIC_PREFIX_LENGTH >= ( size_t ) topicNameLength )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
/* Set the output parameters. */
|
||||
*pThingName = pThingNameStart;
|
||||
*pThingNameLength = thingNameLength;
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotStatus_t AwsIot_ParseStatus( const char * pTopicName,
|
||||
uint16_t topicNameLength )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotStatus_t, AWS_IOT_UNKNOWN );
|
||||
const char * pSuffixStart = NULL;
|
||||
|
||||
/* Both 'accepted' and 'rejected' topics are of the same length
|
||||
* The below is a defensive check at run time to ensure that.
|
||||
*/
|
||||
Iot_DefaultAssert( AWS_IOT_ACCEPTED_SUFFIX_LENGTH == AWS_IOT_REJECTED_SUFFIX_LENGTH );
|
||||
|
||||
/* Check that the status topic name is at least as long as the
|
||||
* "accepted" suffix. This length check will be good for rejected also
|
||||
* as both are of 8 characters in length. */
|
||||
if( topicNameLength > AWS_IOT_ACCEPTED_SUFFIX_LENGTH )
|
||||
{
|
||||
/* Calculate where the "accepted" suffix should start. */
|
||||
pSuffixStart = pTopicName + topicNameLength - AWS_IOT_ACCEPTED_SUFFIX_LENGTH;
|
||||
|
||||
/* Check if the end of the status topic name is "/accepted". */
|
||||
if( strncmp( pSuffixStart,
|
||||
AWS_IOT_ACCEPTED_SUFFIX,
|
||||
AWS_IOT_ACCEPTED_SUFFIX_LENGTH ) == 0 )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ACCEPTED );
|
||||
}
|
||||
|
||||
/* Check if the end of the status topic name is "/rejected". */
|
||||
if( strncmp( pSuffixStart,
|
||||
AWS_IOT_REJECTED_SUFFIX,
|
||||
AWS_IOT_REJECTED_SUFFIX_LENGTH ) == 0 )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_REJECTED );
|
||||
}
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.0
|
||||
* Copyright (C) 2019 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_subscription.c
|
||||
* @brief Functions for common AWS IoT subscriptions.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* AWS IoT include. */
|
||||
#include "aws_iot.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* MQTT include. */
|
||||
#include "iot_mqtt.h"
|
||||
|
||||
/**
|
||||
* @brief Modify subscriptions, either by unsubscribing or subscribing.
|
||||
*
|
||||
* @param[in] mqttOperation Either @ref mqtt_function_subscribesync or @ref
|
||||
* mqtt_function_unsubscribesync.
|
||||
* @param[in] pSubscriptionInfo Information needed to process an MQTT
|
||||
* operation.
|
||||
* @param[in] pTopicFilter The topic filter to modify.
|
||||
* @param[in] topicFilterLength The length of `pTopicFilter`.
|
||||
*
|
||||
* @return See the return values of @ref mqtt_function_subscribesync or
|
||||
* @ref mqtt_function_unsubscribesync.
|
||||
*/
|
||||
static IotMqttError_t _modifySubscriptions( AwsIotMqttFunction_t mqttOperation,
|
||||
const AwsIotSubscriptionInfo_t * pSubscriptionInfo,
|
||||
const char * pTopicFilter,
|
||||
uint16_t topicFilterLength );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static IotMqttError_t _modifySubscriptions( AwsIotMqttFunction_t mqttOperation,
|
||||
const AwsIotSubscriptionInfo_t * pSubscriptionInfo,
|
||||
const char * pTopicFilter,
|
||||
uint16_t topicFilterLength )
|
||||
{
|
||||
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
|
||||
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
|
||||
|
||||
/* Per the AWS IoT documentation, topic subscriptions are QoS 1. */
|
||||
subscription.qos = IOT_MQTT_QOS_1;
|
||||
|
||||
/* Set the members of the subscription parameter. */
|
||||
subscription.pTopicFilter = pTopicFilter;
|
||||
subscription.topicFilterLength = topicFilterLength;
|
||||
subscription.callback.pCallbackContext = NULL;
|
||||
subscription.callback.function = pSubscriptionInfo->callbackFunction;
|
||||
|
||||
/* Call the MQTT operation function.
|
||||
* Subscription count is 1 in this case.
|
||||
* None of the flags are set in this call. Hence 0 is passed for flags.
|
||||
* Please refer to documentation for AwsIotMqttFunction_t for more details.
|
||||
*/
|
||||
status = mqttOperation( pSubscriptionInfo->mqttConnection,
|
||||
&subscription,
|
||||
1,
|
||||
0,
|
||||
pSubscriptionInfo->timeout );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
IotMqttError_t AwsIot_ModifySubscriptions( AwsIotMqttFunction_t mqttOperation,
|
||||
const AwsIotSubscriptionInfo_t * pSubscriptionInfo )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_STATUS_PENDING );
|
||||
IotMqttError_t acceptedStatus = IOT_MQTT_STATUS_PENDING;
|
||||
uint16_t topicFilterLength = 0;
|
||||
|
||||
/* Place the topic "accepted" suffix at the end of the topic buffer. */
|
||||
( void ) memcpy( pSubscriptionInfo->pTopicFilterBase
|
||||
+ pSubscriptionInfo->topicFilterBaseLength,
|
||||
AWS_IOT_ACCEPTED_SUFFIX,
|
||||
AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
|
||||
topicFilterLength = ( uint16_t ) ( pSubscriptionInfo->topicFilterBaseLength
|
||||
+ AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
|
||||
|
||||
/* Modify the subscription to the "accepted" topic. */
|
||||
acceptedStatus = _modifySubscriptions( mqttOperation,
|
||||
pSubscriptionInfo,
|
||||
pSubscriptionInfo->pTopicFilterBase,
|
||||
topicFilterLength );
|
||||
|
||||
if( acceptedStatus != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( acceptedStatus );
|
||||
}
|
||||
|
||||
/* Place the topic "rejected" suffix at the end of the topic buffer. */
|
||||
( void ) memcpy( pSubscriptionInfo->pTopicFilterBase
|
||||
+ pSubscriptionInfo->topicFilterBaseLength,
|
||||
AWS_IOT_REJECTED_SUFFIX,
|
||||
AWS_IOT_REJECTED_SUFFIX_LENGTH );
|
||||
topicFilterLength = ( uint16_t ) ( pSubscriptionInfo->topicFilterBaseLength
|
||||
+ AWS_IOT_REJECTED_SUFFIX_LENGTH );
|
||||
|
||||
/* Modify the subscription to the "rejected" topic. */
|
||||
status = _modifySubscriptions( mqttOperation,
|
||||
pSubscriptionInfo,
|
||||
pSubscriptionInfo->pTopicFilterBase,
|
||||
topicFilterLength );
|
||||
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
/* Clean up on error. */
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
/* Remove the subscription to the "accepted" topic if the subscription
|
||||
* to the "rejected" topic failed. */
|
||||
if( ( mqttOperation == IotMqtt_SubscribeSync ) &&
|
||||
( acceptedStatus == IOT_MQTT_SUCCESS ) )
|
||||
{
|
||||
/* Place the topic "accepted" suffix at the end of the topic buffer. */
|
||||
( void ) memcpy( pSubscriptionInfo->pTopicFilterBase
|
||||
+ pSubscriptionInfo->topicFilterBaseLength,
|
||||
AWS_IOT_ACCEPTED_SUFFIX,
|
||||
AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
|
||||
topicFilterLength = ( uint16_t ) ( pSubscriptionInfo->topicFilterBaseLength
|
||||
+ AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
|
||||
|
||||
( void ) _modifySubscriptions( IotMqtt_UnsubscribeSync,
|
||||
pSubscriptionInfo,
|
||||
pSubscriptionInfo->pTopicFilterBase,
|
||||
topicFilterLength );
|
||||
}
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* AWS IoT Common V1.0.0
|
||||
* Copyright (C) 2019 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_validate.c
|
||||
* @brief Validates Thing Names and other parameters to AWS IoT.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* AWS IoT include. */
|
||||
#include "aws_iot.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool AwsIot_ValidateThingName( const char * pThingName,
|
||||
size_t thingNameLength )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
|
||||
if( pThingName == NULL )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
if( thingNameLength == 0 )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
if( thingNameLength > AWS_IOT_MAX_THING_NAME_LENGTH )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,10 @@
|
|||
+ common
|
||||
Contains the implementation of utility functions used by other AWS IoT libraries.
|
||||
|
||||
+ shadow
|
||||
Contains the implementation of the AWS IoT Shadow client library.
|
||||
|
||||
+ jobs
|
||||
Contain the implementation of the AWS IoT Jobs client library.
|
||||
|
||||
Further libraries will be rolled out soon.
|
|
@ -0,0 +1,5 @@
|
|||
[{000214A0-0000-0000-C000-000000000046}]
|
||||
Prop3=19,11
|
||||
[InternetShortcut]
|
||||
IDList=
|
||||
URL=https://www.freertos.org/jobs
|
|
@ -0,0 +1,927 @@
|
|||
/*
|
||||
* AWS IoT Jobs V1.0.0
|
||||
* Copyright (C) 2019 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_jobs.h
|
||||
* @brief User-facing functions of the Jobs library.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_JOBS_H_
|
||||
#define AWS_IOT_JOBS_H_
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Jobs types include. */
|
||||
#include "types/aws_iot_jobs_types.h"
|
||||
|
||||
/*------------------------- Jobs library functions --------------------------*/
|
||||
|
||||
/**
|
||||
* @functionspage{jobs,Jobs library}
|
||||
* - @functionname{jobs_function_init}
|
||||
* - @functionname{jobs_function_cleanup}
|
||||
* - @functionname{jobs_function_getpendingasync}
|
||||
* - @functionname{jobs_function_getpendingsync}
|
||||
* - @functionname{jobs_function_startnextasync}
|
||||
* - @functionname{jobs_function_startnextsync}
|
||||
* - @functionname{jobs_function_describeasync}
|
||||
* - @functionname{jobs_function_describesync}
|
||||
* - @functionname{jobs_function_updateasync}
|
||||
* - @functionname{jobs_function_updatesync}
|
||||
* - @functionname{jobs_function_wait}
|
||||
* - @functionname{jobs_function_setnotifypendingcallback}
|
||||
* - @functionname{jobs_function_setnotifynextcallback}
|
||||
* - @functionname{jobs_function_removepersistentsubscriptions}
|
||||
* - @functionname{jobs_function_strerror}
|
||||
* - @functionname{jobs_function_statename}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @functionpage{AwsIotJobs_Init,jobs,init}
|
||||
* @functionpage{AwsIotJobs_Cleanup,jobs,cleanup}
|
||||
* @functionpage{AwsIotJobs_GetPendingAsync,jobs,getpendingasync}
|
||||
* @functionpage{AwsIotJobs_GetPendingSync,jobs,getpendingsync}
|
||||
* @functionpage{AwsIotJobs_StartNextAsync,jobs,startnextasync}
|
||||
* @functionpage{AwsIotJobs_StartNextSync,jobs,startnextsync}
|
||||
* @functionpage{AwsIotJobs_DescribeAsync,jobs,describeasync}
|
||||
* @functionpage{AwsIotJobs_DescribeSync,jobs,describesync}
|
||||
* @functionpage{AwsIotJobs_UpdateAsync,jobs,updateasync}
|
||||
* @functionpage{AwsIotJobs_UpdateSync,jobs,updatesync}
|
||||
* @functionpage{AwsIotJobs_Wait,jobs,wait}
|
||||
* @functionpage{AwsIotJobs_SetNotifyPendingCallback,jobs,setnotifypendingcallback}
|
||||
* @functionpage{AwsIotJobs_SetNotifyNextCallback,jobs,setnotifynextcallback}
|
||||
* @functionpage{AwsIotJobs_RemovePersistentSubscriptions,jobs,removepersistentsubscriptions}
|
||||
* @functionpage{AwsIotJobs_strerror,jobs,strerror}
|
||||
* @functionpage{AwsIotJobs_StateName,jobs,statename}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief One-time initialization function for the Jobs library.
|
||||
*
|
||||
* This function performs internal setup of the Jobs library. <b>It must be
|
||||
* called once (and only once) before calling any other Jobs function.</b>
|
||||
* Calling this function more than once without first calling @ref
|
||||
* jobs_function_cleanup may result in a crash.
|
||||
*
|
||||
* @param[in] mqttTimeoutMs The amount of time (in milliseconds) that the Jobs
|
||||
* library will wait for MQTT operations. Optional; set this to `0` to use
|
||||
* @ref AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_INIT_FAILED
|
||||
*
|
||||
* @warning No thread-safety guarantees are provided for this function.
|
||||
*
|
||||
* @see @ref jobs_function_cleanup
|
||||
*/
|
||||
/* @[declare_jobs_init] */
|
||||
AwsIotJobsError_t AwsIotJobs_Init( uint32_t mqttTimeoutMs );
|
||||
/* @[declare_jobs_init] */
|
||||
|
||||
/**
|
||||
* @brief One-time deinitialization function for the Jobs library.
|
||||
*
|
||||
* This function frees resources taken in @ref jobs_function_init and deletes
|
||||
* any [persistent subscriptions.](@ref AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS)
|
||||
* It should be called to clean up the Jobs library. After this function returns,
|
||||
* @ref jobs_function_init must be called again before calling any other Jobs
|
||||
* function.
|
||||
*
|
||||
* @warning No thread-safety guarantees are provided for this function.
|
||||
*
|
||||
* @see @ref jobs_function_init
|
||||
*/
|
||||
/* @[declare_jobs_cleanup] */
|
||||
void AwsIotJobs_Cleanup( void );
|
||||
/* @[declare_jobs_cleanup] */
|
||||
|
||||
/**
|
||||
* @brief Get the list of all pending jobs for a Thing and receive an asynchronous
|
||||
* notification when the response arrives.
|
||||
*
|
||||
* This function implements the [GetPendingJobExecutions]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-getpendingjobexecutions)
|
||||
* command of the Jobs API, which gets the list of all Jobs for a Thing that are
|
||||
* not in a terminal state. The list of retrieved Jobs is returned as the `pResponse`
|
||||
* member in #AwsIotJobsCallbackParam_t, or through the #AwsIotJobsResponse_t
|
||||
* parameter of @ref jobs_function_wait.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pGetPendingOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Jobs operation
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
|
||||
* queuing the Jobs operation.
|
||||
* @return If this function fails before queuing the Jobs operation, it will return one of:
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
|
||||
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
|
||||
* @return Should the Jobs operation fail, the status will be one of:
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*
|
||||
* @see @ref jobs_function_getpendingsync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* // Signature of Jobs callback function.
|
||||
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
|
||||
*
|
||||
* AwsIotJobsOperation_t getPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
|
||||
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
|
||||
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // Set the request info.
|
||||
* requestInfo.mqttConnection = _mqttConnection;
|
||||
* requestInfo.pThingName = THING_NAME;
|
||||
* requestInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // Set the callback function to invoke.
|
||||
* callbackInfo.function = _jobsCallback;
|
||||
*
|
||||
* // Queue Jobs GET PENDING.
|
||||
* AwsIotJobsError_t getPendingResult = AwsIotJobs_GetPendingAsync( &requestInfo,
|
||||
* 0,
|
||||
* &callbackInfo,
|
||||
* &getPendingOperation );
|
||||
*
|
||||
* // GET PENDING should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
|
||||
* // _jobsCallback will be invoked once the Jobs response is received.
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_getpendingasync] */
|
||||
AwsIotJobsError_t AwsIotJobs_GetPendingAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotJobsOperation_t * const pGetPendingOperation );
|
||||
/* @[declare_jobs_getpendingasync] */
|
||||
|
||||
/**
|
||||
* @brief Get the list of all pending jobs for a Thing with a timeout for receiving
|
||||
* the response.
|
||||
*
|
||||
* This function queues a Jobs GET PENDING, then waits for the result. Internally,
|
||||
* this function is a call to @ref jobs_function_getpendingasync followed by
|
||||
* @ref jobs_function_wait. See @ref jobs_function_getpendingasync for more information
|
||||
* on the Jobs GET PENDING command.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] timeoutMs If a response is not received within this timeout, this
|
||||
* function returns #AWS_IOT_JOBS_TIMEOUT.
|
||||
* @param[out] pJobsResponse The response received from the Jobs service.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*/
|
||||
/* @[declare_jobs_getpendingsync] */
|
||||
AwsIotJobsError_t AwsIotJobs_GetPendingSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs,
|
||||
AwsIotJobsResponse_t * const pJobsResponse );
|
||||
/* @[declare_jobs_getpendingsync] */
|
||||
|
||||
/**
|
||||
* @brief Start the next pending job execution for a Thing and receive an asynchronous
|
||||
* notification when the response arrives.
|
||||
*
|
||||
* This function implements the [StartNextPendingJobExecution]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-startnextpendingjobexecution)
|
||||
* command of the Jobs API, which gets and starts the next pending job execution
|
||||
* for a Thing.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] pUpdateInfo Jobs update parameters.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pStartNextOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Jobs operation
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
|
||||
* queuing the Jobs operation.
|
||||
* @return If this function fails before queuing the Jobs operation, it will return one of:
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
|
||||
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
|
||||
* @return Should the Jobs operation fail, the status will be one of:
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*
|
||||
* @see @ref jobs_function_startnextsync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* // Signature of Jobs callback function.
|
||||
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
|
||||
*
|
||||
* AwsIotJobsOperation_t startNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
|
||||
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
|
||||
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
|
||||
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // Set the request info. The update info generally does not need to be
|
||||
* // changed, as its defaults are suitable.
|
||||
* requestInfo.mqttConnection = _mqttConnection;
|
||||
* requestInfo.pThingName = THING_NAME;
|
||||
* requestInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // Set the callback function to invoke.
|
||||
* callbackInfo.function = _jobsCallback;
|
||||
*
|
||||
* // Queue Jobs START NEXT.
|
||||
* AwsIotJobsError_t startNextResult = AwsIotJobs_StartNextAsync( &requestInfo,
|
||||
* &updateInfo,
|
||||
* 0,
|
||||
* &callbackInfo,
|
||||
* &startNextOperation );
|
||||
*
|
||||
* // START NEXT should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
|
||||
* // _jobsCallback will be invoked once the Jobs response is received.
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_startnextasync] */
|
||||
AwsIotJobsError_t AwsIotJobs_StartNextAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotJobsOperation_t * const pStartNextOperation );
|
||||
/* @[declare_jobs_startnextasync] */
|
||||
|
||||
/**
|
||||
* @brief Start the next pending job execution for a Thing with a timeout for
|
||||
* receiving the response.
|
||||
*
|
||||
* This function queues a Jobs START NEXT, then waits for the result. Internally,
|
||||
* this function is a call to @ref jobs_function_startnextasync followed by
|
||||
* @ref jobs_function_wait. See @ref jobs_function_startnextasync for more information
|
||||
* on the Jobs START NEXT command.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] pUpdateInfo Jobs update parameters.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] timeoutMs If a response is not received within this timeout, this
|
||||
* function returns #AWS_IOT_JOBS_TIMEOUT.
|
||||
* @param[out] pJobsResponse The response received from the Jobs service.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*/
|
||||
/* @[declare_jobs_startnextsync] */
|
||||
AwsIotJobsError_t AwsIotJobs_StartNextSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs,
|
||||
AwsIotJobsResponse_t * const pJobsResponse );
|
||||
/* @[declare_jobs_startnextsync] */
|
||||
|
||||
/**
|
||||
* @brief Get detailed information about a job execution and receive an asynchronous
|
||||
* notification when the response arrives.
|
||||
*
|
||||
* This function implements the [DescribeJobExecution]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-describejobexecution)
|
||||
* command of the Jobs API, which gets detailed information about a job execution.
|
||||
* The description is returned as the `pResponse` member in #AwsIotJobsCallbackParam_t,
|
||||
* or through the #AwsIotJobsResponse_t parameter of @ref jobs_function_wait.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] executionNumber The execution number to describe. Optional; pass
|
||||
* #AWS_IOT_JOBS_NO_EXECUTION_NUMBER to ignore.
|
||||
* @param[in] includeJobDocument Whether the response should include the full
|
||||
* Job document.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pDescribeOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Jobs operation
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
|
||||
* queuing the Jobs operation.
|
||||
* @return If this function fails before queuing the Jobs operation, it will return one of:
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
|
||||
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
|
||||
* @return Should the Jobs operation fail, the status will be one of:
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*
|
||||
* @see @ref jobs_function_describesync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* // Signature of Jobs callback function.
|
||||
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
|
||||
*
|
||||
* AwsIotJobsOperation_t describeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
|
||||
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
|
||||
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // Set the request info.
|
||||
* requestInfo.mqttConnection = _mqttConnection;
|
||||
* requestInfo.pThingName = THING_NAME;
|
||||
* requestInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // Describe the next Job. Or, this may be set to a specific Job ID.
|
||||
* requestInfo.pJobId = AWS_IOT_JOBS_NEXT_JOB;
|
||||
* requestInfo.jobIdLength = AWS_IOT_JOBS_NEXT_JOB_LENGTH;
|
||||
*
|
||||
* // Set the callback function to invoke.
|
||||
* callbackInfo.function = _jobsCallback;
|
||||
*
|
||||
* // Queue Jobs DESCRIBE.
|
||||
* AwsIotJobsError_t describeResult = AwsIotJobs_DescribeAsync( &requestInfo,
|
||||
* AWS_IOT_JOBS_NO_EXECUTION_NUMBER,
|
||||
* false,
|
||||
* 0,
|
||||
* &callbackInfo,
|
||||
* &describeOperation );
|
||||
*
|
||||
* // DESCRIBE should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
|
||||
* // _jobsCallback will be invoked once the Jobs response is received.
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_describeasync] */
|
||||
AwsIotJobsError_t AwsIotJobs_DescribeAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
int32_t executionNumber,
|
||||
bool includeJobDocument,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotJobsOperation_t * const pDescribeOperation );
|
||||
/* @[declare_jobs_describeasync] */
|
||||
|
||||
/**
|
||||
* @brief Get detailed information about a job execution with a timeout for receiving
|
||||
* the response.
|
||||
*
|
||||
* This function queues a Jobs DESCRIBE, then waits for the result. Internally,
|
||||
* this function is a call to @ref jobs_function_describeasync followed by
|
||||
* @ref jobs_function_wait. See @ref jobs_function_describeasync for more information
|
||||
* on the Jobs DESCRIBE command.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] executionNumber The execution number to describe. Optional; pass
|
||||
* #AWS_IOT_JOBS_NO_EXECUTION_NUMBER to ignore.
|
||||
* @param[in] includeJobDocument Whether the response should include the full
|
||||
* Job document.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] timeoutMs If a response is not received within this timeout, this
|
||||
* function returns #AWS_IOT_JOBS_TIMEOUT.
|
||||
* @param[out] pJobsResponse The response received from the Jobs service.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*/
|
||||
/* @[declare_jobs_describesync] */
|
||||
AwsIotJobsError_t AwsIotJobs_DescribeSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
int32_t executionNumber,
|
||||
bool includeJobDocument,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs,
|
||||
AwsIotJobsResponse_t * const pJobsResponse );
|
||||
/* @[declare_jobs_describesync] */
|
||||
|
||||
/**
|
||||
* @brief Update the status of a job execution and receive an asynchronous
|
||||
* notification when the Job update completes.
|
||||
*
|
||||
* This function implements the [UpdateJobExecution]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-updatejobexecution)
|
||||
* command of the Jobs API, which updates the status of a Job execution.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] pUpdateInfo Jobs update parameters.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pUpdateOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Jobs operation
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
|
||||
* queuing the Jobs operation.
|
||||
* @return If this function fails before queuing the Jobs operation, it will return one of:
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
|
||||
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
|
||||
* @return Should the Jobs operation fail, the status will be one of:
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*
|
||||
* @see @ref jobs_function_updatesync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* // Signature of Jobs callback function.
|
||||
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
|
||||
*
|
||||
* AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
|
||||
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
|
||||
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
|
||||
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // Set the request info.
|
||||
* requestInfo.mqttConnection = _mqttConnection;
|
||||
* requestInfo.pThingName = THING_NAME;
|
||||
* requestInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // A Job ID must be set. AWS_IOT_JOBS_NEXT_JOB is not valid for UPDATE.
|
||||
* requestInfo.pJobId = "job-id";
|
||||
* requestInfo.jobIdLength = 6;
|
||||
*
|
||||
* // Set the update info.
|
||||
* updateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;
|
||||
*
|
||||
* // Set the callback function to invoke.
|
||||
* callbackInfo.function = _jobsCallback;
|
||||
*
|
||||
* // Queue Jobs UPDATE.
|
||||
* AwsIotJobsError_t updateResult = AwsIotJobs_UpdateAsync( &requestInfo,
|
||||
* &updateInfo,
|
||||
* 0,
|
||||
* &callbackInfo,
|
||||
* &updateOperation );
|
||||
*
|
||||
* // UPDATE should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
|
||||
* // _jobsCallback will be invoked once the Jobs response is received.
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_updateasync] */
|
||||
AwsIotJobsError_t AwsIotJobs_UpdateAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotJobsOperation_t * const pUpdateOperation );
|
||||
/* @[declare_jobs_updateasync] */
|
||||
|
||||
/**
|
||||
* @brief Update the status of a job execution with a timeout for receiving the
|
||||
* response.
|
||||
*
|
||||
* This function queues a Jobs UPDATE, then waits for the result. Internally,
|
||||
* this function is a call to @ref jobs_function_updateasync followed by
|
||||
* @ref jobs_function_wait. See @ref jobs_function_updateasync for more information
|
||||
* on the Jobs UPDATE command.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request parameters.
|
||||
* @param[in] pUpdateInfo Jobs update parameters.
|
||||
* @param[in] flags Flags which modify the behavior of the Jobs request. See
|
||||
* @ref jobs_constants_flags.
|
||||
* @param[in] timeoutMs If a response is not received within this timeout, this
|
||||
* function returns #AWS_IOT_JOBS_TIMEOUT.
|
||||
* @param[out] pJobsResponse The response received from the Jobs service.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*/
|
||||
/* @[declare_jobs_updatesync] */
|
||||
AwsIotJobsError_t AwsIotJobs_UpdateSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs,
|
||||
AwsIotJobsResponse_t * const pJobsResponse );
|
||||
/* @[declare_jobs_updatesync] */
|
||||
|
||||
/**
|
||||
* @brief Wait for a Jobs operation to complete.
|
||||
*
|
||||
* This function blocks to wait for a [GET PENDING](@ref jobs_function_getpendingasync),
|
||||
* [START NEXT](@ref jobs_function_startnextasync), [DESCRIBE](@ref jobs_function_describeasync),
|
||||
* or [UPDATE](@ref jobs_function_updateasync) operation to complete. These operations are
|
||||
* by default asynchronous; the function calls queue an operation for processing,
|
||||
* and a callback is invoked once the operation is complete.
|
||||
*
|
||||
* To use this function, the flag #AWS_IOT_JOBS_FLAG_WAITABLE must have been
|
||||
* set in the operation's function call. Additionally, this function must always
|
||||
* be called with any waitable operation to clean up resources.
|
||||
*
|
||||
* Regardless of its return value, this function always clean up resources used
|
||||
* by the waitable operation. This means `operation` is invalidated as soon as
|
||||
* this function returns, even if it returns #AWS_IOT_JOBS_TIMEOUT or another
|
||||
* error.
|
||||
*
|
||||
* @param[in] operation Reference to the Jobs operation to wait for. The flag
|
||||
* #AWS_IOT_JOBS_FLAG_WAITABLE must have been set for this operation.
|
||||
* @param[in] timeoutMs How long to wait before returning #AWS_IOT_JOBS_TIMEOUT.
|
||||
* @param[out] pJobsResponse The response received from the Jobs service.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_BAD_RESPONSE
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
|
||||
*
|
||||
* <b>Example</b>:
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
|
||||
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
|
||||
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
|
||||
* AwsIotJobsResponse_t jobsResponse = AWS_IOT_JOBS_RESPONSE_INITIALIZER;
|
||||
*
|
||||
* // Set the request info.
|
||||
* requestInfo.mqttConnection = _mqttConnection;
|
||||
* requestInfo.pThingName = THING_NAME;
|
||||
* requestInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // Set the function used to allocate memory for an incoming response.
|
||||
* requestInfo.mallocResponse = malloc;
|
||||
*
|
||||
* // A Job ID must be set. AWS_IOT_JOBS_NEXT_JOB is not valid for UPDATE.
|
||||
* requestInfo.pJobId = "job-id";
|
||||
* requestInfo.jobIdLength = 6;
|
||||
*
|
||||
* // Set the update info.
|
||||
* updateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;
|
||||
*
|
||||
* // Queue Jobs UPDATE.
|
||||
* AwsIotJobsError_t updateResult = AwsIotJobs_UpdateAsync( &requestInfo,
|
||||
* &updateInfo,
|
||||
* AWS_IOT_JOBS_FLAG_WAITABLE,
|
||||
* NULL,
|
||||
* &updateOperation );
|
||||
*
|
||||
* // UPDATE should have returned AWS_IOT_JOBS_STATUS_PENDING. The call to wait
|
||||
* // returns once the result of the UPDATE is available or the timeout expires.
|
||||
* if( updateResult == AWS_IOT_JOBS_STATUS_PENDING )
|
||||
* {
|
||||
* updateResult = AwsIotJobs_Wait( updateOperation, 5000, &jobsResponse );
|
||||
*
|
||||
* if( updateResult == AWS_IOT_JOBS_SUCCESS )
|
||||
* {
|
||||
* // Jobs operation succeeded. Do something with the Jobs response.
|
||||
*
|
||||
* // Once the Jobs response is no longer needed, free it.
|
||||
* free( jobsResponse.pJobsResponse );
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Jobs operation failed.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_wait] */
|
||||
AwsIotJobsError_t AwsIotJobs_Wait( AwsIotJobsOperation_t operation,
|
||||
uint32_t timeoutMs,
|
||||
AwsIotJobsResponse_t * const pJobsResponse );
|
||||
/* @[declare_jobs_wait] */
|
||||
|
||||
/**
|
||||
* @brief Set a callback to be invoked when the list of pending Jobs changes.
|
||||
*
|
||||
* The Jobs service publishes a [JobExecutionsChanged]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-jobexecutionschanged)
|
||||
* message to the `jobs/notify` topic whenever a Job execution is added to or
|
||||
* removed from the list of pending Job executions for a Thing. The message sent is
|
||||
* useful for monitoring the list of pending Job executions.
|
||||
*
|
||||
* A <i>NOTIFY PENDING</i> callback may be invoked whenever a message is published
|
||||
* to `jobs/notify`. Each Thing may have up to @ref AWS_IOT_JOBS_NOTIFY_CALLBACKS
|
||||
* NOTIFY PENDING callbacks set. This function modifies the NOTIFY PENDING callback
|
||||
* for a specific Thing depending on the `pNotifyPendingCallback` parameter and the
|
||||
* presence of any existing NOTIFY PENDING callback.
|
||||
* - When no existing NOTIFY PENDING callback exists for a specific Thing, a new
|
||||
* callback is added.
|
||||
* - If there is an existing NOTIFY PENDING callback and `pNotifyPendingCallback` is not `NULL`,
|
||||
* then the existing callback function and parameter are replaced with `pNotifyPendingCallback`.
|
||||
* - If there is an existing NOTIFY PENDING callback and `pNotifyPendingCallback` is `NULL`,
|
||||
* then the callback is removed.
|
||||
*
|
||||
* The member @ref AwsIotJobsCallbackInfo_t.oldFunction must be used to select an
|
||||
* already-registered callback function for replacement or removal when @ref
|
||||
* AWS_IOT_JOBS_NOTIFY_CALLBACKS is greater than `1`. When multiple callbacks are
|
||||
* set, all of them will be invoked when a message is received.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for the subscription to `jobs/notify`.
|
||||
* @param[in] pThingName The subscription to `jobs/notify` will be added for
|
||||
* this Thing Name.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
|
||||
* not supported for this function and this parameter is ignored.
|
||||
* @param[in] pNotifyPendingCallback Callback function to invoke for incoming messages.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
*
|
||||
* @see @ref jobs_function_setnotifynextcallback for the function to register callbacks
|
||||
* for next Job changes.
|
||||
*
|
||||
* <b>Example</b>:
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* AwsIotJobsError_t result = AWS_IOT_JOBS_STATUS_PENDING;
|
||||
* AwsIotJobsCallbackInfo_t notifyPendingCallback = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // _jobsCallback will be invoked when any messages are received.
|
||||
* notifyPendingCallback.function = _jobsCallback;
|
||||
*
|
||||
* // Set the NOTIFY PENDING callback for the Thing "Test_device".
|
||||
* result = AwsIotJobs_SetNotifyPendingCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* ¬ifyPendingCallback );
|
||||
*
|
||||
* // Check if the callback was successfully set.
|
||||
* if( status == AWS_IOT_JOBS_SUCCESS )
|
||||
* {
|
||||
* // The callback will now be invoked whenever the list of pending Job
|
||||
* // executions changes.
|
||||
*
|
||||
* // Once the callback is no longer needed, it may be removed by passing
|
||||
* // NULL as the callback function and specifying the function to remove.
|
||||
* notifyPendingCallback.function = NULL;
|
||||
* notifyPendingCallback.oldFunction = _jobsCallback;
|
||||
*
|
||||
* status = AwsIotJobs_SetNotifyPendingCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* ¬ifyPendingCallback );
|
||||
*
|
||||
* // The return value from removing a callback should always be success.
|
||||
* assert( status == AWS_IOT_JOBS_SUCCESS );
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_setnotifypendingcallback] */
|
||||
AwsIotJobsError_t AwsIotJobs_SetNotifyPendingCallback( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pNotifyPendingCallback );
|
||||
/* @[declare_jobs_setnotifypendingcallback] */
|
||||
|
||||
/**
|
||||
* @brief Set a callback to be invoked when the next pending Job changes.
|
||||
*
|
||||
* The Jobs service publishes a [NextJobExecutionChanged]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-nextjobexecutionchanged)
|
||||
* message to the `jobs/notify-next` topic whenever the next Job execution in
|
||||
* the list of pending Job executions changes for a Thing. The message sent is
|
||||
* useful for being notified of changes to the next Job.
|
||||
*
|
||||
* A <i>NOTIFY NEXT</i> callback may be invoked whenever a message is published
|
||||
* to `jobs/notify-next`. Each Thing may have up to @ref AWS_IOT_JOBS_NOTIFY_CALLBACKS
|
||||
* NOTIFY NEXT callbacks set. This function modifies the NOTIFY NEXT callback for
|
||||
* a specific Thing depending on the `pNotifyNextCallback` parameter and the presence
|
||||
* of any existing NOTIFY NEXT callback.
|
||||
* - When no existing NOTIFY NEXT callback exists for a specific Thing, a new
|
||||
* callback is added.
|
||||
* - If there is an existing NOTIFY NEXT callback and `pNotifyNextCallback` is not `NULL`,
|
||||
* then the existing callback function and parameter are replaced with `pNotifyNextCallback`.
|
||||
* - If there is an existing NOTIFY NEXT callback and `pNotifyNextCallback` is `NULL`,
|
||||
* then the callback is removed.
|
||||
*
|
||||
* The member @ref AwsIotJobsCallbackInfo_t.oldFunction must be used to select an
|
||||
* already-registered callback function for replacement or removal when @ref
|
||||
* AWS_IOT_JOBS_NOTIFY_CALLBACKS is greater than `1`. When multiple callbacks are
|
||||
* set, all of them will be invoked when a message is received.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for the subscription to `jobs/notify-next`.
|
||||
* @param[in] pThingName The subscription to `jobs/notify-next` will be added for
|
||||
* this Thing Name.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
|
||||
* not supported for this function and this parameter is ignored.
|
||||
* @param[in] pNotifyNextCallback Callback function to invoke for incoming messages.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* - #AWS_IOT_JOBS_NOT_INITIALIZED
|
||||
* - #AWS_IOT_JOBS_BAD_PARAMETER
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
* - #AWS_IOT_JOBS_TIMEOUT
|
||||
*
|
||||
* @see @ref jobs_function_setnotifypendingcallback for the function to register callbacks
|
||||
* for all pending Job changes.
|
||||
*
|
||||
* <b>Example</b>:
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* AwsIotJobsError_t result = AWS_IOT_JOBS_STATUS_PENDING;
|
||||
* AwsIotJobsCallbackInfo_t notifyNextCallback = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // _jobsCallback will be invoked when any messages are received.
|
||||
* notifyNextCallback.function = _jobsCallback;
|
||||
*
|
||||
* // Set the NOTIFY NEXT callback for the Thing "Test_device".
|
||||
* result = AwsIotJobs_SetNotifyNextCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* ¬ifyNextCallback );
|
||||
*
|
||||
* // Check if the callback was successfully set.
|
||||
* if( status == AWS_IOT_JOBS_SUCCESS )
|
||||
* {
|
||||
* // The callback will now be invoked whenever the next pending Job
|
||||
* // execution changes.
|
||||
*
|
||||
* // Once the callback is no longer needed, it may be removed by passing
|
||||
* // NULL as the callback function and specifying the function to remove.
|
||||
* notifyNextCallback.function = NULL;
|
||||
* notifyNextCallback.oldFunction = _jobsCallback;
|
||||
*
|
||||
* status = AwsIotJobs_SetNotifyNextCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* ¬ifyNextCallback );
|
||||
*
|
||||
* // The return value from removing a callback should always be success.
|
||||
* assert( status == AWS_IOT_JOBS_SUCCESS );
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_jobs_setnotifynextcallback] */
|
||||
AwsIotJobsError_t AwsIotJobs_SetNotifyNextCallback( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pNotifyNextCallback );
|
||||
/* @[declare_jobs_setnotifynextcallback] */
|
||||
|
||||
/**
|
||||
* @brief Remove persistent Jobs operation topic subscriptions.
|
||||
*
|
||||
* Passing the flag @ref AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS to @ref jobs_function_getpendingasync,
|
||||
* @ref jobs_function_startnextasync, @ref jobs_function_describeasync, @ref jobs_function_updateasync,
|
||||
* or their blocking versions causes the Jobs operation topic subscriptions to be
|
||||
* maintained for future calls to the same function. If a persistent subscription for a
|
||||
* Jobs topic are no longer needed, this function may be used to remove it.
|
||||
*
|
||||
* @param[in] pRequestInfo Jobs request info. Only the [pThingName]
|
||||
* (@ref #AwsIotJobsRequestInfo_t.pThingName), [thingNameLength]
|
||||
* (@ref #AwsIotJobsRequestInfo_t.thingNameLength), and [mqttConnection]
|
||||
* (@ref #AwsIotJobsRequestInfo_t.mqttConnection) members need to be set for this
|
||||
* function.
|
||||
* @param[in] flags Flags that determine which subscriptions to remove. Valid values are
|
||||
* the bitwise OR of the following individual flags:
|
||||
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_GET_PENDING_SUBSCRIPTIONS
|
||||
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_START_NEXT_SUBSCRIPTIONS
|
||||
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_DESCRIBE_SUBSCRIPTIONS
|
||||
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
|
||||
*
|
||||
* @return On success:
|
||||
* - #AWS_IOT_JOBS_SUCCESS
|
||||
* @return If an MQTT UNSUBSCRIBE packet cannot be sent, one of the following:
|
||||
* - #AWS_IOT_JOBS_NO_MEMORY
|
||||
* - #AWS_IOT_JOBS_MQTT_ERROR
|
||||
*
|
||||
* @note @ref jobs_function_cleanup removes persistent sessions as well.
|
||||
*
|
||||
* @warning This function is not safe to call with any in-progress operations!
|
||||
* It also does not affect NOTIFY PENDING and NOTIFY NEXT callbacks registered
|
||||
* with @ref jobs_function_setnotifypendingcallback and
|
||||
* @ref jobs_function_setnotifynextcallback, respectively. (See documentation for
|
||||
* those functions on how to remove their callbacks).
|
||||
*/
|
||||
/* @[declare_jobs_removepersistentsubscriptions] */
|
||||
AwsIotJobsError_t AwsIotJobs_RemovePersistentSubscriptions( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
uint32_t flags );
|
||||
/* @[declare_jobs_removepersistentsubscriptions] */
|
||||
|
||||
/*-------------------------- Jobs helper functions --------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Returns a string that describes an #AwsIotJobsError_t.
|
||||
*
|
||||
* Like POSIX's `strerror`, this function returns a string describing a return
|
||||
* code. In this case, the return code is a Jobs library error code, `status`.
|
||||
*
|
||||
* The string returned by this function <b>MUST</b> be treated as read-only: any
|
||||
* attempt to modify its contents may result in a crash. Therefore, this function
|
||||
* is limited to usage in logging.
|
||||
*
|
||||
* @param[in] status The status to describe.
|
||||
*
|
||||
* @return A read-only string that describes `status`.
|
||||
*
|
||||
* @warning The string returned by this function must never be modified.
|
||||
*/
|
||||
/* @[declare_jobs_strerror] */
|
||||
const char * AwsIotJobs_strerror( AwsIotJobsError_t status );
|
||||
/* @[declare_jobs_strerror] */
|
||||
|
||||
/**
|
||||
* @brief Returns a string that describes an #AwsIotJobState_t.
|
||||
*
|
||||
* This function returns a string describing a Job state, `state`.
|
||||
*
|
||||
* The string returned by this function <b>MUST</b> be treated as read-only: any
|
||||
* attempt to modify its contents may result in a crash. Therefore, this function
|
||||
* is limited to usage in logging.
|
||||
*
|
||||
* @param[in] state The job state to describe.
|
||||
*
|
||||
* @return A read-only string that describes `state`.
|
||||
*
|
||||
* @warning The string returned by this function must never be modified.
|
||||
*/
|
||||
/* @[declare_jobs_statename] */
|
||||
const char * AwsIotJobs_StateName( AwsIotJobState_t state );
|
||||
/* @[declare_jobs_statename] */
|
||||
|
||||
#endif /* ifndef AWS_IOT_JOBS_H_ */
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,885 @@
|
|||
/*
|
||||
* AWS IoT Jobs V1.0.0
|
||||
* Copyright (C) 2019 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_jobs_operation.c
|
||||
* @brief Implements functions that process Jobs operations.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* Jobs internal include. */
|
||||
#include "private/aws_iot_jobs_internal.h"
|
||||
|
||||
/* Platform threads include. */
|
||||
#include "platform/iot_threads.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* MQTT include. */
|
||||
#include "iot_mqtt.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
|
||||
|
||||
/**
|
||||
* @brief Printable names for each of the Jobs operations.
|
||||
*/
|
||||
const char * const _pAwsIotJobsOperationNames[] =
|
||||
{
|
||||
"GET PENDING",
|
||||
"START NEXT",
|
||||
"DESCRIBE",
|
||||
"UPDATE",
|
||||
"SET NOTIFY-PENDING",
|
||||
"SET NOTIFY-NEXT"
|
||||
};
|
||||
#endif /* if LIBRARY_LOG_LEVEL > IOT_LOG_NONE */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief First parameter to #_jobsOperation_match.
|
||||
*/
|
||||
typedef struct _operationMatchParams
|
||||
{
|
||||
_jobsOperationType_t type; /**< @brief GET PENDING, START NEXT, DESCRIBE, or UPDATE. */
|
||||
const char * pThingName; /**< @brief Thing Name of Jobs operation. */
|
||||
size_t thingNameLength; /**< @brief Length of #_operationMatchParams_t.pThingName. */
|
||||
const char * pResponse; /**< @brief Jobs response document. */
|
||||
size_t responseLength; /**< @brief Length of #_operationMatchParams_t.pResponse. */
|
||||
} _operationMatchParams_t;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Match a received Jobs response with a Jobs operation awaiting a
|
||||
* response.
|
||||
*
|
||||
* @param[in] pOperationLink Pointer to the link member of the #_jobsOperation_t
|
||||
* to check.
|
||||
* @param[in] pMatch Pointer to an #_operationMatchParams_t.
|
||||
*
|
||||
* @return `true` if `pMatch` matches the received response; `false` otherwise.
|
||||
*/
|
||||
static bool _jobsOperation_match( const IotLink_t * pOperationLink,
|
||||
void * pMatch );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Jobs response is received for Jobs GET PENDING.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _getPendingCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Jobs response is received for a Jobs START NEXT.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _startNextCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Jobs response is received for Jobs DESCRIBE.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _describeCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Invoked when a Jobs response is received for a Jobs UPDATE.
|
||||
*
|
||||
* @param[in] pArgument Ignored.
|
||||
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _updateCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Common function for processing received Jobs responses.
|
||||
*
|
||||
* @param[in] type GET PENDING, START NEXT, DESCRIBE, or UPDATE.
|
||||
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
|
||||
*/
|
||||
static void _commonOperationCallback( _jobsOperationType_t type,
|
||||
IotMqttCallbackParam_t * pMessage );
|
||||
|
||||
/**
|
||||
* @brief Notify of a completed Jobs operation.
|
||||
*
|
||||
* @param[in] pOperation The operation which completed.
|
||||
*
|
||||
* Depending on the parameters passed to a user-facing Jobs function, the
|
||||
* notification will cause @ref jobs_function_wait to return or invoke a
|
||||
* user-provided callback.
|
||||
*/
|
||||
static void _notifyCompletion( _jobsOperation_t * pOperation );
|
||||
|
||||
/**
|
||||
* @brief Get a Jobs subscription to use with a Jobs operation.
|
||||
*
|
||||
* This function may use an existing Jobs subscription, or it may allocate a
|
||||
* new one.
|
||||
*
|
||||
* @param[in] pRequestInfo Common Jobs request parameters.
|
||||
* @param[in] pTopicBuffer Contains the topic to use for subscribing.
|
||||
* @param[in] operationTopicLength The length of the base topic in `pTopicBuffer`.
|
||||
* @param[in] pOperation Jobs operation that needs a subscription.
|
||||
* @param[out] pFreeTopicBuffer Whether the caller may free `pTopicBuffer`
|
||||
* (which may be assigned to a subscription).
|
||||
*
|
||||
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY
|
||||
*/
|
||||
static AwsIotJobsError_t _findSubscription( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
_jobsOperation_t * pOperation,
|
||||
bool * pFreeTopicBuffer );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief List of active Jobs operations awaiting a response from the Jobs
|
||||
* service.
|
||||
*/
|
||||
IotListDouble_t _AwsIotJobsPendingOperations = { 0 };
|
||||
|
||||
/**
|
||||
* @brief Protects #_AwsIotJobsPendingOperations from concurrent access.
|
||||
*/
|
||||
IotMutex_t _AwsIotJobsPendingOperationsMutex;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _jobsOperation_match( const IotLink_t * pOperationLink,
|
||||
void * pMatch )
|
||||
{
|
||||
/* Because this function is called from a container function, the given link
|
||||
* must never be NULL. */
|
||||
AwsIotJobs_Assert( pOperationLink != NULL );
|
||||
|
||||
_jobsOperation_t * pOperation = IotLink_Container( _jobsOperation_t,
|
||||
pOperationLink,
|
||||
link );
|
||||
_operationMatchParams_t * pParam = ( _operationMatchParams_t * ) pMatch;
|
||||
_jobsSubscription_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 );
|
||||
|
||||
if( match == true )
|
||||
{
|
||||
IotLogDebug( "Verifying client tokens for Jobs %s.",
|
||||
_pAwsIotJobsOperationNames[ pParam->type ] );
|
||||
|
||||
/* Check the response for a client token. */
|
||||
match = AwsIot_GetClientToken( pParam->pResponse,
|
||||
pParam->responseLength,
|
||||
&pClientToken,
|
||||
&clientTokenLength );
|
||||
|
||||
/* If the response contains a client token, check for a match. */
|
||||
if( match == true )
|
||||
{
|
||||
match = ( pOperation->clientTokenLength == clientTokenLength ) &&
|
||||
( strncmp( pOperation->pClientToken, pClientToken, clientTokenLength ) == 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "Received a Jobs %s response with no client token. "
|
||||
"This is possibly a response to a bad JSON document:\r\n%.*s",
|
||||
_pAwsIotJobsOperationNames[ pParam->type ],
|
||||
pParam->responseLength,
|
||||
pParam->pResponse );
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _getPendingCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( JOBS_GET_PENDING, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _startNextCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( JOBS_START_NEXT, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _describeCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( JOBS_DESCRIBE, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _updateCallback( void * pArgument,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
/* Silence warnings about unused parameter. */
|
||||
( void ) pArgument;
|
||||
|
||||
_commonOperationCallback( JOBS_UPDATE, pMessage );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _commonOperationCallback( _jobsOperationType_t type,
|
||||
IotMqttCallbackParam_t * pMessage )
|
||||
{
|
||||
_jobsOperation_t * pOperation = NULL;
|
||||
IotLink_t * pOperationLink = NULL;
|
||||
_operationMatchParams_t param = { 0 };
|
||||
AwsIotStatus_t status = AWS_IOT_UNKNOWN;
|
||||
uint32_t flags = 0;
|
||||
|
||||
/* Set operation type and response. */
|
||||
param.type = type;
|
||||
param.pResponse = pMessage->u.message.info.pPayload;
|
||||
param.responseLength = 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( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
|
||||
/* Search for a matching pending operation. */
|
||||
pOperationLink = IotListDouble_FindFirstMatch( &_AwsIotJobsPendingOperations,
|
||||
NULL,
|
||||
_jobsOperation_match,
|
||||
¶m );
|
||||
|
||||
/* Find and remove the first Jobs operation of the given type. */
|
||||
if( pOperationLink == NULL )
|
||||
{
|
||||
/* Operation is not pending. It may have already been processed. Return
|
||||
* without doing anything */
|
||||
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
|
||||
IotLogWarn( "Jobs %s callback received an unknown operation.",
|
||||
_pAwsIotJobsOperationNames[ type ] );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
else
|
||||
{
|
||||
pOperation = IotLink_Container( _jobsOperation_t, pOperationLink, link );
|
||||
|
||||
/* Copy the flags from the Jobs operation. */
|
||||
flags = pOperation->flags;
|
||||
|
||||
/* Remove a non-waitable operation from the pending operation list.
|
||||
* Waitable operations are removed by the Wait function. */
|
||||
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == 0 )
|
||||
{
|
||||
IotListDouble_Remove( &( pOperation->link ) );
|
||||
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* 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:
|
||||
case AWS_IOT_REJECTED:
|
||||
_AwsIotJobs_ParseResponse( status,
|
||||
pMessage->u.message.info.pPayload,
|
||||
pMessage->u.message.info.payloadLength,
|
||||
pOperation );
|
||||
break;
|
||||
|
||||
default:
|
||||
IotLogWarn( "Unknown status for %s of %.*s Jobs. Ignoring message.",
|
||||
_pAwsIotJobsOperationNames[ type ],
|
||||
pOperation->pSubscription->thingNameLength,
|
||||
pOperation->pSubscription->pThingName );
|
||||
|
||||
pOperation->status = AWS_IOT_JOBS_BAD_RESPONSE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_notifyCompletion( pOperation );
|
||||
|
||||
/* For waitable operations, unlock the pending operation list mutex to allow
|
||||
* the Wait function to run. */
|
||||
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
|
||||
{
|
||||
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
}
|
||||
|
||||
/* This function has no return value and no cleanup, but uses the cleanup
|
||||
* label to exit on error. */
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _notifyCompletion( _jobsOperation_t * pOperation )
|
||||
{
|
||||
AwsIotJobsCallbackParam_t callbackParam = { .callbackType = ( AwsIotJobsCallbackType_t ) 0 };
|
||||
_jobsSubscription_t * pSubscription = pOperation->pSubscription,
|
||||
* pRemovedSubscription = NULL;
|
||||
|
||||
/* If the operation is waiting, post to its wait semaphore and return. */
|
||||
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
|
||||
{
|
||||
IotSemaphore_Post( &( pOperation->notify.waitSemaphore ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Decrement the reference count. This also removes subscriptions if the
|
||||
* count reaches 0. */
|
||||
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
|
||||
_AwsIotJobs_DecrementReferences( pOperation,
|
||||
pSubscription->pTopicBuffer,
|
||||
&pRemovedSubscription );
|
||||
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
|
||||
|
||||
/* Set the subscription pointer used for the user callback based on whether
|
||||
* a subscription was removed from the list. */
|
||||
if( pRemovedSubscription != NULL )
|
||||
{
|
||||
pSubscription = pRemovedSubscription;
|
||||
}
|
||||
|
||||
AwsIotJobs_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 = ( AwsIotJobsCallbackType_t ) pOperation->type;
|
||||
callbackParam.mqttConnection = pOperation->mqttConnection;
|
||||
callbackParam.pThingName = pSubscription->pThingName;
|
||||
callbackParam.thingNameLength = pSubscription->thingNameLength;
|
||||
callbackParam.u.operation.result = pOperation->status;
|
||||
callbackParam.u.operation.reference = pOperation;
|
||||
callbackParam.u.operation.pResponse = pOperation->pJobsResponse;
|
||||
callbackParam.u.operation.responseLength = pOperation->jobsResponseLength;
|
||||
|
||||
pOperation->notify.callback.function( pOperation->notify.callback.pCallbackContext,
|
||||
&callbackParam );
|
||||
}
|
||||
|
||||
/* Destroy a removed subscription. */
|
||||
if( pRemovedSubscription != NULL )
|
||||
{
|
||||
_AwsIotJobs_DestroySubscription( pRemovedSubscription );
|
||||
}
|
||||
|
||||
_AwsIotJobs_DestroyOperation( pOperation );
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static AwsIotJobsError_t _findSubscription( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
_jobsOperation_t * pOperation,
|
||||
bool * pFreeTopicBuffer )
|
||||
{
|
||||
AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
|
||||
_jobsSubscription_t * pSubscription = NULL;
|
||||
|
||||
/* Lookup table for Jobs operation callbacks. */
|
||||
const AwsIotMqttCallbackFunction_t jobsCallbacks[ JOBS_OPERATION_COUNT ] =
|
||||
{
|
||||
_getPendingCallback,
|
||||
_startNextCallback,
|
||||
_describeCallback,
|
||||
_updateCallback
|
||||
};
|
||||
|
||||
/* Lock the subscriptions mutex for exclusive access. */
|
||||
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
|
||||
|
||||
/* Check for an existing subscription. This function will attempt to allocate
|
||||
* a new subscription if not found. */
|
||||
pSubscription = _AwsIotJobs_FindSubscription( pRequestInfo->pThingName,
|
||||
pRequestInfo->thingNameLength,
|
||||
true );
|
||||
|
||||
if( pSubscription == NULL )
|
||||
{
|
||||
status = AWS_IOT_JOBS_NO_MEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Ensure that the subscription Thing Name matches. */
|
||||
AwsIotJobs_Assert( pSubscription != NULL );
|
||||
AwsIotJobs_Assert( pSubscription->thingNameLength == pRequestInfo->thingNameLength );
|
||||
AwsIotJobs_Assert( strncmp( pSubscription->pThingName,
|
||||
pRequestInfo->pThingName,
|
||||
pRequestInfo->thingNameLength ) == 0 );
|
||||
|
||||
/* Set the subscription object for the Jobs 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 Jobs operation's
|
||||
* subscriptions. */
|
||||
status = _AwsIotJobs_IncrementReferences( pOperation,
|
||||
pTopicBuffer,
|
||||
operationTopicLength,
|
||||
jobsCallbacks[ pOperation->type ] );
|
||||
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
/* Failed to add subscriptions for a Jobs operation. The reference
|
||||
* count was not incremented. Check if this subscription should be
|
||||
* deleted. */
|
||||
_AwsIotJobs_RemoveSubscription( pSubscription, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the Jobs subscription list mutex. */
|
||||
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotJobsError_t _AwsIotJobs_CreateOperation( _jobsOperationType_t type,
|
||||
const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const _jsonRequestContents_t * pRequestContents,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
|
||||
_jobsOperation_t ** pNewOperation )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
|
||||
size_t operationSize = sizeof( _jobsOperation_t );
|
||||
_jobsOperation_t * pOperation = NULL;
|
||||
|
||||
IotLogDebug( "Creating operation record for Jobs %s.",
|
||||
_pAwsIotJobsOperationNames[ type ] );
|
||||
|
||||
/* The Job ID must be saved for DESCRIBE and UPDATE operations. */
|
||||
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
|
||||
{
|
||||
/* Ensure a valid Job ID is provided. */
|
||||
AwsIotJobs_Assert( pRequestInfo->pJobId != NULL );
|
||||
AwsIotJobs_Assert( pRequestInfo->jobIdLength > 1 );
|
||||
AwsIotJobs_Assert( pRequestInfo->jobIdLength <= JOBS_MAX_ID_LENGTH );
|
||||
|
||||
operationSize += pRequestInfo->jobIdLength;
|
||||
}
|
||||
|
||||
/* Allocate memory for a new Jobs operation. */
|
||||
pOperation = AwsIotJobs_MallocOperation( operationSize );
|
||||
|
||||
if( pOperation == NULL )
|
||||
{
|
||||
IotLogError( "Failed to allocate memory for Jobs %s.",
|
||||
_pAwsIotJobsOperationNames[ type ] );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
|
||||
}
|
||||
|
||||
/* Clear the operation data. */
|
||||
( void ) memset( pOperation, 0x00, sizeof( _jobsOperation_t ) );
|
||||
|
||||
/* Set the remaining common members of the Jobs operation. */
|
||||
pOperation->type = type;
|
||||
pOperation->flags = flags;
|
||||
pOperation->status = AWS_IOT_JOBS_STATUS_PENDING;
|
||||
pOperation->mallocResponse = pRequestInfo->mallocResponse;
|
||||
|
||||
/* Save the Job ID for DESCRIBE and UPDATE operations. */
|
||||
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
|
||||
{
|
||||
pOperation->jobIdLength = pRequestInfo->jobIdLength;
|
||||
|
||||
( void ) memcpy( pOperation->pJobId, pRequestInfo->pJobId, pRequestInfo->jobIdLength );
|
||||
}
|
||||
|
||||
/* Generate a Jobs request document. */
|
||||
status = _AwsIotJobs_GenerateJsonRequest( type,
|
||||
pRequestInfo,
|
||||
pRequestContents,
|
||||
pOperation );
|
||||
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Check if the waitable flag is set. If it is, create a semaphore to
|
||||
* wait on. */
|
||||
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
|
||||
{
|
||||
if( IotSemaphore_Create( &( pOperation->notify.waitSemaphore ), 0, 1 ) == false )
|
||||
{
|
||||
IotLogError( "Failed to create semaphore for waitable Jobs %s.",
|
||||
_pAwsIotJobsOperationNames[ type ] );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_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;
|
||||
}
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
/* Clean up on error. */
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
if( pOperation != NULL )
|
||||
{
|
||||
if( pOperation->pJobsRequest != NULL )
|
||||
{
|
||||
AwsIotJobs_FreeString( ( void * ) ( pOperation->pJobsRequest ) );
|
||||
}
|
||||
|
||||
AwsIotJobs_FreeOperation( pOperation );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the output parameter. */
|
||||
*pNewOperation = pOperation;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotJobs_DestroyOperation( void * pData )
|
||||
{
|
||||
_jobsOperation_t * pOperation = ( _jobsOperation_t * ) pData;
|
||||
|
||||
AwsIotJobs_Assert( pOperation != NULL );
|
||||
|
||||
IotLogDebug( "Destroying Jobs operation %s.",
|
||||
_pAwsIotJobsOperationNames[ pOperation->type ] );
|
||||
|
||||
/* Check if a wait semaphore was created for this operation. */
|
||||
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
|
||||
{
|
||||
/* Destroy the wait semaphore */
|
||||
IotSemaphore_Destroy( &( pOperation->notify.waitSemaphore ) );
|
||||
}
|
||||
|
||||
/* Free any Jobs request documents. */
|
||||
if( pOperation->pJobsRequest != NULL )
|
||||
{
|
||||
AwsIotJobs_Assert( pOperation->jobsRequestLength > 0 );
|
||||
|
||||
AwsIotJobs_FreeString( ( void * ) ( pOperation->pJobsRequest ) );
|
||||
}
|
||||
|
||||
/* Free the memory used to hold operation data. */
|
||||
AwsIotJobs_FreeOperation( pOperation );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotJobsError_t _AwsIotJobs_GenerateJobsTopic( _jobsOperationType_t type,
|
||||
const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
char ** pTopicBuffer,
|
||||
uint16_t * pOperationTopicLength )
|
||||
{
|
||||
AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
|
||||
AwsIotTopicInfo_t topicInfo = { 0 };
|
||||
char pJobOperationName[ JOBS_LONGEST_SUFFIX_LENGTH ] = { 0 };
|
||||
uint16_t operationNameLength = 0;
|
||||
|
||||
/* Lookup table for Jobs operation strings. */
|
||||
const char * const pOperationString[ JOBS_OPERATION_COUNT ] =
|
||||
{
|
||||
JOBS_GET_PENDING_OPERATION_STRING, /* Jobs get pending operation. */
|
||||
JOBS_START_NEXT_OPERATION_STRING, /* Jobs start next operation. */
|
||||
JOBS_DESCRIBE_OPERATION_STRING, /* Jobs describe operation */
|
||||
JOBS_UPDATE_OPERATION_STRING /* Jobs update operation. */
|
||||
};
|
||||
|
||||
/* Lookup table for Jobs operation string lengths. */
|
||||
const uint16_t pOperationStringLength[ JOBS_OPERATION_COUNT ] =
|
||||
{
|
||||
JOBS_GET_PENDING_OPERATION_STRING_LENGTH, /* Jobs get pending operation */
|
||||
JOBS_START_NEXT_OPERATION_STRING_LENGTH, /* Jobs start next operation. */
|
||||
JOBS_DESCRIBE_OPERATION_STRING_LENGTH, /* Jobs describe operation */
|
||||
JOBS_UPDATE_OPERATION_STRING_LENGTH /* Jobs update operation. */
|
||||
};
|
||||
|
||||
/* Ensure type is valid. */
|
||||
AwsIotJobs_Assert( ( type == JOBS_GET_PENDING ) || ( type == JOBS_START_NEXT ) ||
|
||||
( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) );
|
||||
|
||||
/* Set the members needed to generate an operation topic. */
|
||||
topicInfo.pThingName = pRequestInfo->pThingName;
|
||||
topicInfo.thingNameLength = pRequestInfo->thingNameLength;
|
||||
topicInfo.longestSuffixLength = JOBS_LONGEST_SUFFIX_LENGTH;
|
||||
topicInfo.mallocString = AwsIotJobs_MallocString;
|
||||
|
||||
/* Job operations that require a Job ID require additional processing to
|
||||
* create an operation name with the Job ID. */
|
||||
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
|
||||
{
|
||||
/* Ensure Job ID length is valid. */
|
||||
AwsIotJobs_Assert( pRequestInfo->jobIdLength > 0 );
|
||||
AwsIotJobs_Assert( pRequestInfo->jobIdLength <= JOBS_MAX_ID_LENGTH );
|
||||
|
||||
/* Construct the Jobs operation name with the Job ID. */
|
||||
( void ) memcpy( pJobOperationName, "/jobs/", 6 );
|
||||
operationNameLength = 6;
|
||||
|
||||
( void ) memcpy( pJobOperationName + operationNameLength,
|
||||
pRequestInfo->pJobId,
|
||||
pRequestInfo->jobIdLength );
|
||||
operationNameLength = ( uint16_t ) ( pRequestInfo->jobIdLength + operationNameLength );
|
||||
|
||||
( void ) memcpy( pJobOperationName + operationNameLength,
|
||||
pOperationString[ type ],
|
||||
pOperationStringLength[ type ] );
|
||||
operationNameLength = ( uint16_t ) ( operationNameLength + pOperationStringLength[ type ] );
|
||||
|
||||
topicInfo.pOperationName = pJobOperationName;
|
||||
topicInfo.operationNameLength = operationNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
topicInfo.pOperationName = pOperationString[ type ];
|
||||
topicInfo.operationNameLength = pOperationStringLength[ type ];
|
||||
}
|
||||
|
||||
if( AwsIot_GenerateOperationTopic( &topicInfo,
|
||||
pTopicBuffer,
|
||||
pOperationTopicLength ) == false )
|
||||
{
|
||||
status = AWS_IOT_JOBS_NO_MEMORY;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotJobsError_t _AwsIotJobs_ProcessOperation( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
_jobsOperation_t * pOperation )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
|
||||
char * pTopicBuffer = NULL;
|
||||
uint16_t operationTopicLength = 0;
|
||||
bool freeTopicBuffer = true;
|
||||
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
|
||||
IotMqttError_t publishStatus = IOT_MQTT_STATUS_PENDING;
|
||||
|
||||
IotLogDebug( "Processing Jobs operation %s for Thing %.*s.",
|
||||
_pAwsIotJobsOperationNames[ pOperation->type ],
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName );
|
||||
|
||||
/* Set the operation's MQTT connection. */
|
||||
pOperation->mqttConnection = pRequestInfo->mqttConnection;
|
||||
|
||||
/* Generate the operation topic buffer. */
|
||||
status = _AwsIotJobs_GenerateJobsTopic( pOperation->type,
|
||||
pRequestInfo,
|
||||
&pTopicBuffer,
|
||||
&operationTopicLength );
|
||||
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
IotLogError( "No memory for Jobs operation topic buffer." );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Get a subscription object for this Jobs operation. */
|
||||
status = _findSubscription( pRequestInfo,
|
||||
pTopicBuffer,
|
||||
operationTopicLength,
|
||||
pOperation,
|
||||
&freeTopicBuffer );
|
||||
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
/* No subscription was found and no subscription could be allocated. */
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Set the members for PUBLISH retry. */
|
||||
publishInfo.qos = pRequestInfo->qos;
|
||||
publishInfo.retryLimit = pRequestInfo->retryLimit;
|
||||
publishInfo.retryMs = pRequestInfo->retryMs;
|
||||
|
||||
/* Set the payload as the Jobs request. */
|
||||
publishInfo.pPayload = pOperation->pJobsRequest;
|
||||
publishInfo.payloadLength = pOperation->jobsRequestLength;
|
||||
|
||||
/* Set the operation topic name. */
|
||||
publishInfo.pTopicName = pTopicBuffer;
|
||||
publishInfo.topicNameLength = operationTopicLength;
|
||||
|
||||
IotLogDebug( "Jobs %s message will be published to topic %.*s",
|
||||
_pAwsIotJobsOperationNames[ pOperation->type ],
|
||||
publishInfo.topicNameLength,
|
||||
publishInfo.pTopicName );
|
||||
|
||||
/* Add Jobs operation to the pending operations list. */
|
||||
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
IotListDouble_InsertHead( &( _AwsIotJobsPendingOperations ),
|
||||
&( pOperation->link ) );
|
||||
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
|
||||
/* Publish to the Jobs topic name. */
|
||||
publishStatus = IotMqtt_PublishSync( pOperation->mqttConnection,
|
||||
&publishInfo,
|
||||
0,
|
||||
_AwsIotJobsMqttTimeoutMs );
|
||||
|
||||
if( publishStatus != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IotLogError( "Failed to publish MQTT message to %s %.*s Jobs, error %s.",
|
||||
_pAwsIotJobsOperationNames[ pOperation->type ],
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName,
|
||||
IotMqtt_strerror( publishStatus ) );
|
||||
|
||||
/* Convert the MQTT "NO MEMORY" error to a Jobs "NO MEMORY" error. */
|
||||
if( publishStatus == IOT_MQTT_NO_MEMORY )
|
||||
{
|
||||
status = AWS_IOT_JOBS_NO_MEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = AWS_IOT_JOBS_MQTT_ERROR;
|
||||
}
|
||||
|
||||
/* If the "keep subscriptions" flag is not set, decrement the reference
|
||||
* count. */
|
||||
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
|
||||
{
|
||||
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
|
||||
_AwsIotJobs_DecrementReferences( pOperation,
|
||||
pTopicBuffer,
|
||||
NULL );
|
||||
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
|
||||
}
|
||||
|
||||
/* Remove Jobs operation from the pending operations list. */
|
||||
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
IotListDouble_Remove( &( pOperation->link ) );
|
||||
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Jobs %s PUBLISH message successfully sent.",
|
||||
_pAwsIotJobsOperationNames[ 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 ) )
|
||||
{
|
||||
AwsIotJobs_FreeString( pTopicBuffer );
|
||||
}
|
||||
|
||||
/* Destroy the Jobs operation on failure. */
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
_AwsIotJobs_DestroyOperation( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert successful return code to "status pending", as the Jobs
|
||||
* library is now waiting for a response from the service. */
|
||||
status = AWS_IOT_JOBS_STATUS_PENDING;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* AWS IoT Jobs V1.0.0
|
||||
* Copyright (C) 2019 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_jobs_static_memory.c
|
||||
* @brief Implementation of Jobs 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"
|
||||
|
||||
/* Jobs internal include. */
|
||||
#include "private/aws_iot_jobs_internal.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @cond DOXYGEN_IGNORE
|
||||
* Doxygen should ignore this section.
|
||||
*
|
||||
* Provide default values for undefined configuration constants.
|
||||
*/
|
||||
#ifndef AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS
|
||||
#define AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ( 10 )
|
||||
#endif
|
||||
#ifndef AWS_IOT_JOBS_SUBSCRIPTIONS
|
||||
#define AWS_IOT_JOBS_SUBSCRIPTIONS ( 2 )
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
/* Validate static memory configuration settings. */
|
||||
#if AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS <= 0
|
||||
#error "AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS cannot be 0 or negative."
|
||||
#endif
|
||||
#if AWS_IOT_JOBS_SUBSCRIPTIONS <= 0
|
||||
#error "AWS_IOT_JOBS_SUBSCRIPTIONS cannot be 0 or negative."
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The size of a static memory Jobs operation.
|
||||
*
|
||||
* Since the pJobId member of #_jobsOperation_t is variable-length,
|
||||
* the constant `JOBS_MAX_ID_LENGTH` is used for the length of
|
||||
* #_jobsOperation_t.pJobId.
|
||||
*/
|
||||
#define JOBS_OPERATION_SIZE ( sizeof( _jobsOperation_t ) + JOBS_MAX_ID_LENGTH )
|
||||
|
||||
/**
|
||||
* @brief The size of a static memory Jobs subscription.
|
||||
*
|
||||
* Since the pThingName member of #_jobsSubscription_t is variable-length,
|
||||
* the constant `AWS_IOT_MAX_THING_NAME_LENGTH` is used for the length of
|
||||
* #_jobsSubscription_t.pThingName.
|
||||
*/
|
||||
#define JOBS_SUBSCRIPTION_SIZE ( sizeof( _jobsSubscription_t ) + AWS_IOT_MAX_THING_NAME_LENGTH )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Static memory buffers and flags, allocated and zeroed at compile-time.
|
||||
*/
|
||||
static uint32_t _pInUseJobsOperations[ AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ] = { 0U }; /**< @brief Jobs operation in-use flags. */
|
||||
static char _pJobsOperations[ AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ][ JOBS_OPERATION_SIZE ] = { { 0 } }; /**< @brief Jobs operations. */
|
||||
|
||||
static uint32_t _pInUseJobsSubscriptions[ AWS_IOT_JOBS_SUBSCRIPTIONS ] = { 0U }; /**< @brief Jobs subscription in-use flags. */
|
||||
static char _pJobsSubscriptions[ AWS_IOT_JOBS_SUBSCRIPTIONS ][ JOBS_SUBSCRIPTION_SIZE ] = { { 0 } }; /**< @brief Jobs subscriptions. */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void * AwsIotJobs_MallocOperation( size_t size )
|
||||
{
|
||||
int32_t freeIndex = -1;
|
||||
void * pNewOperation = NULL;
|
||||
|
||||
/* Check size argument. */
|
||||
if( size <= JOBS_OPERATION_SIZE )
|
||||
{
|
||||
/* Find a free Jobs operation. */
|
||||
freeIndex = IotStaticMemory_FindFree( _pInUseJobsOperations,
|
||||
AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS );
|
||||
|
||||
if( freeIndex != -1 )
|
||||
{
|
||||
pNewOperation = &( _pJobsOperations[ freeIndex ] );
|
||||
}
|
||||
}
|
||||
|
||||
return pNewOperation;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void AwsIotJobs_FreeOperation( void * ptr )
|
||||
{
|
||||
/* Return the in-use Jobs operation. */
|
||||
IotStaticMemory_ReturnInUse( ptr,
|
||||
_pJobsOperations,
|
||||
_pInUseJobsOperations,
|
||||
AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS,
|
||||
JOBS_OPERATION_SIZE );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void * AwsIotJobs_MallocSubscription( size_t size )
|
||||
{
|
||||
int32_t freeIndex = -1;
|
||||
void * pNewSubscription = NULL;
|
||||
|
||||
if( size <= JOBS_SUBSCRIPTION_SIZE )
|
||||
{
|
||||
/* Get the index of a free Jobs subscription. */
|
||||
freeIndex = IotStaticMemory_FindFree( _pInUseJobsSubscriptions,
|
||||
AWS_IOT_JOBS_SUBSCRIPTIONS );
|
||||
|
||||
if( freeIndex != -1 )
|
||||
{
|
||||
pNewSubscription = &( _pJobsSubscriptions[ freeIndex ][ 0 ] );
|
||||
}
|
||||
}
|
||||
|
||||
return pNewSubscription;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void AwsIotJobs_FreeSubscription( void * ptr )
|
||||
{
|
||||
/* Return the in-use Jobs subscription. */
|
||||
IotStaticMemory_ReturnInUse( ptr,
|
||||
_pJobsSubscriptions,
|
||||
_pInUseJobsSubscriptions,
|
||||
AWS_IOT_JOBS_SUBSCRIPTIONS,
|
||||
JOBS_SUBSCRIPTION_SIZE );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* AWS IoT Jobs V1.0.0
|
||||
* Copyright (C) 2019 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_jobs_subscription.c
|
||||
* @brief Implements functions for interacting with the Jobs library's
|
||||
* subscription list.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* Jobs internal include. */
|
||||
#include "private/aws_iot_jobs_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 #_jobsSubscription_t by Thing Name.
|
||||
*
|
||||
* @param[in] pSubscriptionLink Pointer to the link member of a #_jobsSubscription_t
|
||||
* containing the Thing Name to check.
|
||||
* @param[in] pMatch Pointer to a `AwsIotThingName_t`.
|
||||
*
|
||||
* @return `true` if the Thing Names match; `false` otherwise.
|
||||
*/
|
||||
static bool _jobsSubscription_match( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief List of active Jobs subscriptions objects.
|
||||
*/
|
||||
IotListDouble_t _AwsIotJobsSubscriptions = { 0 };
|
||||
|
||||
/**
|
||||
* @brief Protects #_AwsIotJobsSubscriptions from concurrent access.
|
||||
*/
|
||||
IotMutex_t _AwsIotJobsSubscriptionsMutex;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _jobsSubscription_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. */
|
||||
AwsIotJobs_Assert( pSubscriptionLink != NULL );
|
||||
|
||||
const _jobsSubscription_t * pSubscription = IotLink_Container( _jobsSubscription_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;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
_jobsSubscription_t * _AwsIotJobs_FindSubscription( const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
bool createIfNotFound )
|
||||
{
|
||||
_jobsSubscription_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( &( _AwsIotJobsSubscriptions ),
|
||||
NULL,
|
||||
_jobsSubscription_match,
|
||||
&thingName );
|
||||
|
||||
/* Check if a subscription was found. */
|
||||
if( pSubscriptionLink == NULL )
|
||||
{
|
||||
if( createIfNotFound == true )
|
||||
{
|
||||
/* No subscription found. Allocate a new subscription. */
|
||||
pSubscription = AwsIotJobs_MallocSubscription( sizeof( _jobsSubscription_t ) + thingNameLength );
|
||||
|
||||
if( pSubscription != NULL )
|
||||
{
|
||||
/* Clear the new subscription. */
|
||||
( void ) memset( pSubscription, 0x00, sizeof( _jobsSubscription_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( &( _AwsIotJobsSubscriptions ),
|
||||
&( pSubscription->link ) );
|
||||
|
||||
IotLogDebug( "Created new Jobs subscriptions object for %.*s.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogError( "Failed to allocate memory for %.*s Jobs subscriptions.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Found existing Jobs subscriptions object for %.*s.",
|
||||
thingNameLength,
|
||||
pThingName );
|
||||
|
||||
pSubscription = IotLink_Container( _jobsSubscription_t, pSubscriptionLink, link );
|
||||
}
|
||||
|
||||
return pSubscription;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotJobs_RemoveSubscription( _jobsSubscription_t * pSubscription,
|
||||
_jobsSubscription_t ** pRemovedSubscription )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
int32_t i = 0, callbackIndex = 0;
|
||||
|
||||
IotLogDebug( "Checking if subscription object for %.*s can be removed.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
/* Check for active operations. If any Jobs operation's subscription
|
||||
* reference count is not 0, then the subscription cannot be removed. */
|
||||
for( i = 0; i < JOBS_OPERATION_COUNT; i++ )
|
||||
{
|
||||
if( pSubscription->operationReferences[ i ] > 0 )
|
||||
{
|
||||
IotLogDebug( "Reference count %ld for %.*s subscription object. "
|
||||
"Subscription cannot be removed yet.",
|
||||
( long int ) pSubscription->operationReferences[ i ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else if( pSubscription->operationReferences[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
IotLogDebug( "Subscription object for %.*s has persistent subscriptions. "
|
||||
"Subscription will not be removed.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for active subscriptions. If any Jobs callbacks are active, then the
|
||||
* subscription cannot be removed. */
|
||||
if( pSubscription->callbackReferences > 0 )
|
||||
{
|
||||
IotLogDebug( "Notify callbacks are using %.*s subscription object. "
|
||||
"Subscription cannot be removed yet.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
|
||||
for( i = 0; i < JOBS_CALLBACK_COUNT; i++ )
|
||||
{
|
||||
for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )
|
||||
{
|
||||
if( pSubscription->callbacks[ i ][ callbackIndex ].function != NULL )
|
||||
{
|
||||
IotLogDebug( "Found active Jobs %s callback for %.*s subscription object. "
|
||||
"Subscription cannot be removed yet.",
|
||||
_pAwsIotJobsCallbackNames[ i ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the subscription if unused. */
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
if( status == true )
|
||||
{
|
||||
/* No Jobs operation subscription references or active Jobs 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
|
||||
{
|
||||
_AwsIotJobs_DestroySubscription( pSubscription );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotJobs_DestroySubscription( void * pData )
|
||||
{
|
||||
_jobsSubscription_t * pSubscription = ( _jobsSubscription_t * ) pData;
|
||||
|
||||
/* Free the topic buffer if allocated. */
|
||||
if( pSubscription->pTopicBuffer != NULL )
|
||||
{
|
||||
AwsIotJobs_FreeString( pSubscription->pTopicBuffer );
|
||||
}
|
||||
|
||||
/* Free memory used by subscription. */
|
||||
AwsIotJobs_FreeSubscription( pSubscription );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotJobsError_t _AwsIotJobs_IncrementReferences( _jobsOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
AwsIotMqttCallbackFunction_t callback )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
|
||||
const _jobsOperationType_t type = pOperation->type;
|
||||
_jobsSubscription_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->operationReferences[ type ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
IotLogDebug( "Jobs %s for %.*s has persistent subscriptions. Reference "
|
||||
"count will not be incremented.",
|
||||
_pAwsIotJobsOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* When persistent subscriptions are not present, the reference count must
|
||||
* not be negative. */
|
||||
AwsIotJobs_Assert( pSubscription->operationReferences[ type ] >= 0 );
|
||||
|
||||
/* Check if there are any existing references for this operation. */
|
||||
if( pSubscription->operationReferences[ type ] == 0 )
|
||||
{
|
||||
/* Set the parameters needed to add subscriptions. */
|
||||
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
|
||||
subscriptionInfo.callbackFunction = callback;
|
||||
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
|
||||
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
|
||||
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
|
||||
|
||||
subscriptionStatus = AwsIot_ModifySubscriptions( IotMqtt_SubscribeSync,
|
||||
&subscriptionInfo );
|
||||
|
||||
/* Convert MQTT return code to Jobs return code. */
|
||||
switch( subscriptionStatus )
|
||||
{
|
||||
case IOT_MQTT_SUCCESS:
|
||||
status = AWS_IOT_JOBS_SUCCESS;
|
||||
break;
|
||||
|
||||
case IOT_MQTT_NO_MEMORY:
|
||||
status = AWS_IOT_JOBS_NO_MEMORY;
|
||||
break;
|
||||
|
||||
default:
|
||||
status = AWS_IOT_JOBS_MQTT_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if( status != AWS_IOT_JOBS_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_JOBS_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
|
||||
{
|
||||
( pSubscription->operationReferences[ type ] )++;
|
||||
|
||||
IotLogDebug( "Jobs %s subscriptions for %.*s now has count %d.",
|
||||
_pAwsIotJobsOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName,
|
||||
pSubscription->operationReferences[ type ] );
|
||||
}
|
||||
/* Otherwise, set the persistent subscriptions flag. */
|
||||
else
|
||||
{
|
||||
pSubscription->operationReferences[ type ] = AWS_IOT_PERSISTENT_SUBSCRIPTION;
|
||||
|
||||
IotLogDebug( "Set persistent subscriptions flag for Jobs %s of %.*s.",
|
||||
_pAwsIotJobsOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _AwsIotJobs_DecrementReferences( _jobsOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
_jobsSubscription_t ** pRemovedSubscription )
|
||||
{
|
||||
const _jobsOperationType_t type = pOperation->type;
|
||||
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
|
||||
uint16_t operationTopicLength = 0;
|
||||
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
|
||||
AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
|
||||
|
||||
/* Do nothing if this Jobs operation has persistent subscriptions. */
|
||||
if( pSubscription->operationReferences[ type ] != AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
/* Decrement the number of subscription references for this operation.
|
||||
* Ensure that it's positive. */
|
||||
( pSubscription->operationReferences[ type ] )--;
|
||||
AwsIotJobs_Assert( pSubscription->operationReferences[ type ] >= 0 );
|
||||
|
||||
/* Check if the number of references has reached 0. */
|
||||
if( pSubscription->operationReferences[ type ] == 0 )
|
||||
{
|
||||
IotLogDebug( "Reference count for %.*s %s is 0. Unsubscribing.",
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName,
|
||||
_pAwsIotJobsOperationNames[ type ] );
|
||||
|
||||
/* Subscription must have a topic buffer. */
|
||||
AwsIotJobs_Assert( pSubscription->pTopicBuffer != NULL );
|
||||
|
||||
/* Set the parameters needed to generate a Jobs topic. */
|
||||
requestInfo.pThingName = pSubscription->pThingName;
|
||||
requestInfo.thingNameLength = pSubscription->thingNameLength;
|
||||
requestInfo.pJobId = pOperation->pJobId;
|
||||
requestInfo.jobIdLength = pOperation->jobIdLength;
|
||||
|
||||
/* Generate the prefix of the Jobs topic. This function will not
|
||||
* fail when given a buffer. */
|
||||
( void ) _AwsIotJobs_GenerateJobsTopic( ( _jobsOperationType_t ) type,
|
||||
&requestInfo,
|
||||
&( pSubscription->pTopicBuffer ),
|
||||
&operationTopicLength );
|
||||
|
||||
/* Set the parameters needed to remove subscriptions. */
|
||||
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
|
||||
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
|
||||
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
|
||||
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
|
||||
|
||||
( void ) AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
|
||||
&subscriptionInfo );
|
||||
}
|
||||
|
||||
/* Check if this subscription should be deleted. */
|
||||
_AwsIotJobs_RemoveSubscription( pSubscription,
|
||||
pRemovedSubscription );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "Jobs %s for %.*s has persistent subscriptions. Reference "
|
||||
"count will not be decremented.",
|
||||
_pAwsIotJobsOperationNames[ type ],
|
||||
pSubscription->thingNameLength,
|
||||
pSubscription->pThingName );
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
AwsIotJobsError_t AwsIotJobs_RemovePersistentSubscriptions( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
uint32_t flags )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
|
||||
int32_t i = 0;
|
||||
uint16_t operationTopicLength = 0;
|
||||
IotMqttError_t unsubscribeStatus = IOT_MQTT_STATUS_PENDING;
|
||||
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
|
||||
_jobsSubscription_t * pSubscription = NULL;
|
||||
IotLink_t * pSubscriptionLink = NULL;
|
||||
AwsIotThingName_t thingName = { 0 };
|
||||
|
||||
thingName.pThingName = pRequestInfo->pThingName;
|
||||
thingName.thingNameLength = pRequestInfo->thingNameLength;
|
||||
|
||||
IotLogInfo( "Removing persistent subscriptions for %.*s.",
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName );
|
||||
|
||||
/* Check parameters. */
|
||||
if( pRequestInfo->mqttConnection == IOT_MQTT_CONNECTION_INITIALIZER )
|
||||
{
|
||||
IotLogError( "MQTT connection is not initialized." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
|
||||
}
|
||||
|
||||
if( AwsIot_ValidateThingName( pRequestInfo->pThingName,
|
||||
pRequestInfo->thingNameLength ) == false )
|
||||
{
|
||||
IotLogError( "Thing Name is not valid." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
|
||||
}
|
||||
|
||||
if( ( ( flags & AWS_IOT_JOBS_FLAG_REMOVE_DESCRIBE_SUBSCRIPTIONS ) != 0 ) ||
|
||||
( ( flags & AWS_IOT_JOBS_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS ) != 0 ) )
|
||||
{
|
||||
if( ( pRequestInfo->pJobId == NULL ) || ( pRequestInfo->jobIdLength == 0 ) )
|
||||
{
|
||||
IotLogError( "Job ID must be set." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
|
||||
}
|
||||
|
||||
if( pRequestInfo->jobIdLength > JOBS_MAX_ID_LENGTH )
|
||||
{
|
||||
IotLogError( "Job ID cannot be longer than %d.",
|
||||
JOBS_MAX_ID_LENGTH );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
|
||||
}
|
||||
}
|
||||
|
||||
IotMutex_Lock( &( _AwsIotJobsSubscriptionsMutex ) );
|
||||
|
||||
/* Search the list for an existing subscription for Thing Name. */
|
||||
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotJobsSubscriptions ),
|
||||
NULL,
|
||||
_jobsSubscription_match,
|
||||
&thingName );
|
||||
|
||||
if( pSubscriptionLink != NULL )
|
||||
{
|
||||
IotLogDebug( "Found subscription object for %.*s. Checking for persistent "
|
||||
"subscriptions to remove.",
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName );
|
||||
|
||||
pSubscription = IotLink_Container( _jobsSubscription_t, pSubscriptionLink, link );
|
||||
|
||||
for( i = 0; i < JOBS_OPERATION_COUNT; i++ )
|
||||
{
|
||||
if( ( flags & ( 0x1UL << i ) ) != 0 )
|
||||
{
|
||||
IotLogDebug( "Removing %.*s %s persistent subscriptions.",
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName,
|
||||
_pAwsIotJobsOperationNames[ i ] );
|
||||
|
||||
/* Subscription must have a topic buffer. */
|
||||
AwsIotJobs_Assert( pSubscription->pTopicBuffer != NULL );
|
||||
|
||||
if( pSubscription->operationReferences[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
|
||||
{
|
||||
/* Generate the prefix of the Jobs topic. This function will not
|
||||
* fail when given a buffer. */
|
||||
( void ) _AwsIotJobs_GenerateJobsTopic( ( _jobsOperationType_t ) i,
|
||||
pRequestInfo,
|
||||
&( pSubscription->pTopicBuffer ),
|
||||
&operationTopicLength );
|
||||
|
||||
/* Set the parameters needed to remove subscriptions. */
|
||||
subscriptionInfo.mqttConnection = pRequestInfo->mqttConnection;
|
||||
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
|
||||
subscriptionInfo.pTopicFilterBase = pSubscription->pTopicBuffer;
|
||||
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
|
||||
|
||||
unsubscribeStatus = AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
|
||||
&subscriptionInfo );
|
||||
|
||||
/* Convert MQTT return code to Shadow return code. */
|
||||
switch( unsubscribeStatus )
|
||||
{
|
||||
case IOT_MQTT_SUCCESS:
|
||||
status = AWS_IOT_JOBS_SUCCESS;
|
||||
break;
|
||||
|
||||
case IOT_MQTT_NO_MEMORY:
|
||||
status = AWS_IOT_JOBS_NO_MEMORY;
|
||||
break;
|
||||
|
||||
default:
|
||||
status = AWS_IOT_JOBS_MQTT_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if( status != AWS_IOT_JOBS_SUCCESS )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the persistent subscriptions flag and check if the
|
||||
* subscription can be removed. */
|
||||
pSubscription->operationReferences[ i ] = 0;
|
||||
_AwsIotJobs_RemoveSubscription( pSubscription, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogDebug( "%.*s %s does not have persistent subscriptions.",
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName,
|
||||
_pAwsIotJobsOperationNames[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "No subscription object found for %.*s",
|
||||
pRequestInfo->thingNameLength,
|
||||
pRequestInfo->pThingName );
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( _AwsIotJobsSubscriptionsMutex ) );
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,628 @@
|
|||
/*
|
||||
* AWS IoT Jobs V1.0.0
|
||||
* Copyright (C) 2019 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_jobs_internal.h
|
||||
* @brief Internal header of Jobs library. This header should not be included in
|
||||
* typical application code.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_JOBS_INTERNAL_H_
|
||||
#define AWS_IOT_JOBS_INTERNAL_H_
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Linear containers (lists and queues) include. */
|
||||
#include "iot_linear_containers.h"
|
||||
|
||||
/* Jobs include. */
|
||||
#include "aws_iot_jobs.h"
|
||||
|
||||
/* AWS IoT include. */
|
||||
#include "aws_iot.h"
|
||||
|
||||
/**
|
||||
* @def AwsIotJobs_Assert( expression )
|
||||
* @brief Assertion macro for the Jobs library.
|
||||
*
|
||||
* Set @ref AWS_IOT_JOBS_ENABLE_ASSERTS to `1` to enable assertions in the Jobs
|
||||
* library.
|
||||
*
|
||||
* @param[in] expression Expression to be evaluated.
|
||||
*/
|
||||
#if AWS_IOT_JOBS_ENABLE_ASSERTS == 1
|
||||
#ifndef AwsIotJobs_Assert
|
||||
#ifdef Iot_DefaultAssert
|
||||
#define AwsIotJobs_Assert( expression ) Iot_DefaultAssert( expression )
|
||||
#else
|
||||
#error "Asserts are enabled for Jobs, but AwsIotJobs_Assert is not defined"
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#define AwsIotJobs_Assert( expression )
|
||||
#endif
|
||||
|
||||
/* Configure logs for Jobs functions. */
|
||||
#ifdef AWS_IOT_LOG_LEVEL_JOBS
|
||||
#define LIBRARY_LOG_LEVEL AWS_IOT_LOG_LEVEL_JOBS
|
||||
#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 ( "Jobs" )
|
||||
#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 #_jobsOperation_t. This function should have the same
|
||||
* signature as [malloc]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
|
||||
*/
|
||||
void * AwsIotJobs_MallocOperation( size_t size );
|
||||
|
||||
/**
|
||||
* @brief Free a #_jobsOperation_t. This function should have the same
|
||||
* signature as [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
void AwsIotJobs_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 AwsIotJobs_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 AwsIotJobs_FreeString Iot_FreeMessageBuffer
|
||||
|
||||
/**
|
||||
* @brief Allocate a #_jobsSubscription_t. This function should have the
|
||||
* same signature as [malloc]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
|
||||
*/
|
||||
void * AwsIotJobs_MallocSubscription( size_t size );
|
||||
|
||||
/**
|
||||
* @brief Free a #_jobsSubscription_t. This function should have the same
|
||||
* signature as [free]
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
|
||||
*/
|
||||
void AwsIotJobs_FreeSubscription( void * ptr );
|
||||
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
|
||||
#ifndef AwsIotJobs_MallocOperation
|
||||
#ifdef Iot_DefaultMalloc
|
||||
#define AwsIotJobs_MallocOperation Iot_DefaultMalloc
|
||||
#else
|
||||
#error "No malloc function defined for AwsIotJobs_MallocOperation"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotJobs_FreeOperation
|
||||
#ifdef Iot_DefaultFree
|
||||
#define AwsIotJobs_FreeOperation Iot_DefaultFree
|
||||
#else
|
||||
#error "No free function defined for AwsIotJobs_FreeOperation"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotJobs_MallocString
|
||||
#ifdef Iot_DefaultMalloc
|
||||
#define AwsIotJobs_MallocString Iot_DefaultMalloc
|
||||
#else
|
||||
#error "No malloc function defined for AwsIotJobs_MallocString"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotJobs_FreeString
|
||||
#ifdef Iot_DefaultFree
|
||||
#define AwsIotJobs_FreeString Iot_DefaultFree
|
||||
#else
|
||||
#error "No free function defined for AwsIotJobs_FreeString"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotJobs_MallocSubscription
|
||||
#ifdef Iot_DefaultMalloc
|
||||
#define AwsIotJobs_MallocSubscription Iot_DefaultMalloc
|
||||
#else
|
||||
#error "No malloc function defined for AwsIotJobs_MallocSubscription"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AwsIotJobs_FreeSubscription
|
||||
#ifdef Iot_DefaultFree
|
||||
#define AwsIotJobs_FreeSubscription Iot_DefaultFree
|
||||
#else
|
||||
#error "No free function defined for AwsIotJobs_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_JOBS_DEFAULT_MQTT_TIMEOUT_MS
|
||||
#define AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS ( 5000 )
|
||||
#endif
|
||||
#ifndef AWS_IOT_JOBS_NOTIFY_CALLBACKS
|
||||
#define AWS_IOT_JOBS_NOTIFY_CALLBACKS ( 1 )
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief The number of currently available Jobs operations.
|
||||
*
|
||||
* The 4 Jobs operations are GET PENDING, START NEXT, DESCRIBE, and UPDATE.
|
||||
*/
|
||||
#define JOBS_OPERATION_COUNT ( 4 )
|
||||
|
||||
/**
|
||||
* @brief The number of currently available Jobs callbacks.
|
||||
*
|
||||
* The 2 Jobs callbacks are `jobs/notify` (AKA "Notify Pending") and
|
||||
* `/jobs/notify-next` (AKA "Notify Next").
|
||||
*/
|
||||
#define JOBS_CALLBACK_COUNT ( 2 )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Jobs GET PENDING operation in a Jobs MQTT topic.
|
||||
*/
|
||||
#define JOBS_GET_PENDING_OPERATION_STRING "/jobs/get"
|
||||
|
||||
/**
|
||||
* @brief The length of #JOBS_GET_PENDING_OPERATION_STRING.
|
||||
*/
|
||||
#define JOBS_GET_PENDING_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_GET_PENDING_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Jobs START NEXT operation in a Jobs MQTT topic.
|
||||
*/
|
||||
#define JOBS_START_NEXT_OPERATION_STRING "/jobs/start-next"
|
||||
|
||||
/**
|
||||
* @brief The length of #JOBS_START_NEXT_OPERATION_STRING.
|
||||
*/
|
||||
#define JOBS_START_NEXT_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_START_NEXT_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Jobs DESCRIBE operation in a Jobs MQTT topic.
|
||||
*
|
||||
* This string should be placed after a Job ID.
|
||||
*/
|
||||
#define JOBS_DESCRIBE_OPERATION_STRING "/get"
|
||||
|
||||
/**
|
||||
* @brief The length of #JOBS_DESCRIBE_OPERATION_STRING.
|
||||
*/
|
||||
#define JOBS_DESCRIBE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_DESCRIBE_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing a Jobs UPDATE operation in a Jobs MQTT topic.
|
||||
*
|
||||
* This string should be placed after a Job ID.
|
||||
*/
|
||||
#define JOBS_UPDATE_OPERATION_STRING "/update"
|
||||
|
||||
/**
|
||||
* @brief The length of #JOBS_UPDATE_OPERATION_STRING.
|
||||
*
|
||||
* This length excludes the length of the placeholder %s.
|
||||
*/
|
||||
#define JOBS_UPDATE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_UPDATE_OPERATION_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing the Jobs MQTT topic for receiving notifications
|
||||
* of pending Jobs.
|
||||
*/
|
||||
#define JOBS_NOTIFY_PENDING_CALLBACK_STRING "/jobs/notify"
|
||||
|
||||
/**
|
||||
* @brief The length of #JOBS_NOTIFY_PENDING_CALLBACK_STRING.
|
||||
*/
|
||||
#define JOBS_NOTIFY_PENDING_CALLBACK_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_NOTIFY_PENDING_CALLBACK_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The string representing the Jobs MQTT topic for receiving notifications
|
||||
* of the next pending Job.
|
||||
*/
|
||||
#define JOBS_NOTIFY_NEXT_CALLBACK_STRING "/jobs/notify-next"
|
||||
|
||||
/**
|
||||
* @brief The length of #JOBS_NOTIFY_NEXT_CALLBACK_STRING.
|
||||
*/
|
||||
#define JOBS_NOTIFY_NEXT_CALLBACK_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_NOTIFY_NEXT_CALLBACK_STRING ) - 1 ) )
|
||||
|
||||
/**
|
||||
* @brief The maximum length of a Job ID, per AWS IoT Service Limits.
|
||||
*
|
||||
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
|
||||
*/
|
||||
#define JOBS_MAX_ID_LENGTH ( 64 )
|
||||
|
||||
/**
|
||||
* @brief The maximum value of the Jobs step timeout, per AWS IoT Service Limits.
|
||||
*
|
||||
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
|
||||
*/
|
||||
#define JOBS_MAX_TIMEOUT ( 10080 )
|
||||
|
||||
/**
|
||||
* @brief A limit on the maximum length of a Jobs status details, per AWS IoT
|
||||
* Service Limits.
|
||||
*
|
||||
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
|
||||
*
|
||||
* This is actually the limit on the length of an entire Jobs document; but the
|
||||
* status details must also not exceed this length,
|
||||
*/
|
||||
#define JOBS_MAX_STATUS_DETAILS_LENGTH ( 32768 )
|
||||
|
||||
/**
|
||||
* @brief The length of the longest Jobs topic suffix.
|
||||
*
|
||||
* This is the length of the longest Job ID, plus the length of the "UPDATE"
|
||||
* operation suffix, plus the length of "/jobs/".
|
||||
*/
|
||||
#define JOBS_LONGEST_SUFFIX_LENGTH ( JOBS_MAX_ID_LENGTH + JOBS_UPDATE_OPERATION_STRING_LENGTH + 6 )
|
||||
|
||||
/*------------------------ Jobs internal data types -------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Enumerations representing each of the Jobs library's API functions.
|
||||
*/
|
||||
typedef enum _jobsOperationType
|
||||
{
|
||||
/* Jobs operations. */
|
||||
JOBS_GET_PENDING = 0, /**< @ref jobs_function_getpendingasync */
|
||||
JOBS_START_NEXT = 1, /**< @ref jobs_function_startnextasync */
|
||||
JOBS_DESCRIBE = 2, /**< @ref jobs_function_describeasync */
|
||||
JOBS_UPDATE = 3, /**< @ref jobs_function_updateasync */
|
||||
|
||||
/* Jobs callbacks. */
|
||||
SET_NOTIFY_PENDING_CALLBACK = 4, /**< @ref jobs_function_setnotifypendingcallback */
|
||||
SET_NOTIFY_NEXT_CALLBACK = 5 /**< @ref jobs_function_setnotifynextcallback */
|
||||
} _jobsOperationType_t;
|
||||
|
||||
/**
|
||||
* @brief Enumerations representing each of the Jobs callback functions.
|
||||
*/
|
||||
typedef enum _jobsCallbackType
|
||||
{
|
||||
NOTIFY_PENDING_CALLBACK = 0, /**< Pending Job notification callback. */
|
||||
NOTIFY_NEXT_CALLBACK = 1 /**< Next Job notification callback. */
|
||||
} _jobsCallbackType_t;
|
||||
|
||||
/**
|
||||
* @brief Parameter to #_AwsIotJobs_GenerateJsonRequest.
|
||||
*/
|
||||
typedef union _jsonRequestContents
|
||||
{
|
||||
const AwsIotJobsUpdateInfo_t * pUpdateInfo; /**< @brief Valid for #JOBS_START_NEXT and #JOBS_UPDATE. */
|
||||
|
||||
struct
|
||||
{
|
||||
int32_t executionNumber; /**< @brief Execution number. */
|
||||
bool includeJobDocument; /**< @brief Whether the response should include the Job document. */
|
||||
} describe; /**< @brief Valid for #JOBS_DESCRIBE. */
|
||||
} _jsonRequestContents_t;
|
||||
|
||||
/**
|
||||
* @brief Represents a Jobs subscriptions object.
|
||||
*
|
||||
* These structures are stored in a list.
|
||||
*/
|
||||
typedef struct _jobsSubscription
|
||||
{
|
||||
IotLink_t link; /**< @brief List link member. */
|
||||
|
||||
int32_t operationReferences[ JOBS_OPERATION_COUNT ]; /**< @brief Reference counters for Jobs operation topics. */
|
||||
int32_t callbackReferences; /**< @brief Reference counter for Jobs callbacks. */
|
||||
|
||||
/** @brief Jobs callbacks for this Thing. */
|
||||
AwsIotJobsCallbackInfo_t callbacks[ JOBS_CALLBACK_COUNT ][ AWS_IOT_JOBS_NOTIFY_CALLBACKS ];
|
||||
|
||||
/**
|
||||
* @brief Buffer allocated for removing Jobs 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. */
|
||||
} _jobsSubscription_t;
|
||||
|
||||
/**
|
||||
* @brief Internal structure representing a single Jobs operation.
|
||||
*
|
||||
* A list of these structures keeps track of all in-progress Jobs operations.
|
||||
*/
|
||||
typedef struct _jobsOperation
|
||||
{
|
||||
IotLink_t link; /**< @brief List link member. */
|
||||
|
||||
/* Basic operation information. */
|
||||
_jobsOperationType_t type; /**< @brief Operation type. */
|
||||
uint32_t flags; /**< @brief Flags passed to operation API function. */
|
||||
AwsIotJobsError_t status; /**< @brief Status of operation. */
|
||||
|
||||
IotMqttConnection_t mqttConnection; /**< @brief MQTT connection associated with this operation. */
|
||||
_jobsSubscription_t * pSubscription; /**< @brief Jobs subscriptions object associated with this operation. */
|
||||
|
||||
/* Jobs request information. */
|
||||
const char * pJobsRequest; /**< @brief JSON document to send to the Jobs service. */
|
||||
size_t jobsRequestLength; /**< @brief Length of #_jobsOperation_t.pJobsRequest. */
|
||||
|
||||
const char * pClientToken; /**< @brief Client token sent with request. */
|
||||
size_t clientTokenLength; /**< @brief Length of #_jobsOperation_t.pClientToken. */
|
||||
|
||||
/* Jobs response information. */
|
||||
const char * pJobsResponse; /**< @brief Response received from the Jobs service. */
|
||||
size_t jobsResponseLength; /**< @brief Length of #_jobsOperation_t.pJobsResponse. */
|
||||
|
||||
/**
|
||||
* @brief Function to allocate memory for an incoming Jobs response.
|
||||
*
|
||||
* Only used when the flag #AWS_IOT_JOBS_FLAG_WAITABLE is set.
|
||||
*/
|
||||
void * ( *mallocResponse )( size_t );
|
||||
|
||||
/* How to notify of an operation's completion. */
|
||||
union
|
||||
{
|
||||
IotSemaphore_t waitSemaphore; /**< @brief Semaphore to be used with @ref jobs_function_wait. */
|
||||
AwsIotJobsCallbackInfo_t callback; /**< @brief User-provided callback function and parameter. */
|
||||
} notify; /**< @brief How to notify of an operation's completion. */
|
||||
|
||||
size_t jobIdLength; /**< @brief Length of #_jobsOperation_t.pJobId. */
|
||||
char pJobId[]; /**< @brief Job ID, saved for DESCRIBE and UPDATE operations. */
|
||||
} _jobsOperation_t;
|
||||
|
||||
/* Declarations of names printed in logs. */
|
||||
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
|
||||
extern const char * const _pAwsIotJobsOperationNames[];
|
||||
extern const char * const _pAwsIotJobsCallbackNames[];
|
||||
#endif
|
||||
|
||||
/* Declarations of variables for internal Jobs files. */
|
||||
extern uint32_t _AwsIotJobsMqttTimeoutMs;
|
||||
extern IotListDouble_t _AwsIotJobsPendingOperations;
|
||||
extern IotListDouble_t _AwsIotJobsSubscriptions;
|
||||
extern IotMutex_t _AwsIotJobsPendingOperationsMutex;
|
||||
extern IotMutex_t _AwsIotJobsSubscriptionsMutex;
|
||||
|
||||
/*------------------------ Jobs operation functions -------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Create a record for a new in-progress Jobs operation.
|
||||
*
|
||||
* @param[in] type The type of Jobs operation for the request.
|
||||
* @param[in] pRequestInfo Common Jobs request parameters.
|
||||
* @param[in] pRequestContents Additional values to place in the JSON document,
|
||||
* depending on `type`.
|
||||
* @param[in] flags Flags variables passed to a user-facing Jobs function.
|
||||
* @param[in] pCallbackInfo User-provided callback function and parameter.
|
||||
* @param[out] pNewOperation Set to point to the new operation on success.
|
||||
*
|
||||
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY
|
||||
*/
|
||||
AwsIotJobsError_t _AwsIotJobs_CreateOperation( _jobsOperationType_t type,
|
||||
const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const _jsonRequestContents_t * pRequestContents,
|
||||
uint32_t flags,
|
||||
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
|
||||
_jobsOperation_t ** pNewOperation );
|
||||
|
||||
/**
|
||||
* @brief Free resources used to record a Jobs 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 _AwsIotJobs_DestroyOperation( void * pData );
|
||||
|
||||
/**
|
||||
* @brief Fill a buffer with a Jobs topic.
|
||||
*
|
||||
* @param[in] type One of: GET PENDING, START NEXT, DESCRIBE, or UPDATE.
|
||||
* @param[in] pRequestInfo Common Jobs request parameters.
|
||||
* @param[out] pTopicBuffer Address of the buffer for the Jobs 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 Jobs 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 Jobs topic, plus
|
||||
* #JOBS_LONGEST_SUFFIX_LENGTH.
|
||||
*
|
||||
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY. This function
|
||||
* will not return #AWS_IOT_JOBS_NO_MEMORY when a buffer is provided.
|
||||
*/
|
||||
AwsIotJobsError_t _AwsIotJobs_GenerateJobsTopic( _jobsOperationType_t type,
|
||||
const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
char ** pTopicBuffer,
|
||||
uint16_t * pOperationTopicLength );
|
||||
|
||||
/**
|
||||
* @brief Process a Jobs operation by sending the necessary MQTT packets.
|
||||
*
|
||||
* @param[in] pRequestInfo Common Jobs request parameters.
|
||||
* @param[in] pOperation Operation data to process.
|
||||
*
|
||||
* @return #AWS_IOT_JOBS_STATUS_PENDING on success. On error, one of
|
||||
* #AWS_IOT_JOBS_NO_MEMORY or #AWS_IOT_JOBS_MQTT_ERROR.
|
||||
*/
|
||||
AwsIotJobsError_t _AwsIotJobs_ProcessOperation( const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
_jobsOperation_t * pOperation );
|
||||
|
||||
/*----------------------- Jobs subscription functions -----------------------*/
|
||||
|
||||
/**
|
||||
* @brief Find a Jobs 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 Jobs 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.
|
||||
*/
|
||||
_jobsSubscription_t * _AwsIotJobs_FindSubscription( const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
bool createIfNotFound );
|
||||
|
||||
/**
|
||||
* @brief Remove a Jobs 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 _AwsIotJobs_RemoveSubscription( _jobsSubscription_t * pSubscription,
|
||||
_jobsSubscription_t ** pRemovedSubscription );
|
||||
|
||||
/**
|
||||
* @brief Free resources used for a Jobs 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 _AwsIotJobs_DestroySubscription( void * pData );
|
||||
|
||||
/**
|
||||
* @brief Increment the reference count of a Jobs 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 Jobs operation topic, plus enough space for the
|
||||
* status suffix.
|
||||
*
|
||||
* @return #AWS_IOT_JOBS_SUCCESS on success. On error, one of
|
||||
* #AWS_IOT_JOBS_NO_MEMORY or #AWS_IOT_JOBS_MQTT_ERROR.
|
||||
*
|
||||
* @note This function should be called with the subscription list mutex locked.
|
||||
*/
|
||||
AwsIotJobsError_t _AwsIotJobs_IncrementReferences( _jobsOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
uint16_t operationTopicLength,
|
||||
AwsIotMqttCallbackFunction_t callback );
|
||||
|
||||
/**
|
||||
* @brief Decrement the reference count of a Jobs 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 Jobs topic, plus
|
||||
* #JOBS_LONGEST_SUFFIX_LENGTH.
|
||||
*
|
||||
* @note This function should be called with the subscription list mutex locked.
|
||||
*/
|
||||
void _AwsIotJobs_DecrementReferences( _jobsOperation_t * pOperation,
|
||||
char * pTopicBuffer,
|
||||
_jobsSubscription_t ** pRemovedSubscription );
|
||||
|
||||
/*------------------------ Jobs serializer functions ------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Generates a Jobs JSON request document from an #AwsIotJobsRequestInfo_t
|
||||
* and an #AwsIotJobsUpdateInfo_t.
|
||||
*
|
||||
* @param[in] type The type of Jobs operation for the request.
|
||||
* @param[in] pRequestInfo Common Jobs request parameters.
|
||||
* @param[in] pRequestContents Additional values to place in the JSON document,
|
||||
* depending on `type`.
|
||||
* @param[in] pOperation Operation associated with the Jobs request.
|
||||
*
|
||||
* @return #AWS_IOT_JOBS_SUCCESS on success; otherwise, #AWS_IOT_JOBS_NO_MEMORY.
|
||||
*/
|
||||
AwsIotJobsError_t _AwsIotJobs_GenerateJsonRequest( _jobsOperationType_t type,
|
||||
const AwsIotJobsRequestInfo_t * pRequestInfo,
|
||||
const _jsonRequestContents_t * pRequestContents,
|
||||
_jobsOperation_t * pOperation );
|
||||
|
||||
/**
|
||||
* @brief Parse a response received from the Jobs service.
|
||||
*
|
||||
* @param[in] status Either ACCEPTED or REJECTED.
|
||||
* @param[in] pResponse The response received from the Jobs service.
|
||||
* @param[in] responseLength Length of `pResponse`.
|
||||
* @param[out] pOperation Associated Jobs operation, where parse results are
|
||||
* written.
|
||||
*/
|
||||
void _AwsIotJobs_ParseResponse( AwsIotStatus_t status,
|
||||
const char * pResponse,
|
||||
size_t responseLength,
|
||||
_jobsOperation_t * pOperation );
|
||||
|
||||
#endif /* ifndef AWS_IOT_JOBS_INTERNAL_H_ */
|
|
@ -0,0 +1,5 @@
|
|||
[{000214A0-0000-0000-C000-000000000046}]
|
||||
Prop3=19,11
|
||||
[InternetShortcut]
|
||||
IDList=
|
||||
URL=https://www.freertos.org/shadow
|
|
@ -0,0 +1,909 @@
|
|||
/*
|
||||
* 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.h
|
||||
* @brief User-facing functions of the Shadow library.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_SHADOW_H_
|
||||
#define AWS_IOT_SHADOW_H_
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Shadow types include. */
|
||||
#include "types/aws_iot_shadow_types.h"
|
||||
|
||||
/*------------------------ Shadow library functions -------------------------*/
|
||||
|
||||
/**
|
||||
* @functionspage{shadow,Shadow library}
|
||||
* - @functionname{shadow_function_init}
|
||||
* - @functionname{shadow_function_cleanup}
|
||||
* - @functionname{shadow_function_deleteasync}
|
||||
* - @functionname{shadow_function_deletesync}
|
||||
* - @functionname{shadow_function_getasync}
|
||||
* - @functionname{shadow_function_getsync}
|
||||
* - @functionname{shadow_function_updateasync}
|
||||
* - @functionname{shadow_function_updatesync}
|
||||
* - @functionname{shadow_function_wait}
|
||||
* - @functionname{shadow_function_setdeltacallback}
|
||||
* - @functionname{shadow_function_setupdatedcallback}
|
||||
* - @functionname{shadow_function_removepersistentsubscriptions}
|
||||
* - @functionname{shadow_function_strerror}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @functionpage{AwsIotShadow_Init,shadow,init}
|
||||
* @functionpage{AwsIotShadow_Cleanup,shadow,cleanup}
|
||||
* @functionpage{AwsIotShadow_DeleteAsync,shadow,deleteasync}
|
||||
* @functionpage{AwsIotShadow_DeleteSync,shadow,deletesync}
|
||||
* @functionpage{AwsIotShadow_GetAsync,shadow,getasync}
|
||||
* @functionpage{AwsIotShadow_GetSync,shadow,getsync}
|
||||
* @functionpage{AwsIotShadow_UpdateAsync,shadow,updateasync}
|
||||
* @functionpage{AwsIotShadow_UpdateSync,shadow,updatesync}
|
||||
* @functionpage{AwsIotShadow_Wait,shadow,wait}
|
||||
* @functionpage{AwsIotShadow_SetDeltaCallback,shadow,setdeltacallback}
|
||||
* @functionpage{AwsIotShadow_SetUpdatedCallback,shadow,setupdatedcallback}
|
||||
* @functionpage{AwsIotShadow_RemovePersistentSubscriptions,shadow,removepersistentsubscriptions}
|
||||
* @functionpage{AwsIotShadow_strerror,shadow,strerror}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief One-time initialization function for the Shadow library.
|
||||
*
|
||||
* This function performs internal setup of the Shadow library. <b>It must be
|
||||
* called once (and only once) before calling any other Shadow function.</b>
|
||||
* Calling this function more than once without first calling @ref
|
||||
* shadow_function_cleanup may result in a crash.
|
||||
*
|
||||
* @param[in] mqttTimeout The amount of time (in milliseconds) that the Shadow
|
||||
* library will wait for MQTT operations. Optional; set this to `0` to use
|
||||
* @ref AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_INIT_FAILED
|
||||
*
|
||||
* @warning No thread-safety guarantees are provided for this function.
|
||||
*
|
||||
* @see @ref shadow_function_cleanup
|
||||
*/
|
||||
/* @[declare_shadow_init] */
|
||||
AwsIotShadowError_t AwsIotShadow_Init( uint32_t mqttTimeout );
|
||||
/* @[declare_shadow_init] */
|
||||
|
||||
/**
|
||||
* @brief One-time deinitialization function for the Shadow library.
|
||||
*
|
||||
* This function frees resources taken in @ref shadow_function_init and deletes
|
||||
* any [persistent subscriptions.](@ref AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS)
|
||||
* It should be called to clean up the Shadow library. After this function returns,
|
||||
* @ref shadow_function_init must be called again before calling any other Shadow
|
||||
* function.
|
||||
*
|
||||
* @warning No thread-safety guarantees are provided for this function.
|
||||
*
|
||||
* @see @ref shadow_function_init
|
||||
*/
|
||||
/* @[declare_shadow_cleanup] */
|
||||
void AwsIotShadow_Cleanup( void );
|
||||
/* @[declare_shadow_cleanup] */
|
||||
|
||||
/**
|
||||
* @brief Delete a Thing Shadow and receive an asynchronous notification when
|
||||
* the Delete completes.
|
||||
*
|
||||
* This function deletes any existing Shadow document for the given Thing Name.
|
||||
* If the given Thing has no Shadow and this function is called, the result will
|
||||
* be #AWS_IOT_SHADOW_NOT_FOUND.
|
||||
*
|
||||
* Deleting a Shadow involves sending an MQTT message to AWS IoT and waiting on
|
||||
* a response. This message will always be sent at [MQTT QoS 0](@ref #IOT_MQTT_QOS_0).
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for Shadow delete.
|
||||
* @param[in] pThingName The Thing Name associated with the Shadow to delete.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pDeleteOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Shadow delete
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
|
||||
* queuing a Shadow delete.
|
||||
* @return If this function fails before queuing a Shadow delete, it will return one of:
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* @return Upon successful completion of the Shadow delete (either through an #AwsIotShadowCallbackInfo_t
|
||||
* or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
|
||||
* @return Should the Shadow delete fail, the status will be one of:
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*
|
||||
* @see @ref shadow_function_deletesync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* // Shadow operation handle.
|
||||
* AwsIotShadowOperation_t deleteOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
|
||||
*
|
||||
* // Queue a Shadow delete.
|
||||
* AwsIotShadowError_t deleteResult = AwsIotShadow_DeleteAsync( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* AWS_IOT_SHADOW_FLAG_WAITABLE,
|
||||
* NULL,
|
||||
* &deleteOperation );
|
||||
*
|
||||
* // Shadow delete should return AWS_IOT_SHADOW_STATUS_PENDING upon success.
|
||||
* if( deleteResult == AWS_IOT_SHADOW_STATUS_PENDING )
|
||||
* {
|
||||
* // Wait for the Shadow delete to complete.
|
||||
* deleteResult = AwsIotShadow_Wait( deleteOperation, 5000 );
|
||||
*
|
||||
* // Delete result should be AWS_IOT_SHADOW_SUCCESS upon successfully
|
||||
* // deleting an existing Shadow.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_shadow_deleteasync] */
|
||||
AwsIotShadowError_t AwsIotShadow_DeleteAsync( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotShadowOperation_t * const pDeleteOperation );
|
||||
/* @[declare_shadow_deleteasync] */
|
||||
|
||||
/**
|
||||
* @brief Delete a Thing Shadow with a timeout.
|
||||
*
|
||||
* This function queues a Shadow delete, then waits for the result. Internally, this
|
||||
* function is a call to @ref shadow_function_deleteasync followed by @ref shadow_function_wait.
|
||||
* See @ref shadow_function_deleteasync for more information on the Shadow delete operation.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for Shadow delete.
|
||||
* @param[in] pThingName The Thing Name associated with the Shadow to delete.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
|
||||
* @param[in] timeoutMs If the Shadow service does not respond to the Shadow delete
|
||||
* within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - #AWS_IOT_SHADOW_TIMEOUT
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*/
|
||||
/* @[declare_shadow_deletesync] */
|
||||
AwsIotShadowError_t AwsIotShadow_DeleteSync( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs );
|
||||
/* @[declare_shadow_deletesync] */
|
||||
|
||||
/**
|
||||
* @brief Retrieve a Thing Shadow and receive an asynchronous notification when
|
||||
* the Shadow document is received.
|
||||
*
|
||||
* This function retrieves the Thing Shadow document currently stored by the
|
||||
* Shadow service. If a given Thing has no Shadow and this function is called,
|
||||
* the result will be #AWS_IOT_SHADOW_NOT_FOUND.
|
||||
*
|
||||
* Shadow documents may be large, and their size is not known beforehand.
|
||||
* Therefore, this function works best when memory is dynamically allocated.
|
||||
* Because the Shadow document is retrieved in an MQTT PUBLISH packet, the MQTT
|
||||
* library will allocate a buffer for the Shadow document using #IotMqtt_MallocMessage.
|
||||
*
|
||||
* The MQTT library may free the buffer for a retrieved Shadow document as soon
|
||||
* as the [Shadow completion callback](@ref AwsIotShadowCallbackInfo_t) returns.
|
||||
* Therefore, any data needed later must be copied from the Shadow document.
|
||||
* Similarly, if the flag #AWS_IOT_SHADOW_FLAG_WAITABLE is given to this function
|
||||
* (which indicates that the Shadow document will be needed after the Shadow
|
||||
* operation completes), #AwsIotShadowDocumentInfo_t.mallocDocument must be
|
||||
* provided to allocate a longer-lasting buffer.
|
||||
*
|
||||
* @note Because of the potentially large size of complete Shadow documents, it is more
|
||||
* memory-efficient for most applications to use [delta callbacks]
|
||||
* (@ref shadow_function_setdeltacallback) to retrieve Shadows from
|
||||
* the Shadow service.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for Shadow get.
|
||||
* @param[in] pGetInfo Shadow document parameters.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pGetOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Shadow get
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
|
||||
* queuing a Shadow get.
|
||||
* @return If this function fails before queuing a Shadow get, it will return one of:
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* @return Upon successful completion of the Shadow get (either through an #AwsIotShadowCallbackInfo_t
|
||||
* or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
|
||||
* @return Should the Shadow get fail, the status will be one of:
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY (Memory could not be allocated for incoming document)
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*
|
||||
* @see @ref shadow_function_getsync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* // Shadow get completion callback. The retrieved document will be in
|
||||
* // pCallbackParam. Any data in the retrieved document needed after this
|
||||
* // function returns must be copied.
|
||||
* void _processRetrievedDocument( void * pCallbackContext,
|
||||
* AwsIotShadowCallbackParam_t * pCallbackParam );
|
||||
*
|
||||
* // Parameters and return value of Shadow get.
|
||||
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
* AwsIotShadowDocumentInfo_t getInfo = { ... };
|
||||
* uint32_t timeout = 5000; // 5 seconds
|
||||
*
|
||||
* // Callback for get completion.
|
||||
* AwsIotShadowCallbackInfo_t getCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
|
||||
* getCallback.function = _processRetrievedDocument;
|
||||
*
|
||||
* // Shadow get operation.
|
||||
* result = AwsIotShadow_GetAsync( mqttConnection,
|
||||
* &getInfo,
|
||||
* 0,
|
||||
* &getCallback,
|
||||
* NULL );
|
||||
*
|
||||
* // Get should have returned AWS_IOT_SHADOW_STATUS_PENDING. The function
|
||||
* // _processRetrievedDocument will be invoked once the Shadow get completes.
|
||||
* @endcode
|
||||
*
|
||||
* See @ref shadow_function_wait <b>Example 2</b> for an example of using this
|
||||
* function with #AWS_IOT_SHADOW_FLAG_WAITABLE and @ref shadow_function_wait.
|
||||
*/
|
||||
/* @[declare_shadow_getasync] */
|
||||
AwsIotShadowError_t AwsIotShadow_GetAsync( IotMqttConnection_t mqttConnection,
|
||||
const AwsIotShadowDocumentInfo_t * pGetInfo,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotShadowOperation_t * const pGetOperation );
|
||||
/* @[declare_shadow_getasync] */
|
||||
|
||||
/**
|
||||
* @brief Retrieve a Thing Shadow with a timeout.
|
||||
*
|
||||
* This function queues a Shadow get, then waits for the result. Internally, this
|
||||
* function is a call to @ref shadow_function_getasync followed by @ref shadow_function_wait.
|
||||
* See @ref shadow_function_getasync for more information on the Shadow get operation.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for Shadow get.
|
||||
* @param[in] pGetInfo Shadow document parameters.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
|
||||
* @param[in] timeoutMs If the Shadow service does not respond to the Shadow get
|
||||
* within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
|
||||
* @param[out] pShadowDocument A pointer to a buffer containing the Shadow document
|
||||
* retrieved by a Shadow get is placed here. The buffer was allocated with the function
|
||||
* `pGetInfo->get.mallocDocument`. This output parameter is only valid if this function
|
||||
* returns #AWS_IOT_SHADOW_SUCCESS.
|
||||
* @param[out] pShadowDocumentLength The length of the Shadow document in
|
||||
* `pShadowDocument` is placed here. This output parameter is only valid if this function
|
||||
* returns #AWS_IOT_SHADOW_SUCCESS.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - #AWS_IOT_SHADOW_TIMEOUT
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*/
|
||||
/* @[declare_shadow_getsync] */
|
||||
AwsIotShadowError_t AwsIotShadow_GetSync( IotMqttConnection_t mqttConnection,
|
||||
const AwsIotShadowDocumentInfo_t * pGetInfo,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs,
|
||||
const char ** const pShadowDocument,
|
||||
size_t * const pShadowDocumentLength );
|
||||
/* @[declare_shadow_getsync] */
|
||||
|
||||
/**
|
||||
* @brief Send a Thing Shadow update and receive an asynchronous notification when
|
||||
* the Shadow Update completes.
|
||||
*
|
||||
* This function modifies the Thing Shadow document stored by the Shadow service.
|
||||
* If a given Thing has no Shadow and this function is called, then a new Shadow
|
||||
* is created.
|
||||
*
|
||||
* New JSON keys in the Shadow document will be appended. For example, if the Shadow service
|
||||
* currently has a document containing key `example1` and this function sends a document
|
||||
* only containing key `example2`, then the resulting document in the Shadow service
|
||||
* will contain both `example1` and `example2`.
|
||||
*
|
||||
* Existing JSON keys in the Shadow document will be replaced. For example, if the Shadow
|
||||
* service currently has a document containing `"example1": [0,1,2]` and this function sends
|
||||
* a document containing key `"example1": [1,2,3]`, then the resulting document in the Shadow
|
||||
* service will contain `"example1": [1,2,3]`.
|
||||
*
|
||||
* Successful Shadow updates will trigger the [Shadow updated callback]
|
||||
* (@ref shadow_function_setupdatedcallback). If the resulting Shadow document contains
|
||||
* different `desired` and `reported` keys, then the [Shadow delta callback]
|
||||
* (@ref shadow_function_setdeltacallback) will be triggered as well.
|
||||
*
|
||||
* @attention All documents passed to this function must contain a `clientToken`.
|
||||
* The [client token]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-document.html#client-token)
|
||||
* is a string used to distinguish between Shadow updates. They are limited to 64
|
||||
* characters; attempting to use a client token longer than 64 characters will
|
||||
* cause the Shadow update to fail. They must be unique at any given time, i.e.
|
||||
* they may be reused <i>as long as no two Shadow updates are using the same
|
||||
* client token at the same time</i>.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for Shadow update.
|
||||
* @param[in] pUpdateInfo Shadow document parameters.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
|
||||
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
|
||||
* @param[out] pUpdateOperation Set to a handle by which this operation may be referenced
|
||||
* after this function returns. This reference is invalidated once the Shadow update
|
||||
* completes.
|
||||
*
|
||||
* @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
|
||||
* queuing a Shadow update.
|
||||
* @return If this function fails before queuing a Shadow update, it will return one of:
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* @return Upon successful completion of the Shadow update (either through an #AwsIotShadowCallbackInfo_t
|
||||
* or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
|
||||
* @return Should the Shadow update fail, the status will be one of:
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*
|
||||
* @see @ref shadow_function_updatesync for a blocking variant of this function.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* // Shadow update completion callback.
|
||||
* void _updateComplete( void * pCallbackContext,
|
||||
* AwsIotShadowCallbackParam_t * pCallbackParam );
|
||||
*
|
||||
* // Parameters and return value of Shadow update.
|
||||
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
* AwsIotShadowDocumentInfo_t updateInfo = { ... };
|
||||
* uint32_t timeout = 5000; // 5 seconds
|
||||
*
|
||||
* // Set Shadow document to send.
|
||||
* updateInfo.update.pUpdateDocument = "{...}"; // Must contain clientToken
|
||||
* updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
|
||||
*
|
||||
* // Callback for update completion.
|
||||
* AwsIotShadowCallbackInfo_t updateCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
|
||||
* updateCallback.function = _updateComplete;
|
||||
*
|
||||
* // Shadow update operation.
|
||||
* result = AwsIotShadow_UpdateAsync( mqttConnection,
|
||||
* &updateInfo,
|
||||
* 0,
|
||||
* &updateCallback,
|
||||
* NULL );
|
||||
*
|
||||
* // Update should have returned AWS_IOT_SHADOW_STATUS_PENDING. The function
|
||||
* // _updateComplete will be invoked once the Shadow update completes.
|
||||
* @endcode
|
||||
*
|
||||
* See @ref shadow_function_wait <b>Example 1</b> for an example of using this
|
||||
* function with #AWS_IOT_SHADOW_FLAG_WAITABLE and @ref shadow_function_wait.
|
||||
*/
|
||||
/* @[declare_shadow_updateasync] */
|
||||
AwsIotShadowError_t AwsIotShadow_UpdateAsync( IotMqttConnection_t mqttConnection,
|
||||
const AwsIotShadowDocumentInfo_t * pUpdateInfo,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pCallbackInfo,
|
||||
AwsIotShadowOperation_t * const pUpdateOperation );
|
||||
/* @[declare_shadow_updateasync] */
|
||||
|
||||
/**
|
||||
* @brief Send a Thing Shadow update with a timeout.
|
||||
*
|
||||
* This function queues a Shadow update, then waits for the result. Internally, this
|
||||
* function is a call to @ref shadow_function_updateasync followed by @ref shadow_function_wait.
|
||||
* See @ref shadow_function_updateasync for more information on the Shadow update operation.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for Shadow update.
|
||||
* @param[in] pUpdateInfo Shadow document parameters.
|
||||
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
|
||||
* @param[in] timeoutMs If the Shadow service does not respond to the Shadow update
|
||||
* within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - #AWS_IOT_SHADOW_TIMEOUT
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*/
|
||||
/* @[declare_shadow_updatesync] */
|
||||
AwsIotShadowError_t AwsIotShadow_UpdateSync( IotMqttConnection_t mqttConnection,
|
||||
const AwsIotShadowDocumentInfo_t * pUpdateInfo,
|
||||
uint32_t flags,
|
||||
uint32_t timeoutMs );
|
||||
/* @[declare_shadow_updatesync] */
|
||||
|
||||
/**
|
||||
* @brief Wait for a Shadow operation to complete.
|
||||
*
|
||||
* This function blocks to wait for a [delete](@ref shadow_function_deleteasync),
|
||||
* [get](@ref shadow_function_getasync), or [update](@ref shadow_function_updateasync) to
|
||||
* complete. These operations are by default asynchronous; the function calls
|
||||
* queue an operation for processing, and a callback is invoked once the operation
|
||||
* is complete.
|
||||
*
|
||||
* To use this function, the flag #AWS_IOT_SHADOW_FLAG_WAITABLE must have been
|
||||
* set in the operation's function call. Additionally, this function must always
|
||||
* be called with any waitable operation to clean up resources.
|
||||
*
|
||||
* Regardless of its return value, this function always clean up resources used
|
||||
* by the waitable operation. This means `operation` is invalidated as soon as
|
||||
* this function returns, even if it returns #AWS_IOT_SHADOW_TIMEOUT or another
|
||||
* error.
|
||||
*
|
||||
* @param[in] operation Reference to the Shadow operation to wait for. The flag
|
||||
* #AWS_IOT_SHADOW_FLAG_WAITABLE must have been set for this operation.
|
||||
* @param[in] timeoutMs How long to wait before returning #AWS_IOT_SHADOW_TIMEOUT.
|
||||
* @param[out] pShadowDocument A pointer to a buffer containing the Shadow document
|
||||
* retrieved by a [Shadow get](@ref shadow_function_getasync) is placed here. The buffer
|
||||
* was allocated with the function #AwsIotShadowDocumentInfo_t.mallocDocument passed
|
||||
* to @ref shadow_function_getasync. This parameter is only valid for a [Shadow get]
|
||||
* (@ref shadow_function_getasync) and ignored for other Shadow operations. This output
|
||||
* parameter is only valid if this function returns #AWS_IOT_SHADOW_SUCCESS.
|
||||
* @param[out] pShadowDocumentLength The length of the Shadow document in
|
||||
* `pShadowDocument` is placed here. This parameter is only valid for a [Shadow get]
|
||||
* (@ref shadow_function_getasync) and ignored for other Shadow operations. This output
|
||||
* parameter is only valid if this function returns #AWS_IOT_SHADOW_SUCCESS.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_BAD_RESPONSE
|
||||
* - #AWS_IOT_SHADOW_TIMEOUT
|
||||
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
|
||||
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
|
||||
*
|
||||
* <b>Example 1 (Shadow Update)</b>
|
||||
* @code{c}
|
||||
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
* AwsIotShadowDocumentInfo_t updateInfo = { ... };
|
||||
*
|
||||
* // Reference and timeout.
|
||||
* AwsIotShadowOperation_t updateOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
|
||||
* uint32_t timeout = 5000; // 5 seconds
|
||||
*
|
||||
* // Shadow update operation.
|
||||
* result = AwsIotShadow_UpdateAsync( mqttConnection,
|
||||
* &updateInfo,
|
||||
* AWS_IOT_SHADOW_FLAG_WAITABLE,
|
||||
* NULL,
|
||||
* &updateOperation );
|
||||
*
|
||||
* // Update should have returned AWS_IOT_SHADOW_STATUS_PENDING. The call to wait
|
||||
* // returns once the result of the update is available or the timeout expires.
|
||||
* if( result == AWS_IOT_SHADOW_STATUS_PENDING )
|
||||
* {
|
||||
* // The last two parameters are ignored for a Shadow update.
|
||||
* result = AwsIotShadow_Wait( updateOperation, timeout, NULL, NULL );
|
||||
*
|
||||
* // After the call to wait, the result of the update is known
|
||||
* // (not AWS_IOT_SHADOW_STATUS_PENDING).
|
||||
* assert( result != AWS_IOT_SHADOW_STATUS_PENDING );
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* <b>Example 2 (Shadow Get)</b>
|
||||
* @code{c}
|
||||
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
* AwsIotShadowDocumentInfo_t getInfo = { ... };
|
||||
*
|
||||
* // Reference and timeout.
|
||||
* AwsIotShadowOperation_t getOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
|
||||
* uint32_t timeout = 5000; // 5 seconds
|
||||
*
|
||||
* // Buffer pointer and size for retrieved Shadow document.
|
||||
* const char * pShadowDocument = NULL;
|
||||
* size_t documentLength = 0;
|
||||
*
|
||||
* // Buffer allocation function must be set for a waitable Shadow get.
|
||||
* getInfo.get.mallocDocument = malloc;
|
||||
*
|
||||
* // Shadow get operation.
|
||||
* result = AwsIotShadow_GetAsync( mqttConnection,
|
||||
* &getInfo,
|
||||
* AWS_IOT_SHADOW_FLAG_WAITABLE,
|
||||
* NULL,
|
||||
* &getOperation );
|
||||
*
|
||||
* // Get should have returned AWS_IOT_SHADOW_STATUS_PENDING. The call to wait
|
||||
* // returns once the result of the get is available or the timeout expires.
|
||||
* if( result == AWS_IOT_SHADOW_STATUS_PENDING )
|
||||
* {
|
||||
* // The last two parameters must be set for a Shadow get.
|
||||
* result = AwsIotShadow_Wait( getOperation, timeout, &pShadowDocument, &documentLength );
|
||||
*
|
||||
* // After the call to wait, the result of the get is known
|
||||
* // (not AWS_IOT_SHADOW_STATUS_PENDING).
|
||||
* assert( result != AWS_IOT_SHADOW_STATUS_PENDING );
|
||||
*
|
||||
* // The retrieved Shadow document is only valid for a successful Shadow get.
|
||||
* if( result == AWS_IOT_SHADOW_SUCCESS )
|
||||
* {
|
||||
* // Do something with the Shadow document...
|
||||
*
|
||||
* // Free the Shadow document when finished.
|
||||
* free( pShadowDocument );
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_shadow_wait] */
|
||||
AwsIotShadowError_t AwsIotShadow_Wait( AwsIotShadowOperation_t operation,
|
||||
uint32_t timeoutMs,
|
||||
const char ** const pShadowDocument,
|
||||
size_t * const pShadowDocumentLength );
|
||||
/* @[declare_shadow_wait] */
|
||||
|
||||
/**
|
||||
* @brief Set a callback to be invoked when the Thing Shadow `desired` and `reported`
|
||||
* states differ.
|
||||
*
|
||||
* A Thing Shadow contains `reported` and `desired` states, meant to represent
|
||||
* the current device status and some desired status, respectively. When the
|
||||
* `reported` and `desired` states differ, the Thing Shadow service generates a
|
||||
* <i>delta document</i> and publishes it to the topic `update/delta`. Devices
|
||||
* with a subscription for this topic will receive the delta document and may act
|
||||
* based on the different `reported` and `desired` states. See [this page]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/using-device-shadows.html#delta-state)
|
||||
* for more information about using delta documents.
|
||||
*
|
||||
* A <i>delta callback</i> may be invoked whenever a delta document is generated.
|
||||
* Each Thing may have a single delta callback set. This function modifies the delta
|
||||
* callback for a specific Thing depending on the `pDeltaCallback` parameter and
|
||||
* the presence of any existing delta callback:
|
||||
* - When no existing delta callback exists for a specific Thing, a new delta
|
||||
* callback is added.
|
||||
* - If there is an existing delta callback and `pDeltaCallback` is not `NULL`, then
|
||||
* the existing callback function and parameter are replaced with `pDeltaCallback`.
|
||||
* - If there is an existing subscription and `pDeltaCallback` is `NULL`, then the
|
||||
* delta callback is removed.
|
||||
*
|
||||
* This function is always blocking; it may block for up to the default MQTT
|
||||
* timeout. This timeout is set as a parameter to @ref shadow_function_init,
|
||||
* and defaults to @ref AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS if not set. If
|
||||
* this function's underlying MQTT operations fail to complete within this
|
||||
* timeout, then this function returns #AWS_IOT_SHADOW_TIMEOUT.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for the subscription to
|
||||
* `update/delta`.
|
||||
* @param[in] pThingName The subscription to `update/delta` will be added for
|
||||
* this Thing Name.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags This parameter is for future-compatibility. Currently, flags
|
||||
* are not supported for this function and this parameter is ignored.
|
||||
* @param[in] pDeltaCallback Callback function to invoke for incoming delta
|
||||
* documents.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_TIMEOUT
|
||||
*
|
||||
* @return This function always returns #AWS_IOT_SHADOW_SUCCESS when replacing or
|
||||
* removing existing delta callbacks.
|
||||
*
|
||||
* @see @ref shadow_function_setupdatedcallback for the function to register
|
||||
* callbacks for all Shadow updates.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
* AwsIotShadowCallbackInfo_t deltaCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // _deltaCallbackFunction will be invoked when a delta document is received.
|
||||
* deltaCallback.function = _deltaCallbackFunction;
|
||||
*
|
||||
* // Set the delta callback for the Thing "Test_device".
|
||||
* result = AwsIotShadow_SetDeltaCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* &deltaCallback );
|
||||
*
|
||||
* // Check if callback was successfully set.
|
||||
* if( result == AWS_IOT_SHADOW_SUCCESS )
|
||||
* {
|
||||
* AwsIotShadowDocumentInfo_t updateInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
|
||||
*
|
||||
* // Set the Thing Name for Shadow update.
|
||||
* updateInfo.pThingName = THING_NAME;
|
||||
* updateInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // Set the Shadow document to send. This document has different "reported"
|
||||
* // and "desired" states. It represents a scenario where a device is currently
|
||||
* // off, but is being ordered to turn on.
|
||||
* updateInfo.update.pUpdateDocument =
|
||||
* "{"
|
||||
* "\"state\": {"
|
||||
* "\"reported\": { \"deviceOn\": false },"
|
||||
* "\"desired\": { \"deviceOn\": true }"
|
||||
* "}"
|
||||
* "}";
|
||||
* updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
|
||||
*
|
||||
* // Send the Shadow document with different "reported" and desired states.
|
||||
* result = AwsIotShadow_UpdateSync( mqttConnection,
|
||||
* &updateInfo,
|
||||
* 0,
|
||||
* 0,
|
||||
* 5000 );
|
||||
*
|
||||
* // After the update is successfully sent, the function _deltaCallbackFunction
|
||||
* // will be invoked once the Shadow service generates and sends a delta document.
|
||||
* // The delta document will contain the different "deviceOn" states, as well as
|
||||
* // metadata.
|
||||
*
|
||||
* // Once the delta callback is no longer needed, it may be removed by passing
|
||||
* // NULL as pDeltaCallback.
|
||||
* result = AwsIotShadow_SetDeltaCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* NULL );
|
||||
*
|
||||
* // The return value from removing a delta callback should always be success.
|
||||
* assert( result == AWS_IOT_SHADOW_SUCCESS );
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_shadow_setdeltacallback] */
|
||||
AwsIotShadowError_t AwsIotShadow_SetDeltaCallback( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pDeltaCallback );
|
||||
/* @[declare_shadow_setdeltacallback] */
|
||||
|
||||
/**
|
||||
* @brief Set a callback to be invoked when a Thing Shadow changes.
|
||||
*
|
||||
* The Shadow service publishes a state document to the `update/documents` topic
|
||||
* whenever a Thing Shadow is successfully updated. This document reports the
|
||||
* complete previous and current Shadow documents in `previous` and `current`
|
||||
* sections, respectively. Therefore, the `update/documents` topic is useful
|
||||
* for monitoring Shadow updates.
|
||||
*
|
||||
* An <i>updated callback</i> may be invoked whenever a document is published to
|
||||
* `update/documents`. Each Thing may have a single updated callback set. This function
|
||||
* modifies the updated callback for a specific Thing depending on the `pUpdatedCallback`
|
||||
* parameter and the presence of any existing updated callback.
|
||||
* - When no existing updated callback exists for a specific Thing, a new updated
|
||||
* callback is added.
|
||||
* - If there is an existing updated callback and `pUpdatedCallback` is not `NULL`,
|
||||
* then the existing callback function and parameter are replaced with `pUpdatedCallback`.
|
||||
* - If there is an existing updated callback and `pUpdatedCallback` is `NULL`,
|
||||
* then the updated callback is removed.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection to use for the subscription to `update/documents`.
|
||||
* @param[in] pThingName The subscription to `update/documents` will be added for
|
||||
* this Thing Name.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
|
||||
* not supported for this function and this parameter is ignored.
|
||||
* @param[in] pUpdatedCallback Callback function to invoke for incoming updated documents.
|
||||
*
|
||||
* @return One of the following:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
|
||||
* - #AWS_IOT_SHADOW_BAD_PARAMETER
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
* - #AWS_IOT_SHADOW_TIMEOUT
|
||||
*
|
||||
* @note Documents published to `update/documents` will be large, as they contain 2
|
||||
* complete Shadow state documents. If an updated callback is used, ensure that the
|
||||
* device has sufficient memory for incoming documents.
|
||||
*
|
||||
* @see @ref shadow_function_setdeltacallback for the function to register callbacks
|
||||
* for delta documents.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* #define THING_NAME "Test_device"
|
||||
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
|
||||
*
|
||||
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
|
||||
* AwsIotShadowCallbackInfo_t updatedCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
|
||||
*
|
||||
* // _updatedCallbackFunction will be invoked when an updated document is received.
|
||||
* updatedCallback.function = _updatedCallbackFunction;
|
||||
*
|
||||
* // Set the updated callback for the Thing "Test_device".
|
||||
* result = AwsIotShadow_SetUpdatedCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* 0,
|
||||
* &updatedCallback );
|
||||
*
|
||||
* // Check if the callback was successfully set.
|
||||
* if( result == AWS_IOT_SHADOW_SUCCESS )
|
||||
* {
|
||||
* AwsIotShadowDocumentInfo_t updateInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
|
||||
*
|
||||
* // Set the Thing Name for Shadow update.
|
||||
* updateInfo.pThingName = THING_NAME;
|
||||
* updateInfo.thingNameLength = THING_NAME_LENGTH;
|
||||
*
|
||||
* // Set the Shadow document to send. Any Shadow update will trigger the
|
||||
* // updated callback.
|
||||
* updateInfo.update.pUpdateDocument =
|
||||
* "{"
|
||||
* "\"state\": {"
|
||||
* "\"reported\": { \"deviceOn\": false }"
|
||||
* "}"
|
||||
* "}";
|
||||
* updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
|
||||
*
|
||||
* // Send the Shadow document. A successful update will trigger the updated callback.
|
||||
* result = AwsIotShadow_UpdateSync( mqttConnection,
|
||||
* &updateInfo,
|
||||
* 0,
|
||||
* 0,
|
||||
* 5000 );
|
||||
*
|
||||
* // After a successful Shadow update, the updated callback will be invoked.
|
||||
*
|
||||
* // Once the updated callback is no longer needed, it may be removed by
|
||||
* // passing NULL as pUpdatedCallback.
|
||||
* result = AwsIotShadow_SetUpdatedCallback( mqttConnection,
|
||||
* THING_NAME,
|
||||
* THING_NAME_LENGTH,
|
||||
* NULL );
|
||||
*
|
||||
* // The return value from removing an updated callback should always be
|
||||
* // success.
|
||||
* assert( result == AWS_IOT_SHADOW_SUCCESS );
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
/* @[declare_shadow_setupdatedcallback] */
|
||||
AwsIotShadowError_t AwsIotShadow_SetUpdatedCallback( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags,
|
||||
const AwsIotShadowCallbackInfo_t * pUpdatedCallback );
|
||||
/* @[declare_shadow_setupdatedcallback] */
|
||||
|
||||
/**
|
||||
* @brief Remove persistent Thing Shadow operation topic subscriptions.
|
||||
*
|
||||
* Passing the flag @ref AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS to @ref shadow_function_deleteasync,
|
||||
* @ref shadow_function_getasync, @ref shadow_function_updateasync, or their blocking versions.
|
||||
* causes the Shadow operation topic subscriptions to be maintained for future calls to the
|
||||
* same function. If a persistent subscription for a Shadow topic are no longer needed,
|
||||
* this function may be used to remove it.
|
||||
*
|
||||
* @param[in] mqttConnection The MQTT connection associated with the persistent subscription.
|
||||
* @param[in] pThingName The Thing Name associated with the persistent subscription.
|
||||
* @param[in] thingNameLength The length of `pThingName`.
|
||||
* @param[in] flags Flags that determine which subscriptions to remove. Valid values are
|
||||
* the bitwise OR of the following individual flags:
|
||||
* - @ref AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS
|
||||
* - @ref AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS
|
||||
* - @ref AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
|
||||
*
|
||||
* @return On success:
|
||||
* - #AWS_IOT_SHADOW_SUCCESS
|
||||
* @return If an MQTT UNSUBSCRIBE packet cannot be sent, one of the following:
|
||||
* - #AWS_IOT_SHADOW_NO_MEMORY
|
||||
* - #AWS_IOT_SHADOW_MQTT_ERROR
|
||||
*
|
||||
* @note @ref shadow_function_cleanup removes all persistent subscriptions as well.
|
||||
*
|
||||
* @warning This function is not safe to call with any in-progress operations!
|
||||
* It also does not affect delta and updated callbacks registered with @ref
|
||||
* shadow_function_setdeltacallback and @ref shadow_function_setupdatedcallback,
|
||||
* respectively. (See documentation for those functions on how to remove their
|
||||
* callbacks).
|
||||
*/
|
||||
/* @[declare_shadow_removepersistentsubscriptions] */
|
||||
AwsIotShadowError_t AwsIotShadow_RemovePersistentSubscriptions( IotMqttConnection_t mqttConnection,
|
||||
const char * pThingName,
|
||||
size_t thingNameLength,
|
||||
uint32_t flags );
|
||||
/* @[declare_shadow_removepersistentsubscriptions] */
|
||||
|
||||
/*------------------------- Shadow helper functions -------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Returns a string that describes an #AwsIotShadowError_t.
|
||||
*
|
||||
* Like POSIX's `strerror`, this function returns a string describing a return
|
||||
* code. In this case, the return code is a Shadow library error code, `status`.
|
||||
*
|
||||
* The string returned by this function <b>MUST</b> be treated as read-only: any
|
||||
* attempt to modify its contents may result in a crash. Therefore, this function
|
||||
* is limited to usage in logging.
|
||||
*
|
||||
* @param[in] status The status to describe.
|
||||
*
|
||||
* @return A read-only string that describes `status`.
|
||||
*
|
||||
* @warning The string returned by this function must never be modified.
|
||||
*/
|
||||
/* @[declare_shadow_strerror] */
|
||||
const char * AwsIotShadow_strerror( AwsIotShadowError_t status );
|
||||
/* @[declare_shadow_strerror] */
|
||||
|
||||
/**
|
||||
* @cond DOXYGEN_IGNORE
|
||||
* Doxygen should ignore this section.
|
||||
*
|
||||
* Backwards compatibility macros for previous function names.
|
||||
*/
|
||||
#define AwsIotShadow_Delete AwsIotShadow_DeleteAsync
|
||||
#define AwsIotShadow_TimedDelete AwsIotShadow_DeleteSync
|
||||
#define AwsIotShadow_Get AwsIotShadow_GetAsync
|
||||
#define AwsIotShadow_TimedGet AwsIotShadow_GetSync
|
||||
#define AwsIotShadow_Update AwsIotShadow_UpdateAsync
|
||||
#define AwsIotShadow_TimedUpdate AwsIotShadow_UpdateSync
|
||||
/** @endcond */
|
||||
|
||||
#endif /* ifndef AWS_IOT_SHADOW_H_ */
|
|
@ -0,0 +1,641 @@
|
|||
/*
|
||||
* AWS IoT Shadow V2.1.0
|
||||
* Copyright (C) 2019 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_types.h
|
||||
* @brief Types of the Thing Shadow library.
|
||||
*/
|
||||
|
||||
#ifndef AWS_IOT_SHADOW_TYPES_H_
|
||||
#define AWS_IOT_SHADOW_TYPES_H_
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* MQTT types include. */
|
||||
#include "types/iot_mqtt_types.h"
|
||||
|
||||
/*--------------------------- Shadow handle types ---------------------------*/
|
||||
|
||||
/**
|
||||
* @handles{shadow,Shadow library}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup shadow_datatypes_handles
|
||||
* @brief Opaque handle that references an in-progress Shadow operation.
|
||||
*
|
||||
* Set as an output parameter of @ref shadow_function_deleteasync, @ref shadow_function_getasync,
|
||||
* and @ref shadow_function_updateasync. These functions send a message to the Shadow
|
||||
* service requesting a Shadow operation; the result of this operation is unknown
|
||||
* until the Shadow service sends a response. Therefore, this handle serves as a
|
||||
* reference to Shadow operations awaiting a response from the Shadow service.
|
||||
*
|
||||
* This reference will be valid from the successful return of @ref shadow_function_deleteasync,
|
||||
* @ref shadow_function_getasync, or @ref shadow_function_updateasync. The reference becomes
|
||||
* invalid once the [completion callback](@ref AwsIotShadowCallbackInfo_t) is invoked,
|
||||
* or @ref shadow_function_wait returns.
|
||||
*
|
||||
* @initializer{AwsIotShadowOperation_t,AWS_IOT_SHADOW_OPERATION_INITIALIZER}
|
||||
*
|
||||
* @see @ref shadow_function_wait and #AWS_IOT_SHADOW_FLAG_WAITABLE for waiting on
|
||||
* a reference; or #AwsIotShadowCallbackInfo_t and #AwsIotShadowCallbackParam_t for an
|
||||
* asynchronous notification of completion.
|
||||
*/
|
||||
typedef struct _shadowOperation * AwsIotShadowOperation_t;
|
||||
|
||||
/*------------------------- Shadow enumerated types -------------------------*/
|
||||
|
||||
/**
|
||||
* @enums{shadow,Shadow library}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup shadow_datatypes_enums
|
||||
* @brief Return codes of [Shadow functions](@ref shadow_functions).
|
||||
*
|
||||
* The function @ref shadow_function_strerror can be used to get a return code's
|
||||
* description.
|
||||
*
|
||||
* The values between 400 (#AWS_IOT_SHADOW_BAD_REQUEST) and 500
|
||||
* (#AWS_IOT_SHADOW_SERVER_ERROR) may be returned by the Shadow service when it
|
||||
* rejects a Shadow operation. See [this page]
|
||||
* (https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-error-messages.html)
|
||||
* for more information.
|
||||
*/
|
||||
typedef enum AwsIotShadowError
|
||||
{
|
||||
/**
|
||||
* @brief Shadow operation completed successfully.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_init
|
||||
* - @ref shadow_function_wait
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_setdeltacallback
|
||||
* - @ref shadow_function_setupdatedcallback
|
||||
* - @ref shadow_function_removepersistentsubscriptions
|
||||
*
|
||||
* Will also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
* when successful.
|
||||
*/
|
||||
AWS_IOT_SHADOW_SUCCESS = 0,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation queued, awaiting result.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deleteasync
|
||||
* - @ref shadow_function_getasync
|
||||
* - @ref shadow_function_updateasync
|
||||
*/
|
||||
AWS_IOT_SHADOW_STATUS_PENDING = 1,
|
||||
|
||||
/**
|
||||
* @brief Initialization failed.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_init
|
||||
*/
|
||||
AWS_IOT_SHADOW_INIT_FAILED = 2,
|
||||
|
||||
/**
|
||||
* @brief At least one parameter is invalid.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getasync and @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
* - @ref shadow_function_setdeltacallback
|
||||
* - @ref shadow_function_setupdatedcallback
|
||||
*/
|
||||
AWS_IOT_SHADOW_BAD_PARAMETER = 3,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation failed because of memory allocation failure.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getasync and @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_setdeltacallback
|
||||
* - @ref shadow_function_setupdatedcallback
|
||||
*/
|
||||
AWS_IOT_SHADOW_NO_MEMORY = 4,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation failed because of failure in MQTT library.
|
||||
*
|
||||
* Check the Shadow library logs for the error code returned by the MQTT
|
||||
* library.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getasync and @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_setdeltacallback
|
||||
* - @ref shadow_function_setupdatedcallback
|
||||
* - @ref shadow_function_removepersistentsubscriptions
|
||||
*/
|
||||
AWS_IOT_SHADOW_MQTT_ERROR = 5,
|
||||
|
||||
/**
|
||||
* @brief Response received from Shadow service not understood.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_BAD_RESPONSE = 7,
|
||||
|
||||
/**
|
||||
* @brief A blocking Shadow operation timed out.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
* - @ref shadow_function_setdeltacallback
|
||||
* - @ref shadow_function_setupdatedcallback
|
||||
*/
|
||||
AWS_IOT_SHADOW_TIMEOUT = 8,
|
||||
|
||||
/**
|
||||
* @brief An API function was called before @ref shadow_function_init.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getasync and @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
* - @ref shadow_function_setdeltacallback
|
||||
* - @ref shadow_function_setupdatedcallback
|
||||
*/
|
||||
AWS_IOT_SHADOW_NOT_INITIALIZED = 11,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Bad request.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_BAD_REQUEST = 400,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Unauthorized.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_UNAUTHORIZED = 401,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Forbidden.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_FORBIDDEN = 403,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Thing not found.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_NOT_FOUND = 404,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Version conflict.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_CONFLICT = 409,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: The payload exceeds the maximum size
|
||||
* allowed.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_TOO_LARGE = 413,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Unsupported document encoding; supported
|
||||
* encoding is UTF-8.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_UNSUPPORTED = 415,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: The Device Shadow service will generate
|
||||
* this error message when there are more than 10 in-flight requests.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_TOO_MANY_REQUESTS = 429,
|
||||
|
||||
/**
|
||||
* @brief Shadow operation rejected: Internal service failure.
|
||||
*
|
||||
* Functions that may return this value:
|
||||
* - @ref shadow_function_deletesync
|
||||
* - @ref shadow_function_getsync
|
||||
* - @ref shadow_function_updatesync
|
||||
* - @ref shadow_function_wait
|
||||
*
|
||||
* May also be the value of a Shadow operation completion callback's<br>
|
||||
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
|
||||
*/
|
||||
AWS_IOT_SHADOW_SERVER_ERROR = 500,
|
||||
} AwsIotShadowError_t;
|
||||
|
||||
/**
|
||||
* @ingroup shadow_datatypes_enums
|
||||
* @brief Types of Shadow library callbacks.
|
||||
*
|
||||
* One of these values will be placed in #AwsIotShadowCallbackParam_t.callbackType
|
||||
* to identify the reason for invoking a callback function.
|
||||
*/
|
||||
typedef enum AwsIotShadowCallbackType
|
||||
{
|
||||
AWS_IOT_SHADOW_DELETE_COMPLETE, /**< Callback invoked because a [Shadow delete](@ref shadow_function_deleteasync) completed. */
|
||||
AWS_IOT_SHADOW_GET_COMPLETE, /**< Callback invoked because a [Shadow get](@ref shadow_function_getasync) completed. */
|
||||
AWS_IOT_SHADOW_UPDATE_COMPLETE, /**< Callback invoked because a [Shadow update](@ref shadow_function_updateasync) completed. */
|
||||
AWS_IOT_SHADOW_DELTA_CALLBACK, /**< Callback invoked for an incoming message on a [Shadow delta](@ref shadow_function_setdeltacallback) topic. */
|
||||
AWS_IOT_SHADOW_UPDATED_CALLBACK /**< Callback invoked for an incoming message on a [Shadow updated](@ref shadow_function_setupdatedcallback) topic. */
|
||||
} AwsIotShadowCallbackType_t;
|
||||
|
||||
/*------------------------- Shadow parameter structs ------------------------*/
|
||||
|
||||
/**
|
||||
* @paramstructs{shadow,Shadow}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup shadow_datatypes_paramstructs
|
||||
* @brief Parameter to a Shadow callback function.
|
||||
*
|
||||
* @paramfor Shadow callback functions
|
||||
*
|
||||
* The Shadow library passes this struct to a callback function whenever a
|
||||
* Shadow operation completes or a message is received on a Shadow delta or
|
||||
* updated topic.
|
||||
*
|
||||
* The valid members of this struct are different based on
|
||||
* #AwsIotShadowCallbackParam_t.callbackType. If the callback type is
|
||||
* #AWS_IOT_SHADOW_DELETE_COMPLETE, #AWS_IOT_SHADOW_GET_COMPLETE, or
|
||||
* #AWS_IOT_SHADOW_UPDATE_COMPLETE, then #AwsIotShadowCallbackParam_t.operation
|
||||
* is valid. Otherwise, if the callback type is #AWS_IOT_SHADOW_DELTA_CALLBACK
|
||||
* or #AWS_IOT_SHADOW_UPDATED_CALLBACK, then #AwsIotShadowCallbackParam_t.callback
|
||||
* is valid.
|
||||
*
|
||||
* @attention Any pointers in this callback parameter may be freed as soon as the
|
||||
* [callback function](@ref AwsIotShadowCallbackInfo_t.function) returns. Therefore,
|
||||
* data must be copied if it is needed after the callback function returns.
|
||||
* @attention The Shadow library may set strings that are not NULL-terminated.
|
||||
*
|
||||
* @see #AwsIotShadowCallbackInfo_t for the signature of a callback function.
|
||||
*/
|
||||
typedef struct AwsIotShadowCallbackParam
|
||||
{
|
||||
AwsIotShadowCallbackType_t callbackType; /**< @brief Reason for invoking the Shadow callback function to provide context. */
|
||||
|
||||
const char * pThingName; /**< @brief The Thing Name associated with this Shadow callback. */
|
||||
size_t thingNameLength; /**< @brief Length of #AwsIotShadowCallbackParam_t.pThingName. */
|
||||
|
||||
IotMqttConnection_t mqttConnection; /**< @brief The MQTT connection associated with the Shadow callback. */
|
||||
|
||||
union
|
||||
{
|
||||
/* Valid for completed Shadow operations. */
|
||||
struct
|
||||
{
|
||||
/* Valid for a completed Shadow GET operation. */
|
||||
struct
|
||||
{
|
||||
const char * pDocument; /**< @brief Retrieved Shadow document. */
|
||||
size_t documentLength; /**< @brief Length of retrieved Shadow document. */
|
||||
} get; /**< @brief Retrieved Shadow document, valid only for a completed [Shadow Get](@ref shadow_function_getasync). */
|
||||
|
||||
AwsIotShadowError_t result; /**< @brief Result of Shadow operation, e.g. succeeded or failed. */
|
||||
AwsIotShadowOperation_t reference; /**< @brief Reference to the Shadow operation that completed. */
|
||||
} operation; /**< @brief Information on a completed Shadow operation. */
|
||||
|
||||
/* Valid for a message on a Shadow delta or updated topic. */
|
||||
struct
|
||||
{
|
||||
const char * pDocument; /**< @brief Shadow delta or updated document. */
|
||||
size_t documentLength; /**< @brief Length of Shadow delta or updated document. */
|
||||
} callback; /**< @brief Shadow document from an incoming delta or updated topic. */
|
||||
} u; /**< @brief Valid member depends on callback type. */
|
||||
} AwsIotShadowCallbackParam_t;
|
||||
|
||||
/**
|
||||
* @ingroup shadow_datatypes_paramstructs
|
||||
* @brief Information on a user-provided Shadow callback function.
|
||||
*
|
||||
* @paramfor @ref shadow_function_deleteasync, @ref shadow_function_getasync, @ref
|
||||
* shadow_function_updateasync, @ref shadow_function_setdeltacallback, @ref
|
||||
* shadow_function_setupdatedcallback
|
||||
*
|
||||
* Provides a function to be invoked when a Shadow operation completes or when a
|
||||
* Shadow document is received on a callback topic (delta or updated).
|
||||
*
|
||||
* @initializer{AwsIotShadowCallbackInfo_t,AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER}
|
||||
*/
|
||||
typedef struct AwsIotShadowCallbackInfo
|
||||
{
|
||||
void * pCallbackContext; /**< @brief The first parameter to pass to the callback function. */
|
||||
|
||||
/**
|
||||
* @brief User-provided callback function signature.
|
||||
*
|
||||
* @param[in] pCallbackContext #AwsIotShadowCallbackInfo_t.pCallbackContext
|
||||
* @param[in] pCallbackParam Details on the outcome of the Shadow
|
||||
* operation or an incoming Shadow document.
|
||||
*
|
||||
* @see #AwsIotShadowCallbackParam_t for more information on the second parameter.
|
||||
*/
|
||||
void ( * function )( void * pCallbackContext,
|
||||
AwsIotShadowCallbackParam_t * pCallbackParam );
|
||||
} AwsIotShadowCallbackInfo_t;
|
||||
|
||||
/**
|
||||
* @ingroup shadow_datatypes_paramstructs
|
||||
* @brief Information on a Shadow document for @ref shadow_function_getasync or @ref
|
||||
* shadow_function_updateasync.
|
||||
*
|
||||
* @paramfor @ref shadow_function_getasync, @ref shadow_function_updateasync
|
||||
*
|
||||
* The valid members of this struct are different based on whether this struct
|
||||
* is passed to @ref shadow_function_getasync or @ref shadow_function_updateasync. When
|
||||
* passed to @ref shadow_function_getasync, the `get` member is valid. When passed to
|
||||
* @ref shadow_function_updateasync, the `update` member is valid. All other members
|
||||
* must always be set.
|
||||
*
|
||||
* @initializer{AwsIotShadowDocumentInfo_t,AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER}
|
||||
*/
|
||||
typedef struct AwsIotShadowDocumentInfo
|
||||
{
|
||||
const char * pThingName; /**< @brief The Thing Name associated with this Shadow document. */
|
||||
size_t thingNameLength; /**< @brief Length of #AwsIotShadowDocumentInfo_t.pThingName. */
|
||||
|
||||
IotMqttQos_t qos; /**< @brief QoS when sending a Shadow get or update message. See #IotMqttPublishInfo_t.qos. */
|
||||
uint32_t retryLimit; /**< @brief Maximum number of retries for a Shadow get or update message. See #IotMqttPublishInfo_t.retryLimit. */
|
||||
uint32_t retryMs; /**< @brief First retry time for a Shadow get or update message. See IotMqttPublishInfo_t.retryMs. */
|
||||
|
||||
union
|
||||
{
|
||||
/* Valid for Shadow get. */
|
||||
struct
|
||||
{
|
||||
/**
|
||||
* @brief Function to allocate memory for an incoming Shadow document.
|
||||
*
|
||||
* @param[in] documentLength Length of the document that needs to
|
||||
* be allocated.
|
||||
* This only needs to be set if #AWS_IOT_SHADOW_FLAG_WAITABLE is passed to
|
||||
* @ref shadow_function_getasync.
|
||||
*/
|
||||
void *( *mallocDocument )( size_t documentLength );
|
||||
} get; /**< @brief Valid members for @ref shadow_function_getasync. */
|
||||
|
||||
/* Valid for Shadow update. */
|
||||
struct
|
||||
{
|
||||
const char * pUpdateDocument; /**< @brief The Shadow document to send in the update. */
|
||||
size_t updateDocumentLength; /**< @brief Length of Shadow update document. */
|
||||
} update; /**< @brief Valid members for @ref shadow_function_updateasync. */
|
||||
} u; /**< @brief Valid member depends on operation type. */
|
||||
} AwsIotShadowDocumentInfo_t;
|
||||
|
||||
/*------------------------ Shadow defined constants -------------------------*/
|
||||
|
||||
/**
|
||||
* @constantspage{shadow,Shadow library}
|
||||
*
|
||||
* @section shadow_constants_initializers Shadow Initializers
|
||||
* @brief Provides default values for the data types of the Shadow library.
|
||||
*
|
||||
* @snippet this define_shadow_initializers
|
||||
*
|
||||
* All user-facing data types of the Shadow library should be initialized
|
||||
* using one of the following.
|
||||
*
|
||||
* @warning Failing to initialize a Shadow data type with the appropriate
|
||||
* initializer may result in undefined behavior!
|
||||
* @note The initializers may change at any time in future versions, but their
|
||||
* names will remain the same.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* @code{c}
|
||||
* AwsIotShadowCallbackInfo_t callbackInfo = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
|
||||
* AwsIotShadowDocumentInfo_t documentInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
|
||||
* AwsIotShadowOperation_t operation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
|
||||
* @endcode
|
||||
*
|
||||
* @section shadow_constants_flags Shadow Function Flags
|
||||
* @brief Flags that modify the behavior of Shadow library functions.
|
||||
*
|
||||
* Flags should be bitwise-ORed with each other to change the behavior of
|
||||
* Shadow library functions.
|
||||
*
|
||||
* The following flags are valid for the Shadow operation functions:
|
||||
* @ref shadow_function_deleteasync, @ref shadow_function_getasync, @ref shadow_function_updateasync,
|
||||
* and their blocking versions.
|
||||
* - #AWS_IOT_SHADOW_FLAG_WAITABLE <br>
|
||||
* @copybrief AWS_IOT_SHADOW_FLAG_WAITABLE
|
||||
* - #AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS <br>
|
||||
* @copybrief AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS
|
||||
*
|
||||
* The following flags are valid for @ref shadow_function_removepersistentsubscriptions.
|
||||
* These flags are not valid for the Shadow operation functions.
|
||||
* - #AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS <br>
|
||||
* @copybrief AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS
|
||||
* - #AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS <br>
|
||||
* @copybrief AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS
|
||||
* - #AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS <br>
|
||||
* @copybrief AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
|
||||
*
|
||||
* @note The values of the flags may change at any time in future versions, but
|
||||
* their names will remain the same. Additionally, flags which may be used at
|
||||
* the same time will be bitwise-exclusive of each other.
|
||||
*/
|
||||
|
||||
/* @[define_shadow_initializers] */
|
||||
#define AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER { 0 } /**< @brief Initializer for #AwsIotShadowCallbackInfo_t. */
|
||||
#define AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER { 0 } /**< @brief Initializer for #AwsIotShadowDocumentInfo_t. */
|
||||
#define AWS_IOT_SHADOW_OPERATION_INITIALIZER NULL /**< @brief Initializer for #AwsIotShadowOperation_t. */
|
||||
/* @[define_shadow_initializers] */
|
||||
|
||||
/**
|
||||
* @brief Allows the use of @ref shadow_function_wait for blocking until completion.
|
||||
*
|
||||
* This flag is only valid if passed to the functions @ref shadow_function_deleteasync,
|
||||
* @ref shadow_function_getasync, or @ref shadow_function_updateasync.
|
||||
*
|
||||
* An #AwsIotShadowOperation_t <b>MUST</b> be provided if this flag is set.
|
||||
* Additionally, an #AwsIotShadowCallbackInfo_t <b>MUST NOT</b> be provided.
|
||||
*
|
||||
* @note If this flag is set, @ref shadow_function_wait <b>MUST</b> be called to
|
||||
* clean up resources.
|
||||
*/
|
||||
#define AWS_IOT_SHADOW_FLAG_WAITABLE ( 0x00000001 )
|
||||
|
||||
/**
|
||||
* @brief Maintain the subscriptions for the Shadow operation topics, even after
|
||||
* this function returns.
|
||||
*
|
||||
* This flag is only valid if passed to the functions @ref shadow_function_deleteasync,
|
||||
* @ref shadow_function_getasync, @ref shadow_function_updateasync, or their blocking versions.
|
||||
*
|
||||
* The Shadow service reports results of Shadow operations by publishing
|
||||
* messages to MQTT topics. By default, the functions @ref shadow_function_deleteasync,
|
||||
* @ref shadow_function_getasync, and @ref shadow_function_updateasync subscribe to the
|
||||
* necessary topics, wait for the Shadow service to publish the result of the
|
||||
* Shadow operation, then unsubscribe from those topics. This workflow is suitable
|
||||
* for infrequent Shadow operations, but is inefficient for frequent, periodic
|
||||
* Shadow operations (where subscriptions for the Shadow operation topics would be
|
||||
* constantly added and removed).
|
||||
*
|
||||
* This flag causes @ref shadow_function_deleteasync, @ref shadow_function_getasync, or
|
||||
* @ref shadow_function_updateasync to maintain Shadow operation topic subscriptions,
|
||||
* even after the function returns. These subscriptions may then be used by a
|
||||
* future call to the same function.
|
||||
*
|
||||
* This flags only needs to be set once, after which subscriptions are maintained
|
||||
* and reused for a specific Thing Name and Shadow function. The function @ref
|
||||
* shadow_function_removepersistentsubscriptions may be used to remove
|
||||
* subscriptions maintained by this flag.
|
||||
*/
|
||||
#define AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ( 0x00000002 )
|
||||
|
||||
/**
|
||||
* @brief Remove the persistent subscriptions from a Shadow delete operation.
|
||||
*
|
||||
* This flag is only valid if passed to the function @ref
|
||||
* shadow_function_removepersistentsubscriptions.
|
||||
*
|
||||
* This flag may be passed to @ref shadow_function_removepersistentsubscriptions
|
||||
* to remove any subscriptions for a specific Thing Name maintained by a previous
|
||||
* call to @ref shadow_function_deleteasync or @ref shadow_function_deletesync.
|
||||
*
|
||||
* @warning Do not call @ref shadow_function_removepersistentsubscriptions with
|
||||
* this flag for Thing Names with any in-progress Shadow delete operations.
|
||||
*/
|
||||
#define AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS ( 0x00000001 )
|
||||
|
||||
/**
|
||||
* @brief Remove the persistent subscriptions from a Shadow get operation.
|
||||
*
|
||||
* This flag is only valid if passed to the function @ref
|
||||
* shadow_function_removepersistentsubscriptions.
|
||||
*
|
||||
* This flag may be passed to @ref shadow_function_removepersistentsubscriptions
|
||||
* to remove any subscriptions for a specific Thing Name maintained by a previous
|
||||
* call to @ref shadow_function_getasync or @ref shadow_function_getsync.
|
||||
*
|
||||
* @warning Do not call @ref shadow_function_removepersistentsubscriptions with
|
||||
* this flag for Thing Names with any in-progress Shadow get operations.
|
||||
*/
|
||||
#define AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS ( 0x00000002 )
|
||||
|
||||
/**
|
||||
* @brief Remove the persistent subscriptions from a Shadow update operation.
|
||||
*
|
||||
* This flag is only valid if passed to the function @ref
|
||||
* shadow_function_removepersistentsubscriptions.
|
||||
*
|
||||
* This flag may be passed to @ref shadow_function_removepersistentsubscriptions
|
||||
* to remove any subscriptions for a specific Thing Name maintained by a previous
|
||||
* call to @ref shadow_function_updateasync or @ref shadow_function_updatesync.
|
||||
*
|
||||
* @warning Do not call @ref shadow_function_removepersistentsubscriptions with
|
||||
* this flag for Thing Names with any in-progress Shadow update operations.
|
||||
*/
|
||||
#define AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS ( 0x00000004 )
|
||||
|
||||
#endif /* ifndef AWS_IOT_SHADOW_TYPES_H_ */
|
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