The Cayenne MQTT mbed Library provides functions to easily connect to the Cayenne IoT project builder.

Fork of Cayenne-MQTT-mbed by myDevicesIoT

Committer:
pferland
Date:
Tue Apr 25 15:32:32 2017 +0000
Revision:
24:686d9ed1d192
Parent:
16:085bcf2e9a18
Child:
22:0dbabcc6e7b2
Small changes for compatibility with MTSAS socket library

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jburhenn 0:09ef59d2d0f7 1 /*
jburhenn 0:09ef59d2d0f7 2 The MIT License(MIT)
jburhenn 0:09ef59d2d0f7 3
jburhenn 0:09ef59d2d0f7 4 Cayenne MQTT Client Library
jburhenn 0:09ef59d2d0f7 5 Copyright (c) 2016 myDevices
jburhenn 0:09ef59d2d0f7 6
jburhenn 0:09ef59d2d0f7 7 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
jburhenn 0:09ef59d2d0f7 8 documentation files(the "Software"), to deal in the Software without restriction, including without limitation
jburhenn 0:09ef59d2d0f7 9 the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software,
jburhenn 0:09ef59d2d0f7 10 and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
jburhenn 0:09ef59d2d0f7 11 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
jburhenn 0:09ef59d2d0f7 12 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
jburhenn 0:09ef59d2d0f7 13 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
jburhenn 0:09ef59d2d0f7 14 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
jburhenn 0:09ef59d2d0f7 15 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
jburhenn 0:09ef59d2d0f7 16
jburhenn 0:09ef59d2d0f7 17 */
jburhenn 0:09ef59d2d0f7 18
jburhenn 0:09ef59d2d0f7 19 #include <stdlib.h>
jburhenn 0:09ef59d2d0f7 20 #include <string.h>
jburhenn 0:09ef59d2d0f7 21 #include "CayenneUtils.h"
jburhenn 0:09ef59d2d0f7 22
jburhenn 0:09ef59d2d0f7 23 #ifdef DIGITAL_AND_ANALOG_SUPPORT
jburhenn 0:09ef59d2d0f7 24 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 25 #define PARSE_TOPICS_COUNT 13
jburhenn 0:09ef59d2d0f7 26 #else
jburhenn 0:09ef59d2d0f7 27 #define PARSE_TOPICS_COUNT 6
jburhenn 0:09ef59d2d0f7 28 #endif
jburhenn 0:09ef59d2d0f7 29 #else
jburhenn 0:09ef59d2d0f7 30 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 31 #define PARSE_TOPICS_COUNT 7
jburhenn 0:09ef59d2d0f7 32 #else
jburhenn 0:09ef59d2d0f7 33 #define PARSE_TOPICS_COUNT 2
jburhenn 0:09ef59d2d0f7 34 #endif
jburhenn 0:09ef59d2d0f7 35 #endif
jburhenn 0:09ef59d2d0f7 36
jburhenn 0:09ef59d2d0f7 37 #define THINGS_STRING CAYENNE_PSTR("/things/")
jburhenn 0:09ef59d2d0f7 38
jburhenn 0:09ef59d2d0f7 39 typedef struct TopicChannel
jburhenn 0:09ef59d2d0f7 40 {
jburhenn 0:09ef59d2d0f7 41 CayenneTopic topic;
jburhenn 0:09ef59d2d0f7 42 unsigned int channel;
jburhenn 0:09ef59d2d0f7 43 } TopicChannel;
jburhenn 0:09ef59d2d0f7 44
jburhenn 0:09ef59d2d0f7 45 /**
jburhenn 0:09ef59d2d0f7 46 * Build a specified topic string.
jburhenn 0:09ef59d2d0f7 47 * @param[out] topic Returned topic string
jburhenn 0:09ef59d2d0f7 48 * @param[in] length CayenneTopic buffer length
jburhenn 0:09ef59d2d0f7 49 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 50 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 51 * @param[in] suffix The topic suffix
jburhenn 0:09ef59d2d0f7 52 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 53 */
jburhenn 0:09ef59d2d0f7 54 int buildTopic(char* topic, size_t length, const char* username, const char* clientID, const char* suffix) {
jburhenn 0:09ef59d2d0f7 55 size_t topicLength = 0;
jburhenn 0:09ef59d2d0f7 56 if (!topic || !username || !clientID || !suffix)
jburhenn 0:09ef59d2d0f7 57 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 58 topicLength = strlen(username) + strlen(clientID) + strlen(suffix) + 11;
jburhenn 0:09ef59d2d0f7 59 if (topicLength > length)
jburhenn 0:09ef59d2d0f7 60 return CAYENNE_BUFFER_OVERFLOW;
jburhenn 0:09ef59d2d0f7 61
jburhenn 0:09ef59d2d0f7 62 topic[0] = '\0';
jburhenn 0:09ef59d2d0f7 63 strcat(topic, CAYENNE_VERSION);
jburhenn 0:09ef59d2d0f7 64 strcat(topic, "/");
jburhenn 0:09ef59d2d0f7 65 strcat(topic, username);
jburhenn 0:09ef59d2d0f7 66 CAYENNE_STRCAT(topic, THINGS_STRING);
jburhenn 0:09ef59d2d0f7 67 strcat(topic, clientID);
jburhenn 0:09ef59d2d0f7 68 strcat(topic, "/");
jburhenn 0:09ef59d2d0f7 69 strcat(topic, suffix);
jburhenn 0:09ef59d2d0f7 70 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 71 }
jburhenn 0:09ef59d2d0f7 72
jburhenn 0:09ef59d2d0f7 73 /**
jburhenn 0:09ef59d2d0f7 74 * Build a specified topic suffix string.
jburhenn 0:09ef59d2d0f7 75 * @param[out] suffix Returned suffix string
jburhenn 0:09ef59d2d0f7 76 * @param[in] length Suffix buffer length
jburhenn 0:09ef59d2d0f7 77 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 78 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 79 * @return CAYENNE_SUCCESS if suffix string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 80 */
jburhenn 0:09ef59d2d0f7 81 int buildSuffix(char* suffix, size_t length, const CayenneTopic topic, unsigned int channel) {
jburhenn 0:09ef59d2d0f7 82 char* topicString = NULL;
jburhenn 0:09ef59d2d0f7 83 if (!suffix)
jburhenn 0:09ef59d2d0f7 84 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 85 switch (topic)
jburhenn 0:09ef59d2d0f7 86 {
jburhenn 0:09ef59d2d0f7 87 case COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 88 topicString = COMMAND_STRING;
jburhenn 0:09ef59d2d0f7 89 break;
jburhenn 0:09ef59d2d0f7 90 case CONFIG_TOPIC:
jburhenn 0:09ef59d2d0f7 91 topicString = CONFIG_STRING;
jburhenn 0:09ef59d2d0f7 92 break;
jburhenn 0:09ef59d2d0f7 93 case DATA_TOPIC:
jburhenn 0:09ef59d2d0f7 94 topicString = DATA_STRING;
jburhenn 0:09ef59d2d0f7 95 break;
jburhenn 0:09ef59d2d0f7 96 case RESPONSE_TOPIC:
jburhenn 0:09ef59d2d0f7 97 topicString = RESPONSE_STRING;
jburhenn 0:09ef59d2d0f7 98 break;
jburhenn 0:09ef59d2d0f7 99 case SYS_MODEL_TOPIC:
jburhenn 0:09ef59d2d0f7 100 topicString = SYS_MODEL_STRING;
jburhenn 0:09ef59d2d0f7 101 break;
jburhenn 0:09ef59d2d0f7 102 case SYS_VERSION_TOPIC:
jburhenn 0:09ef59d2d0f7 103 topicString = SYS_VERSION_STRING;
jburhenn 0:09ef59d2d0f7 104 break;
jburhenn 0:09ef59d2d0f7 105 case SYS_CPU_MODEL_TOPIC:
jburhenn 0:09ef59d2d0f7 106 topicString = SYS_CPU_MODEL_STRING;
jburhenn 0:09ef59d2d0f7 107 break;
jburhenn 0:09ef59d2d0f7 108 case SYS_CPU_SPEED_TOPIC:
jburhenn 0:09ef59d2d0f7 109 topicString = SYS_CPU_SPEED_STRING;
jburhenn 0:09ef59d2d0f7 110 break;
jburhenn 0:09ef59d2d0f7 111 #ifdef DIGITAL_AND_ANALOG_SUPPORT
jburhenn 0:09ef59d2d0f7 112 case DIGITAL_TOPIC:
jburhenn 0:09ef59d2d0f7 113 topicString = DIGITAL_STRING;
jburhenn 0:09ef59d2d0f7 114 break;
jburhenn 0:09ef59d2d0f7 115 case DIGITAL_COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 116 topicString = DIGITAL_COMMAND_STRING;
jburhenn 0:09ef59d2d0f7 117 break;
jburhenn 0:09ef59d2d0f7 118 case DIGITAL_CONFIG_TOPIC:
jburhenn 0:09ef59d2d0f7 119 topicString = DIGITAL_CONFIG_STRING;
jburhenn 0:09ef59d2d0f7 120 break;
jburhenn 0:09ef59d2d0f7 121 case ANALOG_TOPIC:
jburhenn 0:09ef59d2d0f7 122 topicString = ANALOG_STRING;
jburhenn 0:09ef59d2d0f7 123 break;
jburhenn 0:09ef59d2d0f7 124 case ANALOG_COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 125 topicString = ANALOG_COMMAND_STRING;
jburhenn 0:09ef59d2d0f7 126 break;
jburhenn 0:09ef59d2d0f7 127 case ANALOG_CONFIG_TOPIC:
jburhenn 0:09ef59d2d0f7 128 topicString = ANALOG_CONFIG_STRING;
jburhenn 0:09ef59d2d0f7 129 break;
jburhenn 0:09ef59d2d0f7 130 #endif
jburhenn 0:09ef59d2d0f7 131 default:
jburhenn 0:09ef59d2d0f7 132 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 133 }
jburhenn 0:09ef59d2d0f7 134
jburhenn 0:09ef59d2d0f7 135 if (!topicString)
jburhenn 0:09ef59d2d0f7 136 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 137 if (CAYENNE_STRLEN(topicString) >= length)
jburhenn 0:09ef59d2d0f7 138 return CAYENNE_BUFFER_OVERFLOW;
jburhenn 0:09ef59d2d0f7 139
jburhenn 0:09ef59d2d0f7 140 suffix[0] = '\0';
jburhenn 0:09ef59d2d0f7 141 CAYENNE_STRCAT(suffix, topicString);
jburhenn 0:09ef59d2d0f7 142 if (channel != CAYENNE_NO_CHANNEL) {
jburhenn 0:09ef59d2d0f7 143 strcat(suffix, "/");
jburhenn 0:09ef59d2d0f7 144 if (channel == CAYENNE_ALL_CHANNELS) {
jburhenn 0:09ef59d2d0f7 145 strcat(suffix, "+");
jburhenn 0:09ef59d2d0f7 146 }
jburhenn 0:09ef59d2d0f7 147 else {
jburhenn 0:09ef59d2d0f7 148 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 149 itoa(channel, &suffix[strlen(suffix)], 10);
jburhenn 0:09ef59d2d0f7 150 #else
jburhenn 0:09ef59d2d0f7 151 snprintf(&suffix[strlen(suffix)], length - strlen(suffix), "%u", channel);
jburhenn 0:09ef59d2d0f7 152 #endif
jburhenn 0:09ef59d2d0f7 153 }
jburhenn 0:09ef59d2d0f7 154 }
jburhenn 0:09ef59d2d0f7 155 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 156 }
jburhenn 0:09ef59d2d0f7 157
jburhenn 0:09ef59d2d0f7 158 /**
jburhenn 0:09ef59d2d0f7 159 * Check if topic matches.
jburhenn 0:09ef59d2d0f7 160 * @param[in] filter Filter to check topic against
jburhenn 0:09ef59d2d0f7 161 * @param[in] topicName CayenneTopic name
jburhenn 0:09ef59d2d0f7 162 * @param[in] topicNameLen CayenneTopic name length
jburhenn 0:09ef59d2d0f7 163 * return true if topic matches, false otherwise
jburhenn 0:09ef59d2d0f7 164 */
jburhenn 0:09ef59d2d0f7 165 int topicMatches(char* filter, char* topicName, unsigned int topicNameLen)
jburhenn 0:09ef59d2d0f7 166 {
jburhenn 0:09ef59d2d0f7 167 char* curf = filter;
jburhenn 0:09ef59d2d0f7 168 char* curn = topicName;
jburhenn 0:09ef59d2d0f7 169 char* curn_end = topicName + topicNameLen;
jburhenn 0:09ef59d2d0f7 170
jburhenn 0:09ef59d2d0f7 171 while (*curf && curn < curn_end)
jburhenn 0:09ef59d2d0f7 172 {
jburhenn 0:09ef59d2d0f7 173 if (*curn == '/' && *curf != '/')
jburhenn 0:09ef59d2d0f7 174 break;
jburhenn 0:09ef59d2d0f7 175 if (*curf != '+' && *curf != '#' && *curf != *curn)
jburhenn 0:09ef59d2d0f7 176 break;
jburhenn 0:09ef59d2d0f7 177 if (*curf == '+')
jburhenn 0:09ef59d2d0f7 178 { // skip until we meet the next separator, or end of string
jburhenn 0:09ef59d2d0f7 179 char* nextpos = curn + 1;
jburhenn 0:09ef59d2d0f7 180 while (nextpos < curn_end && *nextpos != '/')
jburhenn 0:09ef59d2d0f7 181 nextpos = ++curn + 1;
jburhenn 0:09ef59d2d0f7 182 }
jburhenn 0:09ef59d2d0f7 183 else if (*curf == '#')
jburhenn 0:09ef59d2d0f7 184 curn = curn_end - 1; // skip until end of string
jburhenn 0:09ef59d2d0f7 185 curf++;
jburhenn 0:09ef59d2d0f7 186 curn++;
jburhenn 0:09ef59d2d0f7 187 };
jburhenn 0:09ef59d2d0f7 188
jburhenn 0:09ef59d2d0f7 189 return ((curn == curn_end) && (*curf == '\0'));
jburhenn 0:09ef59d2d0f7 190 }
jburhenn 0:09ef59d2d0f7 191
jburhenn 0:09ef59d2d0f7 192 /**
jburhenn 0:09ef59d2d0f7 193 * Get the count of values in a message.
jburhenn 0:09ef59d2d0f7 194 * @param[out] count Returned number of values found in message
jburhenn 0:09ef59d2d0f7 195 * @param[in] payload Payload string, must be null terminated
jburhenn 0:09ef59d2d0f7 196 * @param[in] token Character token for splitting "unit=value" payloads, 0 to just parse first comma delimited value
jburhenn 0:09ef59d2d0f7 197 * @return CAYENNE_SUCCESS if value count succeeded, error code otherwise
jburhenn 0:09ef59d2d0f7 198 */
jburhenn 0:09ef59d2d0f7 199 int getValueCount(size_t* count, char* payload, char token) {
jburhenn 0:09ef59d2d0f7 200 char* index = payload;
jburhenn 0:09ef59d2d0f7 201 size_t unitCount = 0;
jburhenn 0:09ef59d2d0f7 202 size_t valueCount = 0;
jburhenn 0:09ef59d2d0f7 203 int countingValues = 0;
jburhenn 0:09ef59d2d0f7 204
jburhenn 0:09ef59d2d0f7 205 if (token == 0) {
jburhenn 0:09ef59d2d0f7 206 //Currently there can only be one value in payload if this isn't a "unit=value" payload.
jburhenn 0:09ef59d2d0f7 207 *count = 1;
jburhenn 0:09ef59d2d0f7 208 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 209 }
jburhenn 0:09ef59d2d0f7 210
jburhenn 0:09ef59d2d0f7 211 *count = 0;
jburhenn 0:09ef59d2d0f7 212 while (*index && index != '\0') {
jburhenn 0:09ef59d2d0f7 213 if ((*index == ',') || (*index == token)) {
jburhenn 0:09ef59d2d0f7 214 if (*index == ',') {
jburhenn 0:09ef59d2d0f7 215 if (countingValues) {
jburhenn 0:09ef59d2d0f7 216 valueCount++;
jburhenn 0:09ef59d2d0f7 217 }
jburhenn 0:09ef59d2d0f7 218 else {
jburhenn 0:09ef59d2d0f7 219 unitCount++;
jburhenn 0:09ef59d2d0f7 220 }
jburhenn 0:09ef59d2d0f7 221 }
jburhenn 0:09ef59d2d0f7 222 else if (*index == token) {
jburhenn 0:09ef59d2d0f7 223 countingValues = 1;
jburhenn 0:09ef59d2d0f7 224 valueCount++;
jburhenn 0:09ef59d2d0f7 225 }
jburhenn 0:09ef59d2d0f7 226 }
jburhenn 0:09ef59d2d0f7 227 index++;
jburhenn 0:09ef59d2d0f7 228 }
jburhenn 0:09ef59d2d0f7 229
jburhenn 0:09ef59d2d0f7 230 if (countingValues) {
jburhenn 0:09ef59d2d0f7 231 if ((valueCount != unitCount) && !(unitCount == 0 && valueCount == 1)) {
jburhenn 0:09ef59d2d0f7 232 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 233 }
jburhenn 0:09ef59d2d0f7 234 }
jburhenn 0:09ef59d2d0f7 235 else {
jburhenn 0:09ef59d2d0f7 236 valueCount = 1;
jburhenn 0:09ef59d2d0f7 237 }
jburhenn 0:09ef59d2d0f7 238 *count = valueCount;
jburhenn 0:09ef59d2d0f7 239 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 240 }
jburhenn 0:09ef59d2d0f7 241
jburhenn 0:09ef59d2d0f7 242 /**
jburhenn 0:09ef59d2d0f7 243 * Parse a null terminated payload string in place. This may modify the payload string.
jburhenn 0:09ef59d2d0f7 244 * @param[out] values Returned payload data unit & value array
jburhenn 0:09ef59d2d0f7 245 * @param[in,out] valuesSize Size of values array, returns the count of values in the array
jburhenn 0:09ef59d2d0f7 246 * @param[out] type Returned type, NULL if there is none
jburhenn 0:09ef59d2d0f7 247 * @param[in] payload Payload string, must be null terminated
jburhenn 0:09ef59d2d0f7 248 * @param[in] token Character token for splitting "unit=value" payloads, 0 to just parse first comma delimited value
jburhenn 0:09ef59d2d0f7 249 * @return CAYENNE_SUCCESS if value and id were parsed, error code otherwise
jburhenn 0:09ef59d2d0f7 250 */
jburhenn 0:09ef59d2d0f7 251 int parsePayload(CayenneValuePair* values, size_t* valuesSize, const char** type, char* payload, char token) {
jburhenn 0:09ef59d2d0f7 252 char* index = payload;
jburhenn 0:09ef59d2d0f7 253 size_t count = 0;
jburhenn 0:09ef59d2d0f7 254 int parsingValues = 0;
jburhenn 0:09ef59d2d0f7 255 size_t valueIndex = 0;
jburhenn 0:09ef59d2d0f7 256 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 257 int result = getValueCount(&count, payload, token);
jburhenn 0:09ef59d2d0f7 258 if (result != CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 259 *valuesSize = 0;
jburhenn 0:09ef59d2d0f7 260 return result;
jburhenn 0:09ef59d2d0f7 261 }
jburhenn 0:09ef59d2d0f7 262 #else
jburhenn 0:09ef59d2d0f7 263 count = 1;
jburhenn 0:09ef59d2d0f7 264 #endif
jburhenn 0:09ef59d2d0f7 265
jburhenn 0:09ef59d2d0f7 266 if(token == 0)
jburhenn 0:09ef59d2d0f7 267 parsingValues = 1; //Don't need to parse units if there is no unit/value separator
jburhenn 0:09ef59d2d0f7 268
jburhenn 0:09ef59d2d0f7 269 values[0].value = NULL;
jburhenn 0:09ef59d2d0f7 270 values[0].unit = NULL;
jburhenn 0:09ef59d2d0f7 271 *type = NULL;
jburhenn 0:09ef59d2d0f7 272 while (*index && index != '\0') {
jburhenn 6:82e142a864ad 273 if ((*index == ',') || (*index == token)) {
jburhenn 0:09ef59d2d0f7 274 if (*index == ',') {
jburhenn 0:09ef59d2d0f7 275 *type = payload;
jburhenn 0:09ef59d2d0f7 276 if (valueIndex < *valuesSize) {
jburhenn 0:09ef59d2d0f7 277 if (parsingValues) {
jburhenn 0:09ef59d2d0f7 278 values[valueIndex].value = index + 1;
jburhenn 0:09ef59d2d0f7 279 }
jburhenn 0:09ef59d2d0f7 280 else {
jburhenn 0:09ef59d2d0f7 281 values[valueIndex].unit = index + 1;
jburhenn 0:09ef59d2d0f7 282 }
jburhenn 0:09ef59d2d0f7 283 }
jburhenn 0:09ef59d2d0f7 284 *index = '\0';
jburhenn 0:09ef59d2d0f7 285 valueIndex++;
jburhenn 0:09ef59d2d0f7 286 if (token == 0)
jburhenn 0:09ef59d2d0f7 287 break;
jburhenn 0:09ef59d2d0f7 288 }
jburhenn 0:09ef59d2d0f7 289 else if (*index == token && !parsingValues) {
jburhenn 0:09ef59d2d0f7 290 parsingValues = 1;
jburhenn 0:09ef59d2d0f7 291 valueIndex = 0;
jburhenn 0:09ef59d2d0f7 292 *type = payload;
jburhenn 0:09ef59d2d0f7 293 values[valueIndex].value = index + 1;
jburhenn 0:09ef59d2d0f7 294 *index = '\0';
jburhenn 0:09ef59d2d0f7 295 valueIndex++;
jburhenn 0:09ef59d2d0f7 296 if (count == valueIndex)
jburhenn 0:09ef59d2d0f7 297 break;
jburhenn 0:09ef59d2d0f7 298 }
jburhenn 0:09ef59d2d0f7 299 }
jburhenn 0:09ef59d2d0f7 300 index++;
jburhenn 0:09ef59d2d0f7 301 };
jburhenn 0:09ef59d2d0f7 302 *valuesSize = count;
jburhenn 0:09ef59d2d0f7 303 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 304 }
jburhenn 0:09ef59d2d0f7 305
jburhenn 0:09ef59d2d0f7 306 /**
jburhenn 0:09ef59d2d0f7 307 * Build a specified topic string.
jburhenn 0:09ef59d2d0f7 308 * @param[out] topicName Returned topic string
jburhenn 0:09ef59d2d0f7 309 * @param[in] length CayenneTopic buffer length
jburhenn 0:09ef59d2d0f7 310 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 311 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 312 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 313 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 314 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 315 */
jburhenn 0:09ef59d2d0f7 316 int CayenneBuildTopic(char* topicName, size_t length, const char* username, const char* clientID, CayenneTopic topic, unsigned int channel) {
jburhenn 0:09ef59d2d0f7 317 char channelSuffix[20] = {0};
jburhenn 0:09ef59d2d0f7 318 int result = buildSuffix(channelSuffix, sizeof(channelSuffix), topic, channel);
jburhenn 0:09ef59d2d0f7 319 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 320 return result;
jburhenn 0:09ef59d2d0f7 321 return buildTopic(topicName, length, username, clientID, channelSuffix);
jburhenn 0:09ef59d2d0f7 322 }
jburhenn 0:09ef59d2d0f7 323
jburhenn 0:09ef59d2d0f7 324 /**
jburhenn 0:09ef59d2d0f7 325 * Build a specified data payload.
jburhenn 0:09ef59d2d0f7 326 * @param[out] payload Returned payload
jburhenn 0:09ef59d2d0f7 327 * @param[in,out] length Payload buffer length
jburhenn 0:09ef59d2d0f7 328 * @param[in] type Optional type to use for type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 329 * @param[in] values Unit/value array
jburhenn 0:09ef59d2d0f7 330 * @param[in] valueCount Number of values
jburhenn 0:09ef59d2d0f7 331 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 332 */
jburhenn 0:09ef59d2d0f7 333 int CayenneBuildDataPayload(char* payload, size_t* length, const char* type, const CayenneValuePair* values, size_t valueCount) {
jburhenn 0:09ef59d2d0f7 334 int i;
jburhenn 0:09ef59d2d0f7 335 size_t payloadLength = 0;
jburhenn 0:09ef59d2d0f7 336 for (i = 0; i < valueCount; ++i) {
jburhenn 6:82e142a864ad 337 if (values[i].unit) {
jburhenn 6:82e142a864ad 338 payloadLength += strlen(values[i].unit) + 1;
jburhenn 6:82e142a864ad 339 }
jburhenn 6:82e142a864ad 340 else if (type) {
jburhenn 16:085bcf2e9a18 341 // If type exists but unit does not, use UNIT_UNDEFINED for the unit.
jburhenn 16:085bcf2e9a18 342 payloadLength += strlen(UNIT_UNDEFINED) + 1;
jburhenn 6:82e142a864ad 343 }
jburhenn 0:09ef59d2d0f7 344 payloadLength += values[i].value ? strlen(values[i].value) + 1 : 0;
jburhenn 0:09ef59d2d0f7 345 }
jburhenn 0:09ef59d2d0f7 346 payloadLength += type ? strlen(type) + 1 : 0;
jburhenn 0:09ef59d2d0f7 347 //If payload can't fit the payload plus a terminating null byte return.
jburhenn 0:09ef59d2d0f7 348 if (payloadLength > *length) {
jburhenn 0:09ef59d2d0f7 349 return CAYENNE_BUFFER_OVERFLOW;
jburhenn 0:09ef59d2d0f7 350 }
jburhenn 0:09ef59d2d0f7 351
jburhenn 0:09ef59d2d0f7 352 payload[0] = '\0';
jburhenn 0:09ef59d2d0f7 353 if (type) {
jburhenn 0:09ef59d2d0f7 354 strcat(payload, type);
jburhenn 0:09ef59d2d0f7 355 }
jburhenn 6:82e142a864ad 356 for (i = 0; i < valueCount; ++i) {
jburhenn 6:82e142a864ad 357 if (payload[0] != '\0')
jburhenn 0:09ef59d2d0f7 358 strcat(payload, ",");
jburhenn 6:82e142a864ad 359 if (values[i].unit)
jburhenn 6:82e142a864ad 360 strcat(payload, values[i].unit);
jburhenn 6:82e142a864ad 361 else if (type)
jburhenn 16:085bcf2e9a18 362 strcat(payload, UNIT_UNDEFINED);
jburhenn 0:09ef59d2d0f7 363 }
jburhenn 0:09ef59d2d0f7 364 if (payload[0] != '\0' && valueCount > 0 && values[0].value)
jburhenn 0:09ef59d2d0f7 365 strcat(payload, "=");
jburhenn 0:09ef59d2d0f7 366 for (i = 0; i < valueCount && values[i].value; ++i) {
jburhenn 0:09ef59d2d0f7 367 strcat(payload, values[i].value);
jburhenn 6:82e142a864ad 368 if (i + 1 < valueCount)
jburhenn 0:09ef59d2d0f7 369 strcat(payload, ",");
jburhenn 0:09ef59d2d0f7 370 }
jburhenn 0:09ef59d2d0f7 371 *length = --payloadLength; //Subtract terminating null
jburhenn 0:09ef59d2d0f7 372 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 373 }
jburhenn 0:09ef59d2d0f7 374
jburhenn 0:09ef59d2d0f7 375 /**
jburhenn 0:09ef59d2d0f7 376 * Build a specified response payload.
jburhenn 0:09ef59d2d0f7 377 * @param[out] payload Returned payload
jburhenn 0:09ef59d2d0f7 378 * @param[in,out] length Payload buffer length
jburhenn 0:09ef59d2d0f7 379 * @param[in] id ID of message the response is for
jburhenn 0:09ef59d2d0f7 380 * @param[in] error Optional error message, NULL for success
jburhenn 0:09ef59d2d0f7 381 * @return CAYENNE_SUCCESS if payload string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 382 */
jburhenn 0:09ef59d2d0f7 383 int CayenneBuildResponsePayload(char* payload, size_t* length, const char* id, const char* error) {
jburhenn 0:09ef59d2d0f7 384 CayenneValuePair values[1];
jburhenn 0:09ef59d2d0f7 385 values[0].unit = id;
jburhenn 0:09ef59d2d0f7 386 values[0].value = error;
jburhenn 0:09ef59d2d0f7 387 if (error) {
jburhenn 0:09ef59d2d0f7 388 return CayenneBuildDataPayload(payload, length, "error", values, 1);
jburhenn 0:09ef59d2d0f7 389 }
jburhenn 0:09ef59d2d0f7 390 return CayenneBuildDataPayload(payload, length, "ok", values, 1);
jburhenn 0:09ef59d2d0f7 391 }
jburhenn 0:09ef59d2d0f7 392
jburhenn 0:09ef59d2d0f7 393 /**
jburhenn 0:09ef59d2d0f7 394 * Parse a topic string in place. This may modify the topic string.
jburhenn 0:09ef59d2d0f7 395 * @param[out] topic Returned Cayenne topic
jburhenn 0:09ef59d2d0f7 396 * @param[out] channel Returned channel, CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 397 * @param[out] clientID Returned client ID
jburhenn 0:09ef59d2d0f7 398 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 399 * @param[in] topicName Topic name string
jburhenn 0:09ef59d2d0f7 400 * @param[in] length Topic name string length
jburhenn 0:09ef59d2d0f7 401 * @return CAYENNE_SUCCESS if topic was parsed, error code otherwise
jburhenn 0:09ef59d2d0f7 402 */
jburhenn 0:09ef59d2d0f7 403 int CayenneParseTopic(CayenneTopic* topic, unsigned int* channel, const char** clientID, const char* username, char* topicName, unsigned int length) {
jburhenn 0:09ef59d2d0f7 404 char* index = NULL;
jburhenn 0:09ef59d2d0f7 405 int i = 0;
jburhenn 0:09ef59d2d0f7 406 TopicChannel parseTopics[PARSE_TOPICS_COUNT] = { { COMMAND_TOPIC, CAYENNE_ALL_CHANNELS },{ CONFIG_TOPIC, CAYENNE_ALL_CHANNELS },
jburhenn 0:09ef59d2d0f7 407 #ifdef DIGITAL_AND_ANALOG_SUPPORT
jburhenn 0:09ef59d2d0f7 408 { ANALOG_COMMAND_TOPIC, CAYENNE_ALL_CHANNELS },{ ANALOG_CONFIG_TOPIC, CAYENNE_ALL_CHANNELS },{ DIGITAL_COMMAND_TOPIC, CAYENNE_ALL_CHANNELS },{ DIGITAL_CONFIG_TOPIC, CAYENNE_ALL_CHANNELS },
jburhenn 0:09ef59d2d0f7 409 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 410 { DIGITAL_TOPIC, CAYENNE_ALL_CHANNELS },{ ANALOG_TOPIC, CAYENNE_ALL_CHANNELS },
jburhenn 0:09ef59d2d0f7 411 #endif
jburhenn 0:09ef59d2d0f7 412 #endif
jburhenn 0:09ef59d2d0f7 413 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 414 { DATA_TOPIC, CAYENNE_ALL_CHANNELS },{ SYS_MODEL_TOPIC, CAYENNE_NO_CHANNEL },{ SYS_VERSION_TOPIC, CAYENNE_NO_CHANNEL },{ SYS_CPU_MODEL_TOPIC, CAYENNE_NO_CHANNEL },{ SYS_CPU_SPEED_TOPIC, CAYENNE_NO_CHANNEL }
jburhenn 0:09ef59d2d0f7 415 #endif
jburhenn 0:09ef59d2d0f7 416 };
jburhenn 0:09ef59d2d0f7 417
jburhenn 0:09ef59d2d0f7 418 if (!topic || !channel || !topicName)
jburhenn 0:09ef59d2d0f7 419 {
jburhenn 0:09ef59d2d0f7 420 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 421 }
jburhenn 0:09ef59d2d0f7 422 if (length > CAYENNE_MAX_MESSAGE_SIZE)
jburhenn 0:09ef59d2d0f7 423 {
jburhenn 0:09ef59d2d0f7 424 return CAYENNE_BUFFER_OVERFLOW;
jburhenn 0:09ef59d2d0f7 425 }
jburhenn 0:09ef59d2d0f7 426 if (strncmp(CAYENNE_VERSION, topicName, strlen(CAYENNE_VERSION)) != 0)
jburhenn 0:09ef59d2d0f7 427 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 428 index = topicName + strlen(CAYENNE_VERSION) + 1;
jburhenn 0:09ef59d2d0f7 429 if (strncmp(username, index, strlen(username)) != 0)
jburhenn 0:09ef59d2d0f7 430 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 431 index += strlen(username);
jburhenn 0:09ef59d2d0f7 432 if (CAYENNE_STRNCMP(index, THINGS_STRING, CAYENNE_STRLEN(THINGS_STRING)) != 0)
jburhenn 0:09ef59d2d0f7 433 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 434 index += CAYENNE_STRLEN(THINGS_STRING);
jburhenn 0:09ef59d2d0f7 435 char* deviceIDEnd = strchr(index, '/');
jburhenn 0:09ef59d2d0f7 436 if (!deviceIDEnd)
jburhenn 0:09ef59d2d0f7 437 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 438 *clientID = index;
jburhenn 0:09ef59d2d0f7 439 *deviceIDEnd = '\0';
jburhenn 0:09ef59d2d0f7 440
jburhenn 0:09ef59d2d0f7 441 index = deviceIDEnd + 1;
jburhenn 0:09ef59d2d0f7 442 *topic = UNDEFINED_TOPIC;
jburhenn 0:09ef59d2d0f7 443 *channel = CAYENNE_NO_CHANNEL;
jburhenn 0:09ef59d2d0f7 444 length -= (index - topicName);
jburhenn 0:09ef59d2d0f7 445 for (i = 0; i < PARSE_TOPICS_COUNT; ++i)
jburhenn 0:09ef59d2d0f7 446 {
jburhenn 0:09ef59d2d0f7 447 char channelSuffix[32] = { 0 };
jburhenn 0:09ef59d2d0f7 448 if (buildSuffix(channelSuffix, sizeof(channelSuffix), parseTopics[i].topic, parseTopics[i].channel) == CAYENNE_SUCCESS && topicMatches(channelSuffix, index, length)) {
jburhenn 0:09ef59d2d0f7 449 *topic = parseTopics[i].topic;
jburhenn 0:09ef59d2d0f7 450 break;
jburhenn 0:09ef59d2d0f7 451 }
jburhenn 0:09ef59d2d0f7 452 }
jburhenn 0:09ef59d2d0f7 453
jburhenn 0:09ef59d2d0f7 454 if (*topic == UNDEFINED_TOPIC)
jburhenn 0:09ef59d2d0f7 455 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 456
jburhenn 0:09ef59d2d0f7 457 if (parseTopics[i].channel != CAYENNE_NO_CHANNEL) {
jburhenn 0:09ef59d2d0f7 458 if (length == 0 || length > 31)
jburhenn 0:09ef59d2d0f7 459 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 460 char* channelIndex = NULL;
jburhenn 0:09ef59d2d0f7 461 char buffer[32] = { 0 };
jburhenn 0:09ef59d2d0f7 462 memcpy(buffer, index, length);
jburhenn 0:09ef59d2d0f7 463 buffer[length] = '\0';
jburhenn 0:09ef59d2d0f7 464 channelIndex = strrchr(buffer, '/');
jburhenn 0:09ef59d2d0f7 465 if (channelIndex && ++channelIndex) {
jburhenn 0:09ef59d2d0f7 466 char* indexEnd = NULL;
jburhenn 0:09ef59d2d0f7 467 unsigned int channelNumber = strtoul(channelIndex, &indexEnd, 10);
jburhenn 0:09ef59d2d0f7 468 if (indexEnd && *indexEnd == '\0') {
jburhenn 0:09ef59d2d0f7 469 if (((channelNumber != 0) && (*channelIndex != '0')) || ((channelNumber == 0) && (*channelIndex == '0') && (channelIndex + 1 == indexEnd))) {
jburhenn 0:09ef59d2d0f7 470 *channel = channelNumber;
jburhenn 0:09ef59d2d0f7 471 }
jburhenn 0:09ef59d2d0f7 472 }
jburhenn 0:09ef59d2d0f7 473 }
jburhenn 0:09ef59d2d0f7 474 }
jburhenn 0:09ef59d2d0f7 475
jburhenn 0:09ef59d2d0f7 476 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 477 }
jburhenn 0:09ef59d2d0f7 478
jburhenn 0:09ef59d2d0f7 479 /**
jburhenn 0:09ef59d2d0f7 480 * Parse a null terminated payload in place. This may modify the payload string.
jburhenn 0:09ef59d2d0f7 481 * @param[out] values Returned payload data unit & value array
jburhenn 0:09ef59d2d0f7 482 * @param[in,out] valuesSize Size of values array, returns the count of values in the array
jburhenn 0:09ef59d2d0f7 483 * @param[out] type Returned type, NULL if there is none
jburhenn 0:09ef59d2d0f7 484 * @param[out] id Returned message id, empty string if there is none
jburhenn 0:09ef59d2d0f7 485 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 486 * @param[in] payload Payload string, must be null terminated.
jburhenn 0:09ef59d2d0f7 487 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 488 */
jburhenn 0:09ef59d2d0f7 489 int CayenneParsePayload(CayenneValuePair* values, size_t* valuesSize, const char** type, const char** id, CayenneTopic topic, char* payload) {
jburhenn 0:09ef59d2d0f7 490 int i;
jburhenn 0:09ef59d2d0f7 491 if (!payload || !valuesSize || *valuesSize == 0)
jburhenn 0:09ef59d2d0f7 492 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 493
jburhenn 0:09ef59d2d0f7 494 *type = NULL;
jburhenn 0:09ef59d2d0f7 495 *id = NULL;
jburhenn 0:09ef59d2d0f7 496 for(i = 0; i < *valuesSize; i++) {
jburhenn 0:09ef59d2d0f7 497 values[i].unit = NULL;
jburhenn 0:09ef59d2d0f7 498 values[i].value = NULL;
jburhenn 0:09ef59d2d0f7 499 }
jburhenn 0:09ef59d2d0f7 500 switch (topic)
jburhenn 0:09ef59d2d0f7 501 {
jburhenn 0:09ef59d2d0f7 502 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 503 case DATA_TOPIC:
jburhenn 0:09ef59d2d0f7 504 parsePayload(values, valuesSize, type, payload, '=');
jburhenn 0:09ef59d2d0f7 505 if (!values[0].value)
jburhenn 0:09ef59d2d0f7 506 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 507 break;
jburhenn 0:09ef59d2d0f7 508 #endif
jburhenn 0:09ef59d2d0f7 509 #ifdef DIGITAL_AND_ANALOG_SUPPORT
jburhenn 0:09ef59d2d0f7 510 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 511 case ANALOG_TOPIC:
jburhenn 0:09ef59d2d0f7 512 parsePayload(values, valuesSize, type, payload, 0);
jburhenn 0:09ef59d2d0f7 513 values[0].unit = values[0].value; //Use unit to store resolution
jburhenn 0:09ef59d2d0f7 514 values[0].value = *type;
jburhenn 0:09ef59d2d0f7 515 *type = NULL;
jburhenn 0:09ef59d2d0f7 516 if (!values[0].value)
jburhenn 0:09ef59d2d0f7 517 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 518 break;
jburhenn 0:09ef59d2d0f7 519 #endif
jburhenn 0:09ef59d2d0f7 520 case DIGITAL_COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 521 case ANALOG_COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 522 #endif
jburhenn 0:09ef59d2d0f7 523 case COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 524 parsePayload(values, valuesSize, type, payload, 0);
jburhenn 0:09ef59d2d0f7 525 *id = *type;
jburhenn 0:09ef59d2d0f7 526 *type = NULL;
jburhenn 0:09ef59d2d0f7 527 if (!values[0].value)
jburhenn 0:09ef59d2d0f7 528 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 529 break;
jburhenn 0:09ef59d2d0f7 530 default:
jburhenn 0:09ef59d2d0f7 531 break;
jburhenn 0:09ef59d2d0f7 532 }
jburhenn 0:09ef59d2d0f7 533
jburhenn 0:09ef59d2d0f7 534 if (!values[0].value) {
jburhenn 0:09ef59d2d0f7 535 values[0].value = payload;
jburhenn 0:09ef59d2d0f7 536 values[0].unit = NULL;
jburhenn 0:09ef59d2d0f7 537 *type = NULL;
jburhenn 0:09ef59d2d0f7 538 *id = NULL;
jburhenn 0:09ef59d2d0f7 539 *valuesSize = 1;
jburhenn 0:09ef59d2d0f7 540 }
jburhenn 0:09ef59d2d0f7 541
jburhenn 0:09ef59d2d0f7 542 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 543 }
jburhenn 0:09ef59d2d0f7 544
jburhenn 0:09ef59d2d0f7 545
jburhenn 0:09ef59d2d0f7 546