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

Dependents:   Cayenne-ESP8266Interface Cayenne-WIZnet_Library Cayenne-WIZnetInterface Cayenne-X-NUCLEO-IDW01M1 ... more

Committer:
jburhenn
Date:
Wed Jan 25 11:10:39 2017 -0700
Branch:
feature/multivalue
Revision:
23:1a9aed5e77c9
Parent:
22:0dbabcc6e7b2
CayenneDataArray updates for multivalue support.

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 22:0dbabcc6e7b2 165 int topicMatches(char* filter, char* topicName, size_t 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 * Parse a null terminated payload string in place. This may modify the payload string.
jburhenn 0:09ef59d2d0f7 194 * @param[out] type Returned type, NULL if there is none
jburhenn 22:0dbabcc6e7b2 195 * @param[out] unit Returned unit, NULL if there is none
jburhenn 22:0dbabcc6e7b2 196 * @param[out] value Returned value, NULL if there is none
jburhenn 0:09ef59d2d0f7 197 * @param[in] payload Payload string, must be null terminated
jburhenn 0:09ef59d2d0f7 198 * @param[in] token Character token for splitting "unit=value" payloads, 0 to just parse first comma delimited value
jburhenn 0:09ef59d2d0f7 199 * @return CAYENNE_SUCCESS if value and id were parsed, error code otherwise
jburhenn 0:09ef59d2d0f7 200 */
jburhenn 22:0dbabcc6e7b2 201 int parsePayload(const char** type, const char** unit, const char** value, char* payload, char token) {
jburhenn 0:09ef59d2d0f7 202 char* index = payload;
jburhenn 0:09ef59d2d0f7 203 *type = NULL;
jburhenn 22:0dbabcc6e7b2 204 *unit = NULL;
jburhenn 22:0dbabcc6e7b2 205 *value = NULL;
jburhenn 0:09ef59d2d0f7 206 while (*index && index != '\0') {
jburhenn 6:82e142a864ad 207 if ((*index == ',') || (*index == token)) {
jburhenn 0:09ef59d2d0f7 208 if (*index == ',') {
jburhenn 0:09ef59d2d0f7 209 *type = payload;
jburhenn 22:0dbabcc6e7b2 210 *unit = index + 1;
jburhenn 0:09ef59d2d0f7 211 *index = '\0';
jburhenn 0:09ef59d2d0f7 212 if (token == 0)
jburhenn 0:09ef59d2d0f7 213 break;
jburhenn 0:09ef59d2d0f7 214 }
jburhenn 22:0dbabcc6e7b2 215 else if (*index == token) {
jburhenn 0:09ef59d2d0f7 216 *type = payload;
jburhenn 22:0dbabcc6e7b2 217 *value = index + 1;
jburhenn 0:09ef59d2d0f7 218 *index = '\0';
jburhenn 22:0dbabcc6e7b2 219 break;
jburhenn 0:09ef59d2d0f7 220 }
jburhenn 0:09ef59d2d0f7 221 }
jburhenn 0:09ef59d2d0f7 222 index++;
jburhenn 0:09ef59d2d0f7 223 };
jburhenn 0:09ef59d2d0f7 224 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 225 }
jburhenn 0:09ef59d2d0f7 226
jburhenn 0:09ef59d2d0f7 227 /**
jburhenn 0:09ef59d2d0f7 228 * Build a specified topic string.
jburhenn 0:09ef59d2d0f7 229 * @param[out] topicName Returned topic string
jburhenn 0:09ef59d2d0f7 230 * @param[in] length CayenneTopic buffer length
jburhenn 0:09ef59d2d0f7 231 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 232 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 233 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 234 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 235 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 236 */
jburhenn 0:09ef59d2d0f7 237 int CayenneBuildTopic(char* topicName, size_t length, const char* username, const char* clientID, CayenneTopic topic, unsigned int channel) {
jburhenn 0:09ef59d2d0f7 238 char channelSuffix[20] = {0};
jburhenn 0:09ef59d2d0f7 239 int result = buildSuffix(channelSuffix, sizeof(channelSuffix), topic, channel);
jburhenn 0:09ef59d2d0f7 240 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 241 return result;
jburhenn 0:09ef59d2d0f7 242 return buildTopic(topicName, length, username, clientID, channelSuffix);
jburhenn 0:09ef59d2d0f7 243 }
jburhenn 0:09ef59d2d0f7 244
jburhenn 0:09ef59d2d0f7 245 /**
jburhenn 0:09ef59d2d0f7 246 * Build a specified data payload.
jburhenn 0:09ef59d2d0f7 247 * @param[out] payload Returned payload
jburhenn 0:09ef59d2d0f7 248 * @param[in,out] length Payload buffer length
jburhenn 0:09ef59d2d0f7 249 * @param[in] type Optional type to use for type,unit=value payload, can be NULL
jburhenn 22:0dbabcc6e7b2 250 * @param[in] unit Payload unit
jburhenn 22:0dbabcc6e7b2 251 * @param[in] value Payload value
jburhenn 0:09ef59d2d0f7 252 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 253 */
jburhenn 22:0dbabcc6e7b2 254 int CayenneBuildDataPayload(char* payload, size_t* length, const char* type, const char* unit, const char* value) {
jburhenn 0:09ef59d2d0f7 255 size_t payloadLength = 0;
jburhenn 22:0dbabcc6e7b2 256 if (unit) {
jburhenn 22:0dbabcc6e7b2 257 payloadLength += strlen(unit) + 1;
jburhenn 0:09ef59d2d0f7 258 }
jburhenn 22:0dbabcc6e7b2 259 else if (type) {
jburhenn 22:0dbabcc6e7b2 260 // If type exists but unit does not, use UNIT_UNDEFINED for the unit.
jburhenn 22:0dbabcc6e7b2 261 payloadLength += strlen(UNIT_UNDEFINED) + 1;
jburhenn 22:0dbabcc6e7b2 262 }
jburhenn 22:0dbabcc6e7b2 263 payloadLength += value ? strlen(value) + 1 : 0;
jburhenn 0:09ef59d2d0f7 264 payloadLength += type ? strlen(type) + 1 : 0;
jburhenn 0:09ef59d2d0f7 265 //If payload can't fit the payload plus a terminating null byte return.
jburhenn 0:09ef59d2d0f7 266 if (payloadLength > *length) {
jburhenn 0:09ef59d2d0f7 267 return CAYENNE_BUFFER_OVERFLOW;
jburhenn 0:09ef59d2d0f7 268 }
jburhenn 0:09ef59d2d0f7 269
jburhenn 0:09ef59d2d0f7 270 payload[0] = '\0';
jburhenn 0:09ef59d2d0f7 271 if (type) {
jburhenn 0:09ef59d2d0f7 272 strcat(payload, type);
jburhenn 0:09ef59d2d0f7 273 }
jburhenn 22:0dbabcc6e7b2 274 if (payload[0] != '\0')
jburhenn 22:0dbabcc6e7b2 275 strcat(payload, ",");
jburhenn 22:0dbabcc6e7b2 276 if (unit)
jburhenn 22:0dbabcc6e7b2 277 strcat(payload, unit);
jburhenn 22:0dbabcc6e7b2 278 else if (type)
jburhenn 22:0dbabcc6e7b2 279 strcat(payload, UNIT_UNDEFINED);
jburhenn 22:0dbabcc6e7b2 280 if (payload[0] != '\0' && value)
jburhenn 0:09ef59d2d0f7 281 strcat(payload, "=");
jburhenn 22:0dbabcc6e7b2 282 if (value)
jburhenn 22:0dbabcc6e7b2 283 strcat(payload, value);
jburhenn 0:09ef59d2d0f7 284 *length = --payloadLength; //Subtract terminating null
jburhenn 0:09ef59d2d0f7 285 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 286 }
jburhenn 0:09ef59d2d0f7 287
jburhenn 0:09ef59d2d0f7 288 /**
jburhenn 0:09ef59d2d0f7 289 * Build a specified response payload.
jburhenn 0:09ef59d2d0f7 290 * @param[out] payload Returned payload
jburhenn 0:09ef59d2d0f7 291 * @param[in,out] length Payload buffer length
jburhenn 0:09ef59d2d0f7 292 * @param[in] id ID of message the response is for
jburhenn 0:09ef59d2d0f7 293 * @param[in] error Optional error message, NULL for success
jburhenn 0:09ef59d2d0f7 294 * @return CAYENNE_SUCCESS if payload string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 295 */
jburhenn 0:09ef59d2d0f7 296 int CayenneBuildResponsePayload(char* payload, size_t* length, const char* id, const char* error) {
jburhenn 0:09ef59d2d0f7 297 if (error) {
jburhenn 22:0dbabcc6e7b2 298 return CayenneBuildDataPayload(payload, length, "error", id, error);
jburhenn 0:09ef59d2d0f7 299 }
jburhenn 22:0dbabcc6e7b2 300 return CayenneBuildDataPayload(payload, length, "ok", id, error);
jburhenn 0:09ef59d2d0f7 301 }
jburhenn 0:09ef59d2d0f7 302
jburhenn 0:09ef59d2d0f7 303 /**
jburhenn 0:09ef59d2d0f7 304 * Parse a topic string in place. This may modify the topic string.
jburhenn 0:09ef59d2d0f7 305 * @param[out] topic Returned Cayenne topic
jburhenn 0:09ef59d2d0f7 306 * @param[out] channel Returned channel, CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 307 * @param[out] clientID Returned client ID
jburhenn 0:09ef59d2d0f7 308 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 309 * @param[in] topicName Topic name string
jburhenn 0:09ef59d2d0f7 310 * @param[in] length Topic name string length
jburhenn 0:09ef59d2d0f7 311 * @return CAYENNE_SUCCESS if topic was parsed, error code otherwise
jburhenn 0:09ef59d2d0f7 312 */
jburhenn 22:0dbabcc6e7b2 313 int CayenneParseTopic(CayenneTopic* topic, unsigned int* channel, const char** clientID, const char* username, char* topicName, size_t length) {
jburhenn 0:09ef59d2d0f7 314 char* index = NULL;
jburhenn 0:09ef59d2d0f7 315 int i = 0;
jburhenn 0:09ef59d2d0f7 316 TopicChannel parseTopics[PARSE_TOPICS_COUNT] = { { COMMAND_TOPIC, CAYENNE_ALL_CHANNELS },{ CONFIG_TOPIC, CAYENNE_ALL_CHANNELS },
jburhenn 0:09ef59d2d0f7 317 #ifdef DIGITAL_AND_ANALOG_SUPPORT
jburhenn 0:09ef59d2d0f7 318 { 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 319 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 320 { DIGITAL_TOPIC, CAYENNE_ALL_CHANNELS },{ ANALOG_TOPIC, CAYENNE_ALL_CHANNELS },
jburhenn 0:09ef59d2d0f7 321 #endif
jburhenn 0:09ef59d2d0f7 322 #endif
jburhenn 0:09ef59d2d0f7 323 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 324 { 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 325 #endif
jburhenn 0:09ef59d2d0f7 326 };
jburhenn 0:09ef59d2d0f7 327
jburhenn 0:09ef59d2d0f7 328 if (!topic || !channel || !topicName)
jburhenn 0:09ef59d2d0f7 329 {
jburhenn 0:09ef59d2d0f7 330 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 331 }
jburhenn 0:09ef59d2d0f7 332 if (length > CAYENNE_MAX_MESSAGE_SIZE)
jburhenn 0:09ef59d2d0f7 333 {
jburhenn 0:09ef59d2d0f7 334 return CAYENNE_BUFFER_OVERFLOW;
jburhenn 0:09ef59d2d0f7 335 }
jburhenn 0:09ef59d2d0f7 336 if (strncmp(CAYENNE_VERSION, topicName, strlen(CAYENNE_VERSION)) != 0)
jburhenn 0:09ef59d2d0f7 337 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 338 index = topicName + strlen(CAYENNE_VERSION) + 1;
jburhenn 0:09ef59d2d0f7 339 if (strncmp(username, index, strlen(username)) != 0)
jburhenn 0:09ef59d2d0f7 340 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 341 index += strlen(username);
jburhenn 0:09ef59d2d0f7 342 if (CAYENNE_STRNCMP(index, THINGS_STRING, CAYENNE_STRLEN(THINGS_STRING)) != 0)
jburhenn 0:09ef59d2d0f7 343 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 344 index += CAYENNE_STRLEN(THINGS_STRING);
jburhenn 0:09ef59d2d0f7 345 char* deviceIDEnd = strchr(index, '/');
jburhenn 0:09ef59d2d0f7 346 if (!deviceIDEnd)
jburhenn 0:09ef59d2d0f7 347 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 348 *clientID = index;
jburhenn 0:09ef59d2d0f7 349 *deviceIDEnd = '\0';
jburhenn 0:09ef59d2d0f7 350
jburhenn 0:09ef59d2d0f7 351 index = deviceIDEnd + 1;
jburhenn 0:09ef59d2d0f7 352 *topic = UNDEFINED_TOPIC;
jburhenn 0:09ef59d2d0f7 353 *channel = CAYENNE_NO_CHANNEL;
jburhenn 0:09ef59d2d0f7 354 length -= (index - topicName);
jburhenn 0:09ef59d2d0f7 355 for (i = 0; i < PARSE_TOPICS_COUNT; ++i)
jburhenn 0:09ef59d2d0f7 356 {
jburhenn 0:09ef59d2d0f7 357 char channelSuffix[32] = { 0 };
jburhenn 0:09ef59d2d0f7 358 if (buildSuffix(channelSuffix, sizeof(channelSuffix), parseTopics[i].topic, parseTopics[i].channel) == CAYENNE_SUCCESS && topicMatches(channelSuffix, index, length)) {
jburhenn 0:09ef59d2d0f7 359 *topic = parseTopics[i].topic;
jburhenn 0:09ef59d2d0f7 360 break;
jburhenn 0:09ef59d2d0f7 361 }
jburhenn 0:09ef59d2d0f7 362 }
jburhenn 0:09ef59d2d0f7 363
jburhenn 0:09ef59d2d0f7 364 if (*topic == UNDEFINED_TOPIC)
jburhenn 0:09ef59d2d0f7 365 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 366
jburhenn 0:09ef59d2d0f7 367 if (parseTopics[i].channel != CAYENNE_NO_CHANNEL) {
jburhenn 0:09ef59d2d0f7 368 if (length == 0 || length > 31)
jburhenn 0:09ef59d2d0f7 369 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 370 char* channelIndex = NULL;
jburhenn 0:09ef59d2d0f7 371 char buffer[32] = { 0 };
jburhenn 0:09ef59d2d0f7 372 memcpy(buffer, index, length);
jburhenn 0:09ef59d2d0f7 373 buffer[length] = '\0';
jburhenn 0:09ef59d2d0f7 374 channelIndex = strrchr(buffer, '/');
jburhenn 0:09ef59d2d0f7 375 if (channelIndex && ++channelIndex) {
jburhenn 0:09ef59d2d0f7 376 char* indexEnd = NULL;
jburhenn 0:09ef59d2d0f7 377 unsigned int channelNumber = strtoul(channelIndex, &indexEnd, 10);
jburhenn 0:09ef59d2d0f7 378 if (indexEnd && *indexEnd == '\0') {
jburhenn 0:09ef59d2d0f7 379 if (((channelNumber != 0) && (*channelIndex != '0')) || ((channelNumber == 0) && (*channelIndex == '0') && (channelIndex + 1 == indexEnd))) {
jburhenn 0:09ef59d2d0f7 380 *channel = channelNumber;
jburhenn 0:09ef59d2d0f7 381 }
jburhenn 0:09ef59d2d0f7 382 }
jburhenn 0:09ef59d2d0f7 383 }
jburhenn 0:09ef59d2d0f7 384 }
jburhenn 0:09ef59d2d0f7 385
jburhenn 0:09ef59d2d0f7 386 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 387 }
jburhenn 0:09ef59d2d0f7 388
jburhenn 0:09ef59d2d0f7 389 /**
jburhenn 0:09ef59d2d0f7 390 * Parse a null terminated payload in place. This may modify the payload string.
jburhenn 0:09ef59d2d0f7 391 * @param[out] type Returned type, NULL if there is none
jburhenn 22:0dbabcc6e7b2 392 * @param[out] unit Returned unit, NULL if there is none
jburhenn 22:0dbabcc6e7b2 393 * @param[out] value Returned value, NULL if there is none
jburhenn 0:09ef59d2d0f7 394 * @param[out] id Returned message id, empty string if there is none
jburhenn 0:09ef59d2d0f7 395 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 396 * @param[in] payload Payload string, must be null terminated.
jburhenn 0:09ef59d2d0f7 397 * @return CAYENNE_SUCCESS if topic string was created, error code otherwise
jburhenn 0:09ef59d2d0f7 398 */
jburhenn 22:0dbabcc6e7b2 399 int CayenneParsePayload(const char** type, const char** unit, const char** value, const char** id, CayenneTopic topic, char* payload) {
jburhenn 22:0dbabcc6e7b2 400 if (!payload)
jburhenn 0:09ef59d2d0f7 401 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 402
jburhenn 0:09ef59d2d0f7 403 *type = NULL;
jburhenn 22:0dbabcc6e7b2 404 *unit = NULL;
jburhenn 22:0dbabcc6e7b2 405 *value = NULL;
jburhenn 0:09ef59d2d0f7 406 *id = NULL;
jburhenn 0:09ef59d2d0f7 407 switch (topic)
jburhenn 0:09ef59d2d0f7 408 {
jburhenn 0:09ef59d2d0f7 409 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 410 case DATA_TOPIC:
jburhenn 22:0dbabcc6e7b2 411 parsePayload(type, unit, value, payload, '=');
jburhenn 22:0dbabcc6e7b2 412 if (!*value)
jburhenn 0:09ef59d2d0f7 413 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 414 break;
jburhenn 0:09ef59d2d0f7 415 #endif
jburhenn 0:09ef59d2d0f7 416 #ifdef DIGITAL_AND_ANALOG_SUPPORT
jburhenn 0:09ef59d2d0f7 417 #ifdef PARSE_INFO_PAYLOADS
jburhenn 0:09ef59d2d0f7 418 case ANALOG_TOPIC:
jburhenn 22:0dbabcc6e7b2 419 //Use unit to store resolution
jburhenn 22:0dbabcc6e7b2 420 parsePayload(value, unit, unit, payload, 0);
jburhenn 22:0dbabcc6e7b2 421 if (!*value)
jburhenn 0:09ef59d2d0f7 422 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 423 break;
jburhenn 0:09ef59d2d0f7 424 #endif
jburhenn 0:09ef59d2d0f7 425 case DIGITAL_COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 426 case ANALOG_COMMAND_TOPIC:
jburhenn 0:09ef59d2d0f7 427 #endif
jburhenn 0:09ef59d2d0f7 428 case COMMAND_TOPIC:
jburhenn 22:0dbabcc6e7b2 429 parsePayload(id, value, unit, payload, 0);
jburhenn 22:0dbabcc6e7b2 430 if (!*value)
jburhenn 0:09ef59d2d0f7 431 return CAYENNE_FAILURE;
jburhenn 0:09ef59d2d0f7 432 break;
jburhenn 0:09ef59d2d0f7 433 default:
jburhenn 0:09ef59d2d0f7 434 break;
jburhenn 0:09ef59d2d0f7 435 }
jburhenn 0:09ef59d2d0f7 436
jburhenn 22:0dbabcc6e7b2 437 if (!*value) {
jburhenn 22:0dbabcc6e7b2 438 *value = payload;
jburhenn 22:0dbabcc6e7b2 439 *unit = NULL;
jburhenn 0:09ef59d2d0f7 440 *type = NULL;
jburhenn 0:09ef59d2d0f7 441 *id = NULL;
jburhenn 0:09ef59d2d0f7 442 }
jburhenn 0:09ef59d2d0f7 443
jburhenn 0:09ef59d2d0f7 444 return CAYENNE_SUCCESS;
jburhenn 0:09ef59d2d0f7 445 }
jburhenn 0:09ef59d2d0f7 446
jburhenn 0:09ef59d2d0f7 447
jburhenn 0:09ef59d2d0f7 448