Modified M2XStreamClient

Fork of M2XStreamClient-JMF by Jim Flynn

Committer:
jk431j
Date:
Thu Oct 26 04:27:28 2017 +0000
Revision:
26:b4cca6806ac3
Parent:
25:67079c11d1ea
Updated WNCInterface from revision 7 to 28

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jb8414 0:f479e4f4db0e 1 #ifndef M2XStreamClient_h
jb8414 0:f479e4f4db0e 2 #define M2XStreamClient_h
jb8414 0:f479e4f4db0e 3
defmacro 22:4d895e732765 4 #if (!defined(ARDUINO_PLATFORM)) && (!defined(ESP8266_PLATFORM)) && (!defined(MBED_PLATFORM))
defmacro 22:4d895e732765 5 #error "Platform definition is missing!"
jb8414 0:f479e4f4db0e 6 #endif
jb8414 0:f479e4f4db0e 7
defmacro 22:4d895e732765 8 #define M2X_VERSION "2.2.0"
jb8414 0:f479e4f4db0e 9
jb8414 0:f479e4f4db0e 10 #ifdef ARDUINO_PLATFORM
defmacro 22:4d895e732765 11 #include "m2x-arduino.h"
defmacro 22:4d895e732765 12 #endif /* ARDUINO_PLATFORM */
defmacro 22:4d895e732765 13
defmacro 22:4d895e732765 14 #ifdef ESP8266_PLATFORM
defmacro 22:4d895e732765 15 #include "m2x-esp8266.h"
defmacro 22:4d895e732765 16 #endif /* ESP8266_PLATFORM */
jb8414 0:f479e4f4db0e 17
jb8414 0:f479e4f4db0e 18 #ifdef MBED_PLATFORM
defmacro 22:4d895e732765 19 #include "m2x-mbed.h"
defmacro 22:4d895e732765 20 #endif /* MBED_PLATFORM */
defmacro 22:4d895e732765 21
defmacro 22:4d895e732765 22 /* If we don't have DBG defined, provide dump implementation */
defmacro 22:4d895e732765 23 #ifndef DBG
jb8414 0:f479e4f4db0e 24 #define DBG(fmt_, data_)
jb8414 0:f479e4f4db0e 25 #define DBGLN(fmt_, data_)
jb8414 0:f479e4f4db0e 26 #define DBGLNEND
defmacro 22:4d895e732765 27 #endif /* DBG */
jb8414 0:f479e4f4db0e 28
defmacro 22:4d895e732765 29 #define MIN(a, b) (((a) > (b))?(b):(a))
defmacro 22:4d895e732765 30 #define TO_HEX(t_) ((char) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0')))
jb8414 0:f479e4f4db0e 31 #define MAX_DOUBLE_DIGITS 7
jb8414 0:f479e4f4db0e 32
defmacro 22:4d895e732765 33 /* For tolower */
defmacro 22:4d895e732765 34 #include <ctype.h>
defmacro 22:4d895e732765 35
jb8414 0:f479e4f4db0e 36 static const int E_OK = 0;
jb8414 0:f479e4f4db0e 37 static const int E_NOCONNECTION = -1;
jb8414 0:f479e4f4db0e 38 static const int E_DISCONNECTED = -2;
jb8414 0:f479e4f4db0e 39 static const int E_NOTREACHABLE = -3;
jb8414 0:f479e4f4db0e 40 static const int E_INVALID = -4;
jb8414 0:f479e4f4db0e 41 static const int E_JSON_INVALID = -5;
citrusbyte 16:7903152de19f 42 static const int E_BUFFER_TOO_SMALL = -6;
citrusbyte 16:7903152de19f 43 static const int E_TIMESTAMP_ERROR = -8;
citrusbyte 16:7903152de19f 44
defmacro 22:4d895e732765 45 static const char* DEFAULT_M2X_HOST = "api-m2x.att.com";
defmacro 22:4d895e732765 46 static const int DEFAULT_M2X_PORT = 80;
defmacro 22:4d895e732765 47
citrusbyte 16:7903152de19f 48 static inline bool m2x_status_is_success(int status) {
citrusbyte 16:7903152de19f 49 return (status == E_OK) || (status >= 200 && status <= 299);
citrusbyte 16:7903152de19f 50 }
citrusbyte 16:7903152de19f 51
citrusbyte 16:7903152de19f 52 static inline bool m2x_status_is_client_error(int status) {
citrusbyte 16:7903152de19f 53 return status >= 400 && status <= 499;
citrusbyte 16:7903152de19f 54 }
citrusbyte 16:7903152de19f 55
citrusbyte 16:7903152de19f 56 static inline bool m2x_status_is_server_error(int status) {
citrusbyte 16:7903152de19f 57 return status >= 500 && status <= 599;
citrusbyte 16:7903152de19f 58 }
citrusbyte 16:7903152de19f 59
citrusbyte 16:7903152de19f 60 static inline bool m2x_status_is_error(int status) {
citrusbyte 16:7903152de19f 61 return m2x_status_is_client_error(status) ||
citrusbyte 16:7903152de19f 62 m2x_status_is_server_error(status);
citrusbyte 16:7903152de19f 63 }
jb8414 0:f479e4f4db0e 64
defmacro 22:4d895e732765 65 // Null Print class used to calculate length to print
defmacro 22:4d895e732765 66 class NullPrint : public Print {
defmacro 22:4d895e732765 67 public:
defmacro 22:4d895e732765 68 size_t counter;
defmacro 22:4d895e732765 69
defmacro 22:4d895e732765 70 virtual size_t write(uint8_t b) {
defmacro 22:4d895e732765 71 counter++;
defmacro 22:4d895e732765 72 return 1;
defmacro 22:4d895e732765 73 }
defmacro 22:4d895e732765 74
defmacro 22:4d895e732765 75 virtual size_t write(const uint8_t* buf, size_t size) {
defmacro 22:4d895e732765 76 counter += size;
defmacro 22:4d895e732765 77 return size;
defmacro 22:4d895e732765 78 }
defmacro 22:4d895e732765 79 };
defmacro 22:4d895e732765 80
defmacro 22:4d895e732765 81 // Encodes and prints string using Percent-encoding specified
defmacro 22:4d895e732765 82 // in RFC 1738, Section 2.2
defmacro 22:4d895e732765 83 static inline int print_encoded_string(Print* print, const char* str) {
defmacro 22:4d895e732765 84 int bytes = 0;
defmacro 22:4d895e732765 85 for (int i = 0; str[i] != 0; i++) {
defmacro 22:4d895e732765 86 if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
defmacro 22:4d895e732765 87 ((str[i] >= 'a') && (str[i] <= 'z')) ||
defmacro 22:4d895e732765 88 ((str[i] >= '0') && (str[i] <= '9')) ||
defmacro 22:4d895e732765 89 (str[i] == '-') || (str[i] == '_') ||
defmacro 22:4d895e732765 90 (str[i] == '.') || (str[i] == '~')) {
defmacro 22:4d895e732765 91 bytes += print->print(str[i]);
defmacro 22:4d895e732765 92 } else {
defmacro 22:4d895e732765 93 // Encode all other characters
defmacro 22:4d895e732765 94 bytes += print->print('%');
defmacro 22:4d895e732765 95 bytes += print->print(TO_HEX(str[i] / 16));
defmacro 22:4d895e732765 96 bytes += print->print(TO_HEX(str[i] % 16));
defmacro 22:4d895e732765 97 }
defmacro 22:4d895e732765 98 }
defmacro 22:4d895e732765 99 return bytes;
defmacro 22:4d895e732765 100 }
defmacro 22:4d895e732765 101
defmacro 22:4d895e732765 102 #ifdef M2X_ENABLE_READER
citrusbyte 10:4ce9eba38dbe 103 /*
citrusbyte 10:4ce9eba38dbe 104 * +type+ indicates the value type: 1 for string, 2 for number
citrusbyte 10:4ce9eba38dbe 105 * NOTE that the value type here only contains a hint on how
citrusbyte 10:4ce9eba38dbe 106 * you can use the value. Even though 2 is returned, the value
citrusbyte 10:4ce9eba38dbe 107 * is still stored in (const char *), and atoi/atof is needed to
citrusbyte 10:4ce9eba38dbe 108 * get the actual value
citrusbyte 10:4ce9eba38dbe 109 */
jb8414 0:f479e4f4db0e 110 typedef void (*stream_value_read_callback)(const char* at,
jb8414 0:f479e4f4db0e 111 const char* value,
jb8414 0:f479e4f4db0e 112 int index,
citrusbyte 10:4ce9eba38dbe 113 void* context,
citrusbyte 10:4ce9eba38dbe 114 int type);
jb8414 0:f479e4f4db0e 115
jb8414 0:f479e4f4db0e 116 typedef void (*location_read_callback)(const char* name,
jb8414 0:f479e4f4db0e 117 double latitude,
jb8414 0:f479e4f4db0e 118 double longitude,
jb8414 0:f479e4f4db0e 119 double elevation,
jb8414 0:f479e4f4db0e 120 const char* timestamp,
jb8414 0:f479e4f4db0e 121 int index,
jb8414 0:f479e4f4db0e 122 void* context);
JMF 23:f32837239193 123
defmacro 22:4d895e732765 124 typedef void (*m2x_command_read_callback)(const char* id,
defmacro 22:4d895e732765 125 const char* name,
defmacro 22:4d895e732765 126 int index,
defmacro 22:4d895e732765 127 void *context);
defmacro 22:4d895e732765 128 #endif /* M2X_ENABLE_READER */
defmacro 22:4d895e732765 129
defmacro 22:4d895e732765 130 typedef void (*m2x_fill_data_callback)(Print *print, void *context);
defmacro 22:4d895e732765 131
jb8414 0:f479e4f4db0e 132 class M2XStreamClient {
jb8414 0:f479e4f4db0e 133 public:
jb8414 0:f479e4f4db0e 134 M2XStreamClient(Client* client,
jb8414 0:f479e4f4db0e 135 const char* key,
defmacro 22:4d895e732765 136 void (* idlefunc)(void) = NULL,
jb8414 0:f479e4f4db0e 137 int case_insensitive = 1,
defmacro 22:4d895e732765 138 const char* host = DEFAULT_M2X_HOST,
defmacro 22:4d895e732765 139 int port = DEFAULT_M2X_PORT,
NetArc 14:205076b587fe 140 const char* path_prefix = NULL);
jb8414 0:f479e4f4db0e 141
citrusbyte 10:4ce9eba38dbe 142 // Push data stream value using PUT request, returns the HTTP status code
defmacro 22:4d895e732765 143 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 144 // the device ID here.
jb8414 0:f479e4f4db0e 145 template <class T>
citrusbyte 13:0d574742208f 146 int updateStreamValue(const char* deviceId, const char* streamName, T value);
jb8414 0:f479e4f4db0e 147
jb8414 0:f479e4f4db0e 148 // Post multiple values to M2X all at once.
citrusbyte 13:0d574742208f 149 // +deviceId+ - id of the device to post values
jb8414 0:f479e4f4db0e 150 // +streamNum+ - Number of streams to post
jb8414 0:f479e4f4db0e 151 // +names+ - Array of stream names, the length of the array should
jb8414 0:f479e4f4db0e 152 // be exactly +streamNum+
jb8414 0:f479e4f4db0e 153 // +counts+ - Array of +streamNum+ length, each item in this array
jb8414 0:f479e4f4db0e 154 // containing the number of values we want to post for each stream
jb8414 0:f479e4f4db0e 155 // +ats+ - Timestamps for each value, the length of this array should
jb8414 0:f479e4f4db0e 156 // be the some of all values in +counts+, for the first +counts[0]+
jb8414 0:f479e4f4db0e 157 // items, the values belong to the first stream, for the following
jb8414 0:f479e4f4db0e 158 // +counts[1]+ number of items, the values belong to the second stream,
citrusbyte 10:4ce9eba38dbe 159 // etc. Notice that timestamps are required here: you must provide
citrusbyte 10:4ce9eba38dbe 160 // a timestamp for each value posted.
jb8414 0:f479e4f4db0e 161 // +values+ - Values to post. This works the same way as +ats+, the
jb8414 0:f479e4f4db0e 162 // first +counts[0]+ number of items contain values to post to the first
jb8414 0:f479e4f4db0e 163 // stream, the succeeding +counts[1]+ number of items contain values
jb8414 0:f479e4f4db0e 164 // for the second stream, etc. The length of this array should be
jb8414 0:f479e4f4db0e 165 // the sum of all values in +counts+ array.
defmacro 22:4d895e732765 166 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 167 // the device ID here.
jb8414 0:f479e4f4db0e 168 template <class T>
citrusbyte 13:0d574742208f 169 int postDeviceUpdates(const char* deviceId, int streamNum,
citrusbyte 13:0d574742208f 170 const char* names[], const int counts[],
citrusbyte 13:0d574742208f 171 const char* ats[], T values[]);
jb8414 0:f479e4f4db0e 172
citrusbyte 19:4dfa28d37b8f 173 // Post multiple values of a single device at once.
citrusbyte 19:4dfa28d37b8f 174 // +deviceId+ - id of the device to post values
citrusbyte 19:4dfa28d37b8f 175 // +streamNum+ - Number of streams to post
citrusbyte 19:4dfa28d37b8f 176 // +names+ - Array of stream names, the length of the array should
citrusbyte 19:4dfa28d37b8f 177 // be exactly +streamNum+
citrusbyte 19:4dfa28d37b8f 178 // +values+ - Array of values to post, the length of the array should
citrusbyte 19:4dfa28d37b8f 179 // be exactly +streamNum+. Notice that the array of +values+ should
citrusbyte 19:4dfa28d37b8f 180 // match the array of +names+, and that the ith value in +values+ is
citrusbyte 19:4dfa28d37b8f 181 // exactly the value to post for the ith stream name in +names+
defmacro 22:4d895e732765 182 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 183 // the device ID here.
citrusbyte 19:4dfa28d37b8f 184 template <class T>
defmacro 22:4d895e732765 185 int postDeviceUpdate(const char* deviceId, int streamNum,
defmacro 22:4d895e732765 186 const char* names[], T values[],
defmacro 22:4d895e732765 187 const char* at = NULL);
citrusbyte 19:4dfa28d37b8f 188
defmacro 22:4d895e732765 189 #ifdef M2X_ENABLE_READER
jb8414 0:f479e4f4db0e 190 // Fetch values for a particular data stream. Since memory is
jb8414 0:f479e4f4db0e 191 // very limited on an Arduino, we cannot parse and get all the
jb8414 0:f479e4f4db0e 192 // data points in memory. Instead, we use callbacks here: whenever
jb8414 0:f479e4f4db0e 193 // a new data point is parsed, we call the callback using the values,
jb8414 0:f479e4f4db0e 194 // after that, the values will be thrown away to make space for new
jb8414 0:f479e4f4db0e 195 // values.
jb8414 0:f479e4f4db0e 196 // Note that you can also pass in a user-specified context in this
jb8414 0:f479e4f4db0e 197 // function, this context will be passed to the callback function
jb8414 0:f479e4f4db0e 198 // each time we get a data point.
jb8414 0:f479e4f4db0e 199 // For each data point, the callback will be called once. The HTTP
jb8414 0:f479e4f4db0e 200 // status code will be returned. And the content is only parsed when
jb8414 0:f479e4f4db0e 201 // the status code is 200.
citrusbyte 13:0d574742208f 202 int listStreamValues(const char* deviceId, const char* streamName,
citrusbyte 13:0d574742208f 203 stream_value_read_callback callback, void* context,
citrusbyte 13:0d574742208f 204 const char* query = NULL);
defmacro 22:4d895e732765 205 #endif /* M2X_ENABLE_READER */
jb8414 0:f479e4f4db0e 206
jb8414 0:f479e4f4db0e 207 // Update datasource location
jb8414 0:f479e4f4db0e 208 // NOTE: On an Arduino Uno and other ATMEGA based boards, double has
jb8414 0:f479e4f4db0e 209 // 4-byte (32 bits) precision, which is the same as float. So there's
jb8414 0:f479e4f4db0e 210 // no natural double-precision floating number on these boards. With
jb8414 0:f479e4f4db0e 211 // a float value, we have a precision of roughly 7 digits, that means
jb8414 0:f479e4f4db0e 212 // either 5 or 6 digits after the floating point. According to wikipedia,
jb8414 0:f479e4f4db0e 213 // a difference of 0.00001 will give us ~1.1132m distance. If this
jb8414 0:f479e4f4db0e 214 // precision is good for you, you can use the double-version we provided
jb8414 0:f479e4f4db0e 215 // here. Otherwise, you may need to use the string-version and do the
jb8414 0:f479e4f4db0e 216 // actual conversion by yourselves.
jb8414 0:f479e4f4db0e 217 // However, with an Arduino Due board, double has 8-bytes (64 bits)
jb8414 0:f479e4f4db0e 218 // precision, which means you are free to use the double-version only
jb8414 0:f479e4f4db0e 219 // without any precision problems.
jb8414 0:f479e4f4db0e 220 // Returned value is the http status code.
defmacro 22:4d895e732765 221 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 222 // the device ID here.
jb8414 0:f479e4f4db0e 223 template <class T>
citrusbyte 13:0d574742208f 224 int updateLocation(const char* deviceId, const char* name,
jb8414 0:f479e4f4db0e 225 T latitude, T longitude, T elevation);
jb8414 0:f479e4f4db0e 226
defmacro 22:4d895e732765 227 #ifdef M2X_ENABLE_READER
citrusbyte 13:0d574742208f 228 // Read location information for a device. Also used callback to process
jb8414 0:f479e4f4db0e 229 // data points for memory reasons. The HTTP status code is returned,
jb8414 0:f479e4f4db0e 230 // response is only parsed when the HTTP status code is 200
citrusbyte 13:0d574742208f 231 int readLocation(const char* deviceId, location_read_callback callback,
jb8414 0:f479e4f4db0e 232 void* context);
JMF 23:f32837239193 233
JMF 23:f32837239193 234 // Delete location information for a device. The HTTP status code is
JMF 23:f32837239193 235 // returned,response is only parsed when the HTTP status code is 200
JMF 24:6a5ba4cfa19a 236 int deleteLocations(const char* deviceId,
JMF 23:f32837239193 237 const char* from, const char* end);
JMF 23:f32837239193 238
defmacro 22:4d895e732765 239 #endif /* M2X_ENABLE_READER */
citrusbyte 10:4ce9eba38dbe 240
citrusbyte 10:4ce9eba38dbe 241 // Delete values from a data stream
citrusbyte 10:4ce9eba38dbe 242 // You will need to provide from and end date/time strings in the ISO8601
citrusbyte 10:4ce9eba38dbe 243 // format "yyyy-mm-ddTHH:MM:SS.SSSZ" where
citrusbyte 10:4ce9eba38dbe 244 // yyyy: the year
citrusbyte 10:4ce9eba38dbe 245 // mm: the month
citrusbyte 10:4ce9eba38dbe 246 // dd: the day
citrusbyte 10:4ce9eba38dbe 247 // HH: the hour (24 hour format)
citrusbyte 10:4ce9eba38dbe 248 // MM: the minute
citrusbyte 10:4ce9eba38dbe 249 // SS.SSS: the seconds (to the millisecond)
citrusbyte 10:4ce9eba38dbe 250 // NOTE: the time is given in Zulu (GMT)
citrusbyte 10:4ce9eba38dbe 251 // M2X will delete all values within the from to end date/time range.
citrusbyte 10:4ce9eba38dbe 252 // The status code is 204 on success and 400 on a bad request (e.g. the
citrusbyte 10:4ce9eba38dbe 253 // timestamp is not in ISO8601 format or the from timestamp is not less than
citrusbyte 10:4ce9eba38dbe 254 // or equal to the end timestamp.
citrusbyte 13:0d574742208f 255 int deleteValues(const char* deviceId, const char* streamName,
citrusbyte 10:4ce9eba38dbe 256 const char* from, const char* end);
citrusbyte 16:7903152de19f 257
defmacro 22:4d895e732765 258 #ifdef M2X_ENABLE_READER
defmacro 22:4d895e732765 259 // Fetch commands available for this device, notice that for memory constraints,
defmacro 22:4d895e732765 260 // we only keep ID and name of the command received here.
defmacro 22:4d895e732765 261 // You can tweak the command receiving via the query parameter.
defmacro 22:4d895e732765 262 int listCommands(const char* deviceId,
defmacro 22:4d895e732765 263 m2x_command_read_callback callback, void* context,
defmacro 22:4d895e732765 264 const char* query = NULL);
defmacro 22:4d895e732765 265 #endif /* M2X_ENABLE_READER */
defmacro 22:4d895e732765 266
defmacro 22:4d895e732765 267 // Mark a command as processed.
defmacro 22:4d895e732765 268 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Processed
defmacro 22:4d895e732765 269 // To make sure the minimal amount of memory is needed, this API works with
defmacro 22:4d895e732765 270 // a callback function. The callback function is then used to fill the request
defmacro 22:4d895e732765 271 // data, note that in order to correctly set the content length in HTTP header,
defmacro 22:4d895e732765 272 // the callback function will be called twice, the caller must make sure both
defmacro 22:4d895e732765 273 // calls fill the Print object with exactly the same data.
defmacro 22:4d895e732765 274 // If you have a pre-allocated buffer filled with the data to post, you can
defmacro 22:4d895e732765 275 // use markCommandProcessedWithData API below.
defmacro 22:4d895e732765 276 int markCommandProcessed(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 277 m2x_fill_data_callback callback, void *context);
defmacro 22:4d895e732765 278
defmacro 22:4d895e732765 279 // Mark a command as processed with a data buffer
defmacro 22:4d895e732765 280 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Processed
defmacro 22:4d895e732765 281 // This is exactly like markCommandProcessed, except that a buffer is use to
defmacro 22:4d895e732765 282 // contain the data to post as the request body.
defmacro 22:4d895e732765 283 int markCommandProcessedWithData(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 284 const char* data);
defmacro 22:4d895e732765 285
defmacro 22:4d895e732765 286 // Mark a command as rejected.
defmacro 22:4d895e732765 287 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Rejected
defmacro 22:4d895e732765 288 // To make sure the minimal amount of memory is needed, this API works with
defmacro 22:4d895e732765 289 // a callback function. The callback function is then used to fill the request
defmacro 22:4d895e732765 290 // data, note that in order to correctly set the content length in HTTP header,
defmacro 22:4d895e732765 291 // the callback function will be called twice, the caller must make sure both
defmacro 22:4d895e732765 292 // calls fill the Print object with exactly the same data.
defmacro 22:4d895e732765 293 // If you have a pre-allocated buffer filled with the data to post, you can
defmacro 22:4d895e732765 294 // use markCommandRejectedWithData API below.
defmacro 22:4d895e732765 295 int markCommandRejected(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 296 m2x_fill_data_callback callback, void *context);
defmacro 22:4d895e732765 297
defmacro 22:4d895e732765 298 // Mark a command as rejected with a data buffer
defmacro 22:4d895e732765 299 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Rejected
defmacro 22:4d895e732765 300 // This is exactly like markCommandRejected, except that a buffer is use to
defmacro 22:4d895e732765 301 // contain the data to post as the request body.
defmacro 22:4d895e732765 302 int markCommandRejectedWithData(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 303 const char* data);
defmacro 22:4d895e732765 304
citrusbyte 16:7903152de19f 305 // Fetches current timestamp in seconds from M2X server. Since we
citrusbyte 16:7903152de19f 306 // are using signed 32-bit integer as return value, this will only
citrusbyte 16:7903152de19f 307 // return valid results before 03:14:07 UTC on 19 January 2038. If
citrusbyte 16:7903152de19f 308 // the device is supposed to work after that, this function should
citrusbyte 16:7903152de19f 309 // not be used.
citrusbyte 16:7903152de19f 310 //
citrusbyte 16:7903152de19f 311 // The returned value will contain the status code(positive values)
citrusbyte 16:7903152de19f 312 // or the error code(negative values).
citrusbyte 16:7903152de19f 313 // In case of success, the current timestamp will be filled in the
citrusbyte 16:7903152de19f 314 // +ts+ pointer passed in as argument.
citrusbyte 16:7903152de19f 315 //
citrusbyte 16:7903152de19f 316 // NOTE: although returning uint32_t can give us a larger space,
citrusbyte 16:7903152de19f 317 // we prefer to cope with the unix convention here.
citrusbyte 16:7903152de19f 318 int getTimestamp32(int32_t* ts);
citrusbyte 16:7903152de19f 319
citrusbyte 16:7903152de19f 320 // Fetches current timestamp in seconds from M2X server.
citrusbyte 16:7903152de19f 321 // This function will return the timestamp as an integer literal
citrusbyte 16:7903152de19f 322 // in the provided buffer. Hence there's no problem working after
citrusbyte 16:7903152de19f 323 // 03:14:07 UTC on 19 January 2038. The drawback part here, is that
citrusbyte 16:7903152de19f 324 // you will have to work with 64-bit integer, which is not available
citrusbyte 16:7903152de19f 325 // on certain platform(such as Arduino), a bignum library or alike
citrusbyte 16:7903152de19f 326 // is needed in this case.
citrusbyte 16:7903152de19f 327 //
citrusbyte 16:7903152de19f 328 // Notice +bufferLength+ is supposed to contain the length of the
citrusbyte 16:7903152de19f 329 // buffer when calling this function. It is also the caller's
citrusbyte 16:7903152de19f 330 // responsibility to ensure the buffer is big enough, otherwise
citrusbyte 16:7903152de19f 331 // the library will return an error indicating the buffer is too
citrusbyte 16:7903152de19f 332 // small.
citrusbyte 16:7903152de19f 333 // While this is not accurate all the time, one trick here is to
citrusbyte 16:7903152de19f 334 // pass in 0 as the bufferLength, in which case we will always return
citrusbyte 16:7903152de19f 335 // the buffer-too-small error. However, the correct buffer length
citrusbyte 16:7903152de19f 336 // can be found this way so a secound execution is most likely to work
citrusbyte 16:7903152de19f 337 // (unless we are at the edge of the buffer length increasing, for
citrusbyte 16:7903152de19f 338 // example, when the timestamp jumps from 9999999999 to 10000000000,
citrusbyte 16:7903152de19f 339 // which is highly unlikely to happend). However, given that the
citrusbyte 16:7903152de19f 340 // maximum 64-bit integer can be stored in 19 bytes, there's not
citrusbyte 16:7903152de19f 341 // much need to use this trick.)
citrusbyte 16:7903152de19f 342 //
citrusbyte 16:7903152de19f 343 // The returned value will contain the status code(positive values)
citrusbyte 16:7903152de19f 344 // or the error code(negative values).
citrusbyte 16:7903152de19f 345 // In case of success, the current timestamp will be filled in the
citrusbyte 16:7903152de19f 346 // passed +buffer+ pointer, and the actual used buffer length will
citrusbyte 16:7903152de19f 347 // be returned in +bufferLength+ pointer.
citrusbyte 16:7903152de19f 348 // NOTE: as long as we can read the returned buffer length, it will
citrusbyte 16:7903152de19f 349 // be used to fill in the +bufferLength+ variable even though other
citrusbyte 16:7903152de19f 350 // errors occur(buffer is not enough, network is shutdown before
citrusbyte 16:7903152de19f 351 // reading the whole buffer, etc.)
citrusbyte 16:7903152de19f 352 int getTimestamp(char* buffer, int* bufferLength);
jb8414 0:f479e4f4db0e 353 private:
jb8414 0:f479e4f4db0e 354 Client* _client;
jb8414 0:f479e4f4db0e 355 const char* _key;
jb8414 0:f479e4f4db0e 356 int _case_insensitive;
jb8414 0:f479e4f4db0e 357 const char* _host;
jb8414 0:f479e4f4db0e 358 int _port;
defmacro 22:4d895e732765 359 void (* _idlefunc)(void);
NetArc 14:205076b587fe 360 const char* _path_prefix;
jb8414 0:f479e4f4db0e 361 NullPrint _null_print;
jb8414 0:f479e4f4db0e 362
jb8414 0:f479e4f4db0e 363 // Writes the HTTP header part for updating a stream value
citrusbyte 13:0d574742208f 364 void writePutHeader(const char* deviceId,
citrusbyte 10:4ce9eba38dbe 365 const char* streamName,
citrusbyte 10:4ce9eba38dbe 366 int contentLength);
citrusbyte 10:4ce9eba38dbe 367 // Writes the HTTP header part for deleting stream values
citrusbyte 13:0d574742208f 368 void writeDeleteHeader(const char* deviceId,
citrusbyte 10:4ce9eba38dbe 369 const char* streamName,
citrusbyte 10:4ce9eba38dbe 370 int contentLength);
JMF 23:f32837239193 371 void writeDeleteLocationHeader(const char* deviceId,
JMF 23:f32837239193 372 int contentLength);
JMF 23:f32837239193 373
jb8414 0:f479e4f4db0e 374 // Writes HTTP header lines including M2X API Key, host, content
jb8414 0:f479e4f4db0e 375 // type and content length(if the body exists)
jb8414 0:f479e4f4db0e 376 void writeHttpHeader(int contentLength);
jb8414 0:f479e4f4db0e 377 // Parses HTTP response header and return the content length.
jb8414 0:f479e4f4db0e 378 // Note that this function does not parse all http headers, as long
jb8414 0:f479e4f4db0e 379 // as the content length is found, this function will return
jb8414 0:f479e4f4db0e 380 int readContentLength();
jb8414 0:f479e4f4db0e 381 // Skips all HTTP response header part. Return minus value in case
jb8414 0:f479e4f4db0e 382 // the connection is closed before we got all headers
jb8414 0:f479e4f4db0e 383 int skipHttpHeader();
jb8414 0:f479e4f4db0e 384 // Parses and returns the HTTP status code, note this function will
jb8414 0:f479e4f4db0e 385 // return immediately once it gets the status code
jb8414 0:f479e4f4db0e 386 int readStatusCode(bool closeClient);
jb8414 0:f479e4f4db0e 387 // Waits for a certain string pattern in the HTTP header, and returns
jb8414 0:f479e4f4db0e 388 // once the pattern is found. In the pattern, you can use '*' to denote
jb8414 0:f479e4f4db0e 389 // any character
jb8414 0:f479e4f4db0e 390 int waitForString(const char* str);
jb8414 0:f479e4f4db0e 391 // Closes the connection
jb8414 0:f479e4f4db0e 392 void close();
defmacro 22:4d895e732765 393
defmacro 22:4d895e732765 394 #ifdef M2X_ENABLE_READER
jb8414 0:f479e4f4db0e 395 // Parses JSON response of stream value API, and calls callback function
jb8414 0:f479e4f4db0e 396 // once we get a data point
jb8414 0:f479e4f4db0e 397 int readStreamValue(stream_value_read_callback callback, void* context);
jb8414 0:f479e4f4db0e 398 // Parses JSON response of location API, and calls callback function once
jb8414 0:f479e4f4db0e 399 // we get a data point
jb8414 0:f479e4f4db0e 400 int readLocation(location_read_callback callback, void* context);
defmacro 22:4d895e732765 401 // Parses JSON response of command API, and calls callback function once
defmacro 22:4d895e732765 402 // we get a data point
defmacro 22:4d895e732765 403 int readCommand(m2x_command_read_callback callback, void* context);
defmacro 22:4d895e732765 404 #endif /* M2X_ENABLE_READER */
defmacro 22:4d895e732765 405 };
defmacro 22:4d895e732765 406
defmacro 22:4d895e732765 407
defmacro 22:4d895e732765 408 // A ISO8601 timestamp generation service for M2X.
defmacro 22:4d895e732765 409 // It uses the Time API provided by the M2X server to initialize
defmacro 22:4d895e732765 410 // clock, then uses millis() function provided by Arduino to calculate
defmacro 22:4d895e732765 411 // time advancements so as to reduce API query times.
defmacro 22:4d895e732765 412 //
defmacro 22:4d895e732765 413 // Right now, this service only works with 32-bit timestamp, meaning that
defmacro 22:4d895e732765 414 // this service won't work after 03:14:07 UTC on 19 January 2038. However,
defmacro 22:4d895e732765 415 // a similar service that uses 64-bit timestamp can be implemented following
defmacro 22:4d895e732765 416 // the logic here.
defmacro 22:4d895e732765 417 class TimeService {
defmacro 22:4d895e732765 418 public:
defmacro 22:4d895e732765 419 TimeService(M2XStreamClient* client);
defmacro 22:4d895e732765 420
defmacro 22:4d895e732765 421 // Initialize the time service. Notice the TimeService instance is only
defmacro 22:4d895e732765 422 // working after calling this function successfully.
defmacro 22:4d895e732765 423 int init();
defmacro 22:4d895e732765 424
defmacro 22:4d895e732765 425 // Reset the internal recorded time by calling M2X Time API again. Normally,
defmacro 22:4d895e732765 426 // you don't need to call this manually. TimeService will handle Arduino clock
defmacro 22:4d895e732765 427 // overflow automatically
defmacro 22:4d895e732765 428 int reset();
defmacro 22:4d895e732765 429
defmacro 22:4d895e732765 430 // Fills ISO8601 formatted timestamp into the buffer provided. +length+ should
defmacro 22:4d895e732765 431 // contains the maximum supported length of the buffer when calling. For now,
defmacro 22:4d895e732765 432 // the buffer should be able to store 25 characters for a full ISO8601 formatted
defmacro 22:4d895e732765 433 // timestamp, otherwise, an error will be returned.
defmacro 22:4d895e732765 434 int getTimestamp(char* buffer, int* length);
defmacro 22:4d895e732765 435 private:
defmacro 22:4d895e732765 436 M2XStreamClient* _client;
defmacro 22:4d895e732765 437 int32_t _server_timestamp;
defmacro 22:4d895e732765 438 uint32_t _local_last_milli;
defmacro 22:4d895e732765 439 M2XTimer _timer;
jb8414 0:f479e4f4db0e 440 };
jb8414 0:f479e4f4db0e 441
defmacro 22:4d895e732765 442
defmacro 22:4d895e732765 443 // Implementations
defmacro 22:4d895e732765 444 M2XStreamClient::M2XStreamClient(Client* client,
defmacro 22:4d895e732765 445 const char* key,
defmacro 22:4d895e732765 446 void (* idlefunc)(void),
defmacro 22:4d895e732765 447 int case_insensitive,
defmacro 22:4d895e732765 448 const char* host,
defmacro 22:4d895e732765 449 int port,
defmacro 22:4d895e732765 450 const char* path_prefix) : _client(client),
defmacro 22:4d895e732765 451 _key(key),
defmacro 22:4d895e732765 452 _idlefunc(idlefunc),
defmacro 22:4d895e732765 453 _case_insensitive(case_insensitive),
defmacro 22:4d895e732765 454 _host(host),
defmacro 22:4d895e732765 455 _port(port),
defmacro 22:4d895e732765 456 _path_prefix(path_prefix),
defmacro 22:4d895e732765 457 _null_print() {
defmacro 22:4d895e732765 458 }
defmacro 22:4d895e732765 459
defmacro 22:4d895e732765 460 template <class T>
defmacro 22:4d895e732765 461 int M2XStreamClient::updateStreamValue(const char* deviceId, const char* streamName, T value) {
defmacro 22:4d895e732765 462 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 463 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 464 writePutHeader(deviceId, streamName,
defmacro 22:4d895e732765 465 // for {"value": and }
defmacro 22:4d895e732765 466 _null_print.print(value) + 12);
defmacro 22:4d895e732765 467 _client->print("{\"value\":\"");
defmacro 22:4d895e732765 468 _client->print(value);
defmacro 22:4d895e732765 469 _client->print("\"}");
defmacro 22:4d895e732765 470 } else {
defmacro 22:4d895e732765 471 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 472 return E_NOCONNECTION;
defmacro 22:4d895e732765 473 }
defmacro 22:4d895e732765 474
defmacro 22:4d895e732765 475 return readStatusCode(true);
defmacro 22:4d895e732765 476 }
defmacro 22:4d895e732765 477
defmacro 22:4d895e732765 478 template <class T>
defmacro 22:4d895e732765 479 inline int write_multiple_values(Print* print, int streamNum,
defmacro 22:4d895e732765 480 const char* names[], const int counts[],
defmacro 22:4d895e732765 481 const char* ats[], T values[]) {
defmacro 22:4d895e732765 482 int bytes = 0, value_index = 0;
defmacro 22:4d895e732765 483 bytes += print->print("{\"values\":{");
defmacro 22:4d895e732765 484 for (int i = 0; i < streamNum; i++) {
defmacro 22:4d895e732765 485 bytes += print->print("\"");
defmacro 22:4d895e732765 486 bytes += print->print(names[i]);
defmacro 22:4d895e732765 487 bytes += print->print("\":[");
defmacro 22:4d895e732765 488 for (int j = 0; j < counts[i]; j++) {
defmacro 22:4d895e732765 489 bytes += print->print("{\"timestamp\": \"");
defmacro 22:4d895e732765 490 bytes += print->print(ats[value_index]);
defmacro 22:4d895e732765 491 bytes += print->print("\",\"value\": \"");
defmacro 22:4d895e732765 492 bytes += print->print(values[value_index]);
defmacro 22:4d895e732765 493 bytes += print->print("\"}");
defmacro 22:4d895e732765 494 if (j < counts[i] - 1) { bytes += print->print(","); }
defmacro 22:4d895e732765 495 value_index++;
defmacro 22:4d895e732765 496 }
defmacro 22:4d895e732765 497 bytes += print->print("]");
defmacro 22:4d895e732765 498 if (i < streamNum - 1) { bytes += print->print(","); }
defmacro 22:4d895e732765 499 }
defmacro 22:4d895e732765 500 bytes += print->print("}}");
defmacro 22:4d895e732765 501 return bytes;
defmacro 22:4d895e732765 502 }
defmacro 22:4d895e732765 503
defmacro 22:4d895e732765 504 template <class T>
defmacro 22:4d895e732765 505 int M2XStreamClient::postDeviceUpdates(const char* deviceId, int streamNum,
defmacro 22:4d895e732765 506 const char* names[], const int counts[],
defmacro 22:4d895e732765 507 const char* ats[], T values[]) {
defmacro 22:4d895e732765 508 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 509 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 510 int length = write_multiple_values(&_null_print, streamNum, names,
defmacro 22:4d895e732765 511 counts, ats, values);
defmacro 22:4d895e732765 512 _client->print("POST ");
defmacro 22:4d895e732765 513 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 514 _client->print("/v2/devices/");
defmacro 22:4d895e732765 515 _client->print(deviceId);
JMF 25:67079c11d1ea 516 _client->println("/updates HTTP/1.1");
defmacro 22:4d895e732765 517 writeHttpHeader(length);
defmacro 22:4d895e732765 518 write_multiple_values(_client, streamNum, names, counts, ats, values);
defmacro 22:4d895e732765 519 } else {
defmacro 22:4d895e732765 520 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 521 return E_NOCONNECTION;
defmacro 22:4d895e732765 522 }
defmacro 22:4d895e732765 523 return readStatusCode(true);
defmacro 22:4d895e732765 524 }
defmacro 22:4d895e732765 525
defmacro 22:4d895e732765 526 template <class T>
defmacro 22:4d895e732765 527 inline int write_single_device_values(Print* print, int streamNum,
defmacro 22:4d895e732765 528 const char* names[], T values[],
defmacro 22:4d895e732765 529 const char* at) {
defmacro 22:4d895e732765 530 int bytes = 0;
defmacro 22:4d895e732765 531 bytes += print->print("{\"values\":{");
defmacro 22:4d895e732765 532 for (int i = 0; i < streamNum; i++) {
defmacro 22:4d895e732765 533 bytes += print->print("\"");
defmacro 22:4d895e732765 534 bytes += print->print(names[i]);
defmacro 22:4d895e732765 535 bytes += print->print("\": \"");
defmacro 22:4d895e732765 536 bytes += print->print(values[i]);
defmacro 22:4d895e732765 537 bytes += print->print("\"");
defmacro 22:4d895e732765 538 if (i < streamNum - 1) { bytes += print->print(","); }
defmacro 22:4d895e732765 539 }
defmacro 22:4d895e732765 540 bytes += print->print("}");
defmacro 22:4d895e732765 541 if (at != NULL) {
defmacro 22:4d895e732765 542 bytes += print->print(",\"timestamp\":\"");
defmacro 22:4d895e732765 543 bytes += print->print(at);
defmacro 22:4d895e732765 544 bytes += print->print("\"");
defmacro 22:4d895e732765 545 }
defmacro 22:4d895e732765 546 bytes += print->print("}");
defmacro 22:4d895e732765 547 return bytes;
defmacro 22:4d895e732765 548 }
defmacro 22:4d895e732765 549
defmacro 22:4d895e732765 550 template <class T>
defmacro 22:4d895e732765 551 int M2XStreamClient::postDeviceUpdate(const char* deviceId, int streamNum,
defmacro 22:4d895e732765 552 const char* names[], T values[],
defmacro 22:4d895e732765 553 const char* at) {
defmacro 22:4d895e732765 554 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 555 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 556 int length = write_single_device_values(&_null_print, streamNum, names,
defmacro 22:4d895e732765 557 values, at);
defmacro 22:4d895e732765 558 _client->print("POST ");
defmacro 22:4d895e732765 559 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 560 _client->print("/v2/devices/");
defmacro 22:4d895e732765 561 _client->print(deviceId);
JMF 25:67079c11d1ea 562 _client->println("/update HTTP/1.1");
defmacro 22:4d895e732765 563 writeHttpHeader(length);
defmacro 22:4d895e732765 564 write_single_device_values(_client, streamNum, names, values, at);
defmacro 22:4d895e732765 565 } else {
defmacro 22:4d895e732765 566 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 567 return E_NOCONNECTION;
defmacro 22:4d895e732765 568 }
defmacro 22:4d895e732765 569 return readStatusCode(true);
defmacro 22:4d895e732765 570 }
defmacro 22:4d895e732765 571
defmacro 22:4d895e732765 572 template <class T>
defmacro 22:4d895e732765 573 static int write_location_data(Print* print, const char* name,
defmacro 22:4d895e732765 574 T latitude, T longitude,
defmacro 22:4d895e732765 575 T elevation) {
defmacro 22:4d895e732765 576 int bytes = 0;
defmacro 22:4d895e732765 577 bytes += print->print("{\"name\":\"");
defmacro 22:4d895e732765 578 bytes += print->print(name);
defmacro 22:4d895e732765 579 bytes += print->print("\",\"latitude\":\"");
defmacro 22:4d895e732765 580 bytes += print->print(latitude);
defmacro 22:4d895e732765 581 bytes += print->print("\",\"longitude\":\"");
defmacro 22:4d895e732765 582 bytes += print->print(longitude);
defmacro 22:4d895e732765 583 bytes += print->print("\",\"elevation\":\"");
defmacro 22:4d895e732765 584 bytes += print->print(elevation);
defmacro 22:4d895e732765 585 bytes += print->print("\"}");
defmacro 22:4d895e732765 586 return bytes;
defmacro 22:4d895e732765 587 }
defmacro 22:4d895e732765 588
defmacro 22:4d895e732765 589 static int write_location_data(Print* print, const char* name,
defmacro 22:4d895e732765 590 double latitude, double longitude,
defmacro 22:4d895e732765 591 double elevation) {
defmacro 22:4d895e732765 592 int bytes = 0;
defmacro 22:4d895e732765 593 bytes += print->print("{\"name\":\"");
defmacro 22:4d895e732765 594 bytes += print->print(name);
defmacro 22:4d895e732765 595 bytes += print->print("\",\"latitude\":\"");
defmacro 22:4d895e732765 596 bytes += print->print(latitude, MAX_DOUBLE_DIGITS);
defmacro 22:4d895e732765 597 bytes += print->print("\",\"longitude\":\"");
defmacro 22:4d895e732765 598 bytes += print->print(longitude, MAX_DOUBLE_DIGITS);
defmacro 22:4d895e732765 599 bytes += print->print("\",\"elevation\":\"");
defmacro 22:4d895e732765 600 bytes += print->print(elevation);
defmacro 22:4d895e732765 601 bytes += print->print("\"}");
defmacro 22:4d895e732765 602 return bytes;
defmacro 22:4d895e732765 603 }
defmacro 22:4d895e732765 604
defmacro 22:4d895e732765 605 template <class T>
defmacro 22:4d895e732765 606 int M2XStreamClient::updateLocation(const char* deviceId,
defmacro 22:4d895e732765 607 const char* name,
defmacro 22:4d895e732765 608 T latitude,
defmacro 22:4d895e732765 609 T longitude,
defmacro 22:4d895e732765 610 T elevation) {
defmacro 22:4d895e732765 611 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 612 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 613
defmacro 22:4d895e732765 614 int length = write_location_data(&_null_print, name, latitude, longitude,
defmacro 22:4d895e732765 615 elevation);
defmacro 22:4d895e732765 616 _client->print("PUT ");
defmacro 22:4d895e732765 617 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 618 _client->print("/v2/devices/");
defmacro 22:4d895e732765 619 _client->print(deviceId);
JMF 25:67079c11d1ea 620 _client->println("/location HTTP/1.1");
defmacro 22:4d895e732765 621
defmacro 22:4d895e732765 622 writeHttpHeader(length);
defmacro 22:4d895e732765 623 write_location_data(_client, name, latitude, longitude, elevation);
defmacro 22:4d895e732765 624 } else {
defmacro 22:4d895e732765 625 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 626 return E_NOCONNECTION;
defmacro 22:4d895e732765 627 }
defmacro 22:4d895e732765 628 return readStatusCode(true);
defmacro 22:4d895e732765 629 }
defmacro 22:4d895e732765 630
defmacro 22:4d895e732765 631 static inline int write_delete_values(Print* print, const char* from,
defmacro 22:4d895e732765 632 const char* end) {
defmacro 22:4d895e732765 633 int bytes = 0;
defmacro 22:4d895e732765 634 bytes += print->print("{\"from\":\"");
defmacro 22:4d895e732765 635 bytes += print->print(from);
defmacro 22:4d895e732765 636 bytes += print->print("\",\"end\":\"");
defmacro 22:4d895e732765 637 bytes += print->print(end);
defmacro 22:4d895e732765 638 bytes += print->print("\"}");
defmacro 22:4d895e732765 639 return bytes;
defmacro 22:4d895e732765 640 }
defmacro 22:4d895e732765 641
defmacro 22:4d895e732765 642 int M2XStreamClient::deleteValues(const char* deviceId, const char* streamName,
defmacro 22:4d895e732765 643 const char* from, const char* end) {
defmacro 22:4d895e732765 644 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 645 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 646 int length = write_delete_values(&_null_print, from, end);
defmacro 22:4d895e732765 647 writeDeleteHeader(deviceId, streamName, length);
defmacro 22:4d895e732765 648 write_delete_values(_client, from, end);
defmacro 22:4d895e732765 649 } else {
defmacro 22:4d895e732765 650 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 651 return E_NOCONNECTION;
defmacro 22:4d895e732765 652 }
defmacro 22:4d895e732765 653
defmacro 22:4d895e732765 654 return readStatusCode(true);
defmacro 22:4d895e732765 655 }
defmacro 22:4d895e732765 656
defmacro 22:4d895e732765 657 int M2XStreamClient::markCommandProcessed(const char* deviceId,
defmacro 22:4d895e732765 658 const char* commandId,
defmacro 22:4d895e732765 659 m2x_fill_data_callback callback,
defmacro 22:4d895e732765 660 void *context) {
defmacro 22:4d895e732765 661 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 662 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 663 _null_print.counter = 0;
defmacro 22:4d895e732765 664 callback(&_null_print, context);
defmacro 22:4d895e732765 665 int length = _null_print.counter;
defmacro 22:4d895e732765 666 _client->print("POST ");
defmacro 22:4d895e732765 667 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 668 _client->print("/v2/devices/");
defmacro 22:4d895e732765 669 _client->print(deviceId);
defmacro 22:4d895e732765 670 _client->print("/commands/");
defmacro 22:4d895e732765 671 _client->print(commandId);
defmacro 22:4d895e732765 672 _client->print("/process");
JMF 25:67079c11d1ea 673 _client->println(" HTTP/1.1");
defmacro 22:4d895e732765 674 writeHttpHeader(length);
defmacro 22:4d895e732765 675 callback(_client, context);
defmacro 22:4d895e732765 676 } else {
defmacro 22:4d895e732765 677 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 678 return E_NOCONNECTION;
defmacro 22:4d895e732765 679 }
defmacro 22:4d895e732765 680 return readStatusCode(true);
defmacro 22:4d895e732765 681 }
defmacro 22:4d895e732765 682
defmacro 22:4d895e732765 683 static void m2x_fixed_buffer_filling_callback(Print *print, void *context) {
defmacro 22:4d895e732765 684 if (context) {
defmacro 22:4d895e732765 685 print->print((const char *) context);
defmacro 22:4d895e732765 686 }
defmacro 22:4d895e732765 687 }
defmacro 22:4d895e732765 688
defmacro 22:4d895e732765 689 int M2XStreamClient::markCommandProcessedWithData(const char* deviceId,
defmacro 22:4d895e732765 690 const char* commandId,
defmacro 22:4d895e732765 691 const char* data) {
defmacro 22:4d895e732765 692 return markCommandProcessed(deviceId, commandId,
defmacro 22:4d895e732765 693 m2x_fixed_buffer_filling_callback, (void *) data);
defmacro 22:4d895e732765 694 }
defmacro 22:4d895e732765 695
defmacro 22:4d895e732765 696 int M2XStreamClient::markCommandRejected(const char* deviceId,
defmacro 22:4d895e732765 697 const char* commandId,
defmacro 22:4d895e732765 698 m2x_fill_data_callback callback,
defmacro 22:4d895e732765 699 void *context) {
defmacro 22:4d895e732765 700 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 701 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 702 _null_print.counter = 0;
defmacro 22:4d895e732765 703 callback(&_null_print, context);
defmacro 22:4d895e732765 704 int length = _null_print.counter;
defmacro 22:4d895e732765 705 _client->print("POST ");
defmacro 22:4d895e732765 706 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 707 _client->print("/v2/devices/");
defmacro 22:4d895e732765 708 _client->print(deviceId);
defmacro 22:4d895e732765 709 _client->print("/commands/");
defmacro 22:4d895e732765 710 _client->print(commandId);
defmacro 22:4d895e732765 711 _client->print("/reject");
JMF 25:67079c11d1ea 712 _client->println(" HTTP/1.1");
defmacro 22:4d895e732765 713 writeHttpHeader(length);
defmacro 22:4d895e732765 714 callback(_client, context);
defmacro 22:4d895e732765 715 } else {
defmacro 22:4d895e732765 716 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 717 return E_NOCONNECTION;
defmacro 22:4d895e732765 718 }
defmacro 22:4d895e732765 719 return readStatusCode(true);
defmacro 22:4d895e732765 720 }
defmacro 22:4d895e732765 721
defmacro 22:4d895e732765 722 int M2XStreamClient::markCommandRejectedWithData(const char* deviceId,
defmacro 22:4d895e732765 723 const char* commandId,
defmacro 22:4d895e732765 724 const char* data) {
defmacro 22:4d895e732765 725 return markCommandRejected(deviceId, commandId,
defmacro 22:4d895e732765 726 m2x_fixed_buffer_filling_callback, (void *) data);
defmacro 22:4d895e732765 727 }
defmacro 22:4d895e732765 728
defmacro 22:4d895e732765 729 int M2XStreamClient::getTimestamp32(int32_t *ts) {
defmacro 22:4d895e732765 730 // The maximum value of signed 64-bit integer is 0x7fffffffffffffff,
defmacro 22:4d895e732765 731 // which is 9223372036854775807. It consists of 19 characters, so a
defmacro 22:4d895e732765 732 // buffer of 20 is definitely enough here
defmacro 22:4d895e732765 733 int length = 20;
defmacro 22:4d895e732765 734 char buffer[20];
defmacro 22:4d895e732765 735 int status = getTimestamp(buffer, &length);
defmacro 22:4d895e732765 736 if (status == 200) {
defmacro 22:4d895e732765 737 int32_t result = 0;
defmacro 22:4d895e732765 738 for (int i = 0; i < length; i++) {
defmacro 22:4d895e732765 739 result = result * 10 + (buffer[i] - '0');
defmacro 22:4d895e732765 740 }
defmacro 22:4d895e732765 741 if (ts != NULL) { *ts = result; }
defmacro 22:4d895e732765 742 }
defmacro 22:4d895e732765 743 return status;
defmacro 22:4d895e732765 744 }
defmacro 22:4d895e732765 745
defmacro 22:4d895e732765 746 int M2XStreamClient::getTimestamp(char* buffer, int *bufferLength) {
defmacro 22:4d895e732765 747 if (bufferLength == NULL) { return E_INVALID; }
defmacro 22:4d895e732765 748 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 749 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 750 _client->print("GET ");
defmacro 22:4d895e732765 751 if (_path_prefix) { _client->print(_path_prefix); }
JMF 25:67079c11d1ea 752 _client->println("/v2/time/seconds HTTP/1.1");
defmacro 22:4d895e732765 753
defmacro 22:4d895e732765 754 writeHttpHeader(-1);
defmacro 22:4d895e732765 755 } else {
defmacro 22:4d895e732765 756 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 757 return E_NOCONNECTION;
defmacro 22:4d895e732765 758 }
defmacro 22:4d895e732765 759 int status = readStatusCode(false);
defmacro 22:4d895e732765 760 if (status == 200) {
defmacro 22:4d895e732765 761 int length = readContentLength();
defmacro 22:4d895e732765 762 if (length < 0) {
defmacro 22:4d895e732765 763 close();
defmacro 22:4d895e732765 764 return length;
defmacro 22:4d895e732765 765 }
defmacro 22:4d895e732765 766 if (*bufferLength < length) {
defmacro 22:4d895e732765 767 *bufferLength = length;
defmacro 22:4d895e732765 768 return E_BUFFER_TOO_SMALL;
defmacro 22:4d895e732765 769 }
defmacro 22:4d895e732765 770 *bufferLength = length;
defmacro 22:4d895e732765 771 int index = skipHttpHeader();
defmacro 22:4d895e732765 772 if (index != E_OK) {
defmacro 22:4d895e732765 773 close();
defmacro 22:4d895e732765 774 return index;
defmacro 22:4d895e732765 775 }
defmacro 22:4d895e732765 776 index = 0;
defmacro 22:4d895e732765 777 while (index < length) {
defmacro 22:4d895e732765 778 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 779 while ((index < length) && _client->available()) {
defmacro 22:4d895e732765 780 buffer[index++] = _client->read();
defmacro 22:4d895e732765 781 DBG("%c", buffer[index - 1]);
defmacro 22:4d895e732765 782 }
defmacro 22:4d895e732765 783 DBGLNEND;
defmacro 22:4d895e732765 784
defmacro 22:4d895e732765 785 if ((!_client->connected()) &&
defmacro 22:4d895e732765 786 (index < length)) {
defmacro 22:4d895e732765 787 close();
defmacro 22:4d895e732765 788 return E_NOCONNECTION;
defmacro 22:4d895e732765 789 }
defmacro 22:4d895e732765 790
defmacro 22:4d895e732765 791 if (_idlefunc!=NULL) {
defmacro 22:4d895e732765 792 _idlefunc();
defmacro 22:4d895e732765 793 } else {
defmacro 22:4d895e732765 794 delay(200);
defmacro 22:4d895e732765 795 }
defmacro 22:4d895e732765 796 }
defmacro 22:4d895e732765 797 }
defmacro 22:4d895e732765 798 close();
defmacro 22:4d895e732765 799 return status;
defmacro 22:4d895e732765 800 }
defmacro 22:4d895e732765 801
defmacro 22:4d895e732765 802
defmacro 22:4d895e732765 803 void M2XStreamClient::writePutHeader(const char* deviceId,
defmacro 22:4d895e732765 804 const char* streamName,
defmacro 22:4d895e732765 805 int contentLength) {
defmacro 22:4d895e732765 806 _client->print("PUT ");
defmacro 22:4d895e732765 807 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 808 _client->print("/v2/devices/");
defmacro 22:4d895e732765 809 _client->print(deviceId);
defmacro 22:4d895e732765 810 _client->print("/streams/");
defmacro 22:4d895e732765 811 print_encoded_string(_client, streamName);
JMF 25:67079c11d1ea 812 _client->println("/value HTTP/1.1");
defmacro 22:4d895e732765 813
defmacro 22:4d895e732765 814 writeHttpHeader(contentLength);
defmacro 22:4d895e732765 815 }
defmacro 22:4d895e732765 816
defmacro 22:4d895e732765 817 void M2XStreamClient::writeDeleteHeader(const char* deviceId,
defmacro 22:4d895e732765 818 const char* streamName,
defmacro 22:4d895e732765 819 int contentLength) {
defmacro 22:4d895e732765 820 _client->print("DELETE ");
defmacro 22:4d895e732765 821 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 822 _client->print("/v2/devices/");
defmacro 22:4d895e732765 823 _client->print(deviceId);
defmacro 22:4d895e732765 824 _client->print("/streams/");
defmacro 22:4d895e732765 825 print_encoded_string(_client, streamName);
defmacro 22:4d895e732765 826 _client->print("/values");
JMF 25:67079c11d1ea 827 _client->println(" HTTP/1.1");
defmacro 22:4d895e732765 828
defmacro 22:4d895e732765 829 writeHttpHeader(contentLength);
defmacro 22:4d895e732765 830 }
defmacro 22:4d895e732765 831
JMF 23:f32837239193 832 void M2XStreamClient::writeDeleteLocationHeader(const char* deviceId,
JMF 23:f32837239193 833 int contentLength) {
JMF 23:f32837239193 834 _client->print("DELETE ");
JMF 23:f32837239193 835 if (_path_prefix) { _client->print(_path_prefix); }
JMF 23:f32837239193 836 _client->print("/v2/devices/");
JMF 23:f32837239193 837 _client->print(deviceId);
JMF 23:f32837239193 838 _client->print("/location/waypoints/");
JMF 25:67079c11d1ea 839 _client->println(" HTTP/1.1");
JMF 23:f32837239193 840
JMF 23:f32837239193 841 writeHttpHeader(contentLength);
JMF 23:f32837239193 842 }
JMF 23:f32837239193 843
JMF 23:f32837239193 844
defmacro 22:4d895e732765 845 void M2XStreamClient::writeHttpHeader(int contentLength) {
defmacro 22:4d895e732765 846 _client->println(USER_AGENT);
defmacro 22:4d895e732765 847 _client->print("X-M2X-KEY: ");
defmacro 22:4d895e732765 848 _client->println(_key);
defmacro 22:4d895e732765 849
defmacro 22:4d895e732765 850 _client->print("Host: ");
defmacro 22:4d895e732765 851 print_encoded_string(_client, _host);
defmacro 22:4d895e732765 852 if (_port != DEFAULT_M2X_PORT) {
defmacro 22:4d895e732765 853 _client->print(":");
defmacro 22:4d895e732765 854 // port is an integer, does not need encoding
defmacro 22:4d895e732765 855 _client->print(_port);
defmacro 22:4d895e732765 856 }
defmacro 22:4d895e732765 857 _client->println();
defmacro 22:4d895e732765 858
defmacro 22:4d895e732765 859 if (contentLength > 0) {
defmacro 22:4d895e732765 860 _client->println("Content-Type: application/json");
defmacro 22:4d895e732765 861 DBG("%s", "Content Length: ");
defmacro 22:4d895e732765 862 DBGLN("%d", contentLength);
defmacro 22:4d895e732765 863
defmacro 22:4d895e732765 864 _client->print("Content-Length: ");
defmacro 22:4d895e732765 865 _client->println(contentLength);
defmacro 22:4d895e732765 866 }
defmacro 22:4d895e732765 867 _client->println();
defmacro 22:4d895e732765 868 }
defmacro 22:4d895e732765 869
defmacro 22:4d895e732765 870 int M2XStreamClient::waitForString(const char* str) {
defmacro 22:4d895e732765 871 int currentIndex = 0;
defmacro 22:4d895e732765 872 if (str[currentIndex] == '\0') return E_OK;
defmacro 22:4d895e732765 873
defmacro 22:4d895e732765 874 while (true) {
defmacro 22:4d895e732765 875 while (_client->available()) {
defmacro 22:4d895e732765 876 char c = _client->read();
defmacro 22:4d895e732765 877 DBG("%c", c);
defmacro 22:4d895e732765 878
defmacro 22:4d895e732765 879 int cmp;
defmacro 22:4d895e732765 880 if (_case_insensitive) {
defmacro 22:4d895e732765 881 cmp = tolower(c) - tolower(str[currentIndex]);
defmacro 22:4d895e732765 882 } else {
defmacro 22:4d895e732765 883 cmp = c - str[currentIndex];
defmacro 22:4d895e732765 884 }
defmacro 22:4d895e732765 885
defmacro 22:4d895e732765 886 if ((str[currentIndex] == '*') || (cmp == 0)) {
defmacro 22:4d895e732765 887 currentIndex++;
defmacro 22:4d895e732765 888 if (str[currentIndex] == '\0') {
defmacro 22:4d895e732765 889 return E_OK;
defmacro 22:4d895e732765 890 }
defmacro 22:4d895e732765 891 } else {
defmacro 22:4d895e732765 892 // start from the beginning
defmacro 22:4d895e732765 893 currentIndex = 0;
defmacro 22:4d895e732765 894 }
defmacro 22:4d895e732765 895 }
defmacro 22:4d895e732765 896
defmacro 22:4d895e732765 897 if (!_client->connected()) {
defmacro 22:4d895e732765 898 DBGLN("%s", "ERROR: The client is disconnected from the server!");
defmacro 22:4d895e732765 899
defmacro 22:4d895e732765 900 close();
defmacro 22:4d895e732765 901 return E_DISCONNECTED;
defmacro 22:4d895e732765 902 }
defmacro 22:4d895e732765 903 if (_idlefunc!=NULL)
defmacro 22:4d895e732765 904 _idlefunc();
defmacro 22:4d895e732765 905 else
defmacro 22:4d895e732765 906 delay(200);
defmacro 22:4d895e732765 907 }
defmacro 22:4d895e732765 908 // never reached here
defmacro 22:4d895e732765 909 return E_NOTREACHABLE;
defmacro 22:4d895e732765 910 }
defmacro 22:4d895e732765 911
defmacro 22:4d895e732765 912 int M2XStreamClient::readStatusCode(bool closeClient) {
defmacro 22:4d895e732765 913 int responseCode = 0;
defmacro 22:4d895e732765 914 int ret = waitForString("HTTP/*.* ");
defmacro 22:4d895e732765 915 if (ret != E_OK) {
defmacro 22:4d895e732765 916 if (closeClient) close();
defmacro 22:4d895e732765 917 return ret;
defmacro 22:4d895e732765 918 }
defmacro 22:4d895e732765 919
defmacro 22:4d895e732765 920 // ret is not needed from here(since it must be E_OK), so we can use it
defmacro 22:4d895e732765 921 // as a regular variable now.
defmacro 22:4d895e732765 922 ret = 0;
defmacro 22:4d895e732765 923 while (true) {
defmacro 22:4d895e732765 924 while (_client->available()) {
defmacro 22:4d895e732765 925 char c = _client->read();
defmacro 22:4d895e732765 926 DBG("%c", c);
defmacro 22:4d895e732765 927
defmacro 22:4d895e732765 928 responseCode = responseCode * 10 + (c - '0');
defmacro 22:4d895e732765 929 ret++;
defmacro 22:4d895e732765 930 if (ret == 3) {
defmacro 22:4d895e732765 931 if (closeClient) close();
defmacro 22:4d895e732765 932 return responseCode;
defmacro 22:4d895e732765 933 }
defmacro 22:4d895e732765 934 }
defmacro 22:4d895e732765 935
defmacro 22:4d895e732765 936 if (!_client->connected()) {
defmacro 22:4d895e732765 937 DBGLN("%s", "ERROR: The client is disconnected from the server!");
defmacro 22:4d895e732765 938
defmacro 22:4d895e732765 939 if (closeClient) close();
defmacro 22:4d895e732765 940 return E_DISCONNECTED;
defmacro 22:4d895e732765 941 }
defmacro 22:4d895e732765 942 if (_idlefunc!=NULL)
defmacro 22:4d895e732765 943 _idlefunc();
defmacro 22:4d895e732765 944 else
defmacro 22:4d895e732765 945 delay(200);
defmacro 22:4d895e732765 946 }
defmacro 22:4d895e732765 947
defmacro 22:4d895e732765 948 // never reached here
defmacro 22:4d895e732765 949 return E_NOTREACHABLE;
defmacro 22:4d895e732765 950 }
defmacro 22:4d895e732765 951
defmacro 22:4d895e732765 952 int M2XStreamClient::readContentLength() {
defmacro 22:4d895e732765 953 int ret = waitForString("Content-Length: ");
defmacro 22:4d895e732765 954 if (ret != E_OK) {
defmacro 22:4d895e732765 955 return ret;
defmacro 22:4d895e732765 956 }
defmacro 22:4d895e732765 957
defmacro 22:4d895e732765 958 // From now on, ret is not needed, we can use it
defmacro 22:4d895e732765 959 // to keep the final result
defmacro 22:4d895e732765 960 ret = 0;
defmacro 22:4d895e732765 961 while (true) {
defmacro 22:4d895e732765 962 while (_client->available()) {
defmacro 22:4d895e732765 963 char c = _client->read();
defmacro 22:4d895e732765 964 DBG("%c", c);
defmacro 22:4d895e732765 965
defmacro 22:4d895e732765 966 if ((c == '\r') || (c == '\n')) {
defmacro 22:4d895e732765 967 return (ret == 0) ? (E_INVALID) : (ret);
defmacro 22:4d895e732765 968 } else {
defmacro 22:4d895e732765 969 ret = ret * 10 + (c - '0');
defmacro 22:4d895e732765 970 }
defmacro 22:4d895e732765 971 }
defmacro 22:4d895e732765 972
defmacro 22:4d895e732765 973 if (!_client->connected()) {
defmacro 22:4d895e732765 974 DBGLN("%s", "ERROR: The client is disconnected from the server!");
defmacro 22:4d895e732765 975
defmacro 22:4d895e732765 976 return E_DISCONNECTED;
defmacro 22:4d895e732765 977 }
defmacro 22:4d895e732765 978 if (_idlefunc!=NULL)
defmacro 22:4d895e732765 979 _idlefunc();
defmacro 22:4d895e732765 980 else
defmacro 22:4d895e732765 981 delay(200);
defmacro 22:4d895e732765 982 }
defmacro 22:4d895e732765 983
defmacro 22:4d895e732765 984 // never reached here
defmacro 22:4d895e732765 985 return E_NOTREACHABLE;
defmacro 22:4d895e732765 986 }
defmacro 22:4d895e732765 987
defmacro 22:4d895e732765 988 int M2XStreamClient::skipHttpHeader() {
defmacro 22:4d895e732765 989 return waitForString("\n\r\n");
defmacro 22:4d895e732765 990 }
defmacro 22:4d895e732765 991
defmacro 22:4d895e732765 992 void M2XStreamClient::close() {
defmacro 22:4d895e732765 993 // Eats up buffered data before closing
defmacro 22:4d895e732765 994 _client->flush();
defmacro 22:4d895e732765 995 _client->stop();
defmacro 22:4d895e732765 996 }
defmacro 22:4d895e732765 997
defmacro 22:4d895e732765 998 static inline int fill_iso8601_timestamp(int32_t seconds, int32_t milli,
defmacro 22:4d895e732765 999 char* buffer, int* length);
defmacro 22:4d895e732765 1000
defmacro 22:4d895e732765 1001 TimeService::TimeService(M2XStreamClient* client) : _client(client) {
defmacro 22:4d895e732765 1002 }
defmacro 22:4d895e732765 1003
defmacro 22:4d895e732765 1004 int TimeService::init() {
defmacro 22:4d895e732765 1005 _timer.start();
defmacro 22:4d895e732765 1006 return reset();
defmacro 22:4d895e732765 1007 }
defmacro 22:4d895e732765 1008
defmacro 22:4d895e732765 1009 int TimeService::reset() {
defmacro 22:4d895e732765 1010 int32_t ts;
defmacro 22:4d895e732765 1011 int status = _client->getTimestamp32(&ts);
defmacro 22:4d895e732765 1012
defmacro 22:4d895e732765 1013 if (m2x_status_is_success(status)) {
defmacro 22:4d895e732765 1014 _server_timestamp = ts;
defmacro 22:4d895e732765 1015 _local_last_milli = _timer.read_ms();
defmacro 22:4d895e732765 1016 }
defmacro 22:4d895e732765 1017
defmacro 22:4d895e732765 1018 return status;
defmacro 22:4d895e732765 1019 }
defmacro 22:4d895e732765 1020
defmacro 22:4d895e732765 1021 int TimeService::getTimestamp(char* buffer, int* length) {
defmacro 22:4d895e732765 1022 uint32_t now = _timer.read_ms();
defmacro 22:4d895e732765 1023 if (now < _local_last_milli) {
defmacro 22:4d895e732765 1024 // In case of a timestamp overflow(happens once every 50 days on
defmacro 22:4d895e732765 1025 // Arduino), we reset the server timestamp recorded.
defmacro 22:4d895e732765 1026 // NOTE: while on an Arduino this might be okay, the situation is worse
defmacro 22:4d895e732765 1027 // on mbed, see the notes in m2x-mbed.h for details
defmacro 22:4d895e732765 1028 int status = reset();
defmacro 22:4d895e732765 1029 if (!m2x_status_is_success(status)) { return status; }
defmacro 22:4d895e732765 1030 now = _timer.read_ms();
defmacro 22:4d895e732765 1031 }
defmacro 22:4d895e732765 1032 if (now < _local_last_milli) {
defmacro 22:4d895e732765 1033 // We have already reseted the timestamp, so this cannot happen
defmacro 22:4d895e732765 1034 // (an HTTP request can take longer than 50 days to finished? You
defmacro 22:4d895e732765 1035 // must be kidding here). Something else must be wrong here
defmacro 22:4d895e732765 1036 return E_TIMESTAMP_ERROR;
defmacro 22:4d895e732765 1037 }
defmacro 22:4d895e732765 1038 uint32_t diff = now - _local_last_milli;
defmacro 22:4d895e732765 1039 _local_last_milli = now;
defmacro 22:4d895e732765 1040 _server_timestamp += (int32_t) (diff / 1000); // Milliseconds to seconds
defmacro 22:4d895e732765 1041 return fill_iso8601_timestamp(_server_timestamp, (int32_t) (diff % 1000),
defmacro 22:4d895e732765 1042 buffer, length);
defmacro 22:4d895e732765 1043 }
defmacro 22:4d895e732765 1044
defmacro 22:4d895e732765 1045 #define SIZE_ISO_8601 25
defmacro 22:4d895e732765 1046 static inline bool is_leap_year(int16_t y) {
defmacro 22:4d895e732765 1047 return ((1970 + y) > 0) &&
defmacro 22:4d895e732765 1048 !((1970 + y) % 4) &&
defmacro 22:4d895e732765 1049 (((1970 + y) % 100) || !((1970 + y) % 400));
defmacro 22:4d895e732765 1050 }
defmacro 22:4d895e732765 1051 static inline int32_t days_in_year(int16_t y) {
defmacro 22:4d895e732765 1052 return is_leap_year(y) ? 366 : 365;
defmacro 22:4d895e732765 1053 }
defmacro 22:4d895e732765 1054 static const uint8_t MONTH_DAYS[]={31,28,31,30,31,30,31,31,30,31,30,31};
defmacro 22:4d895e732765 1055
defmacro 22:4d895e732765 1056 static inline int fill_iso8601_timestamp(int32_t timestamp, int32_t milli,
defmacro 22:4d895e732765 1057 char* buffer, int* length) {
defmacro 22:4d895e732765 1058 int16_t year;
defmacro 22:4d895e732765 1059 int8_t month, month_length;
defmacro 22:4d895e732765 1060 int32_t day;
defmacro 22:4d895e732765 1061 int8_t hour, minute, second;
defmacro 22:4d895e732765 1062
defmacro 22:4d895e732765 1063 if (*length < SIZE_ISO_8601) {
defmacro 22:4d895e732765 1064 *length = SIZE_ISO_8601;
defmacro 22:4d895e732765 1065 return E_BUFFER_TOO_SMALL;
defmacro 22:4d895e732765 1066 }
defmacro 22:4d895e732765 1067
defmacro 22:4d895e732765 1068 second = timestamp % 60;
defmacro 22:4d895e732765 1069 timestamp /= 60; // now it is minutes
defmacro 22:4d895e732765 1070
defmacro 22:4d895e732765 1071 minute = timestamp % 60;
defmacro 22:4d895e732765 1072 timestamp /= 60; // now it is hours
defmacro 22:4d895e732765 1073
defmacro 22:4d895e732765 1074 hour = timestamp % 24;
defmacro 22:4d895e732765 1075 timestamp /= 24; // now it is days
defmacro 22:4d895e732765 1076
defmacro 22:4d895e732765 1077 year = 0;
defmacro 22:4d895e732765 1078 day = 0;
defmacro 22:4d895e732765 1079 while ((day += days_in_year(year)) <= timestamp) {
defmacro 22:4d895e732765 1080 year++;
defmacro 22:4d895e732765 1081 }
defmacro 22:4d895e732765 1082 day -= days_in_year(year);
defmacro 22:4d895e732765 1083 timestamp -= day; // now it is days in this year, starting at 0
defmacro 22:4d895e732765 1084
defmacro 22:4d895e732765 1085 day = 0;
defmacro 22:4d895e732765 1086 month_length = 0;
defmacro 22:4d895e732765 1087 for (month = 0; month < 12; month++) {
defmacro 22:4d895e732765 1088 if (month == 1) {
defmacro 22:4d895e732765 1089 // February
defmacro 22:4d895e732765 1090 month_length = is_leap_year(year) ? 29 : 28;
defmacro 22:4d895e732765 1091 } else {
defmacro 22:4d895e732765 1092 month_length = MONTH_DAYS[month];
defmacro 22:4d895e732765 1093 }
defmacro 22:4d895e732765 1094
defmacro 22:4d895e732765 1095 if (timestamp >= month_length) {
defmacro 22:4d895e732765 1096 timestamp -= month_length;
defmacro 22:4d895e732765 1097 } else {
defmacro 22:4d895e732765 1098 break;
defmacro 22:4d895e732765 1099 }
defmacro 22:4d895e732765 1100 }
defmacro 22:4d895e732765 1101 year = 1970 + year;
defmacro 22:4d895e732765 1102 month++; // offset by 1
defmacro 22:4d895e732765 1103 day = timestamp + 1;
defmacro 22:4d895e732765 1104
defmacro 22:4d895e732765 1105 int i = 0, j = 0;
defmacro 22:4d895e732765 1106
defmacro 22:4d895e732765 1107 // NOTE: It seems the snprintf implementation in Arduino has bugs,
defmacro 22:4d895e732765 1108 // we have to manually piece the string together here.
defmacro 22:4d895e732765 1109 #define INT_TO_STR(v_, width_) \
defmacro 22:4d895e732765 1110 for (j = 0; j < (width_); j++) { \
defmacro 22:4d895e732765 1111 buffer[i + (width_) - 1 - j] = '0' + ((v_) % 10); \
defmacro 22:4d895e732765 1112 (v_) /= 10; \
defmacro 22:4d895e732765 1113 } \
defmacro 22:4d895e732765 1114 i += (width_)
defmacro 22:4d895e732765 1115
defmacro 22:4d895e732765 1116 INT_TO_STR(year, 4);
defmacro 22:4d895e732765 1117 buffer[i++] = '-';
defmacro 22:4d895e732765 1118 INT_TO_STR(month, 2);
defmacro 22:4d895e732765 1119 buffer[i++] = '-';
defmacro 22:4d895e732765 1120 INT_TO_STR(day, 2);
defmacro 22:4d895e732765 1121 buffer[i++] = 'T';
defmacro 22:4d895e732765 1122 INT_TO_STR(hour, 2);
defmacro 22:4d895e732765 1123 buffer[i++] = ':';
defmacro 22:4d895e732765 1124 INT_TO_STR(minute, 2);
defmacro 22:4d895e732765 1125 buffer[i++] = ':';
defmacro 22:4d895e732765 1126 INT_TO_STR(second, 2);
defmacro 22:4d895e732765 1127 buffer[i++] = '.';
defmacro 22:4d895e732765 1128 INT_TO_STR(milli, 3);
defmacro 22:4d895e732765 1129 buffer[i++] = 'Z';
defmacro 22:4d895e732765 1130 buffer[i++] = '\0';
defmacro 22:4d895e732765 1131
defmacro 22:4d895e732765 1132 #undef INT_TO_STR
defmacro 22:4d895e732765 1133
defmacro 22:4d895e732765 1134 *length = i;
defmacro 22:4d895e732765 1135 return E_OK;
defmacro 22:4d895e732765 1136 }
defmacro 22:4d895e732765 1137
defmacro 22:4d895e732765 1138 /* Reader functions */
defmacro 22:4d895e732765 1139 #ifdef M2X_ENABLE_READER
defmacro 22:4d895e732765 1140
defmacro 22:4d895e732765 1141 // Data structures and functions used to parse stream values
defmacro 22:4d895e732765 1142
defmacro 22:4d895e732765 1143 #define STREAM_BUF_LEN 32
defmacro 22:4d895e732765 1144
defmacro 22:4d895e732765 1145 typedef struct {
defmacro 22:4d895e732765 1146 uint8_t state;
defmacro 22:4d895e732765 1147 char at_str[STREAM_BUF_LEN + 1];
defmacro 22:4d895e732765 1148 char value_str[STREAM_BUF_LEN + 1];
defmacro 22:4d895e732765 1149 int index;
defmacro 22:4d895e732765 1150
defmacro 22:4d895e732765 1151 stream_value_read_callback callback;
defmacro 22:4d895e732765 1152 void* context;
defmacro 22:4d895e732765 1153 } stream_parsing_context_state;
defmacro 22:4d895e732765 1154
defmacro 22:4d895e732765 1155 #define WAITING_AT 0x1
defmacro 22:4d895e732765 1156 #define GOT_AT 0x2
defmacro 22:4d895e732765 1157 #define WAITING_VALUE 0x4
defmacro 22:4d895e732765 1158 #define GOT_VALUE 0x8
defmacro 22:4d895e732765 1159
defmacro 22:4d895e732765 1160 #define GOT_STREAM (GOT_AT | GOT_VALUE)
defmacro 22:4d895e732765 1161 #define TEST_GOT_STREAM(state_) (((state_) & GOT_STREAM) == GOT_STREAM)
defmacro 22:4d895e732765 1162
defmacro 22:4d895e732765 1163 #define TEST_IS_AT(state_) (((state_) & (WAITING_AT | GOT_AT)) == WAITING_AT)
defmacro 22:4d895e732765 1164 #define TEST_IS_VALUE(state_) (((state_) & (WAITING_VALUE | GOT_VALUE)) == \
defmacro 22:4d895e732765 1165 WAITING_VALUE)
defmacro 22:4d895e732765 1166
defmacro 22:4d895e732765 1167 static void on_stream_key_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1168 jsonlite_token* token)
defmacro 22:4d895e732765 1169 {
defmacro 22:4d895e732765 1170 stream_parsing_context_state* state =
defmacro 22:4d895e732765 1171 (stream_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1172 if (strncmp((const char*) token->start, "timestamp", 9) == 0) {
defmacro 22:4d895e732765 1173 state->state |= WAITING_AT;
defmacro 22:4d895e732765 1174 } else if ((strncmp((const char*) token->start, "value", 5) == 0) &&
defmacro 22:4d895e732765 1175 (token->start[5] != 's')) { // get rid of "values"
defmacro 22:4d895e732765 1176 state->state |= WAITING_VALUE;
defmacro 22:4d895e732765 1177 }
defmacro 22:4d895e732765 1178 }
defmacro 22:4d895e732765 1179
defmacro 22:4d895e732765 1180 static void on_stream_value_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1181 jsonlite_token* token,
defmacro 22:4d895e732765 1182 int type)
defmacro 22:4d895e732765 1183 {
defmacro 22:4d895e732765 1184 stream_parsing_context_state* state =
defmacro 22:4d895e732765 1185 (stream_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1186
defmacro 22:4d895e732765 1187 if (TEST_IS_AT(state->state)) {
defmacro 22:4d895e732765 1188 strncpy(state->at_str, (const char*) token->start,
defmacro 22:4d895e732765 1189 MIN(token->end - token->start, STREAM_BUF_LEN));
defmacro 22:4d895e732765 1190 state->at_str[MIN(token->end - token->start, STREAM_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1191 state->state |= GOT_AT;
defmacro 22:4d895e732765 1192 } else if (TEST_IS_VALUE(state->state)) {
defmacro 22:4d895e732765 1193 strncpy(state->value_str, (const char*) token->start,
defmacro 22:4d895e732765 1194 MIN(token->end - token->start, STREAM_BUF_LEN));
defmacro 22:4d895e732765 1195 state->value_str[MIN(token->end - token->start, STREAM_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1196 state->state |= GOT_VALUE;
defmacro 22:4d895e732765 1197 }
defmacro 22:4d895e732765 1198
defmacro 22:4d895e732765 1199 if (TEST_GOT_STREAM(state->state)) {
defmacro 22:4d895e732765 1200 state->callback(state->at_str, state->value_str,
defmacro 22:4d895e732765 1201 state->index++, state->context, type);
defmacro 22:4d895e732765 1202 state->state = 0;
defmacro 22:4d895e732765 1203 }
defmacro 22:4d895e732765 1204 }
defmacro 22:4d895e732765 1205
defmacro 22:4d895e732765 1206 static void on_stream_string_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1207 jsonlite_token* token)
defmacro 22:4d895e732765 1208 {
defmacro 22:4d895e732765 1209 on_stream_value_found(context, token, 1);
defmacro 22:4d895e732765 1210 }
defmacro 22:4d895e732765 1211
defmacro 22:4d895e732765 1212 static void on_stream_number_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1213 jsonlite_token* token)
defmacro 22:4d895e732765 1214 {
defmacro 22:4d895e732765 1215 on_stream_value_found(context, token, 2);
defmacro 22:4d895e732765 1216 }
defmacro 22:4d895e732765 1217
defmacro 22:4d895e732765 1218 // Data structures and functions used to parse locations
defmacro 22:4d895e732765 1219
defmacro 22:4d895e732765 1220 #define LOCATION_BUF_LEN 20
defmacro 22:4d895e732765 1221
defmacro 22:4d895e732765 1222 typedef struct {
defmacro 22:4d895e732765 1223 uint16_t state;
defmacro 22:4d895e732765 1224 char name_str[LOCATION_BUF_LEN + 1];
defmacro 22:4d895e732765 1225 double latitude;
defmacro 22:4d895e732765 1226 double longitude;
defmacro 22:4d895e732765 1227 double elevation;
defmacro 22:4d895e732765 1228 char timestamp_str[LOCATION_BUF_LEN + 1];
defmacro 22:4d895e732765 1229 int index;
defmacro 22:4d895e732765 1230
defmacro 22:4d895e732765 1231 location_read_callback callback;
defmacro 22:4d895e732765 1232 void* context;
defmacro 22:4d895e732765 1233 } location_parsing_context_state;
defmacro 22:4d895e732765 1234
defmacro 22:4d895e732765 1235 #define WAITING_NAME 0x1
defmacro 22:4d895e732765 1236 #define WAITING_LATITUDE 0x2
defmacro 22:4d895e732765 1237 #define WAITING_LONGITUDE 0x4
defmacro 22:4d895e732765 1238 #define WAITING_ELEVATION 0x8
defmacro 22:4d895e732765 1239 #define WAITING_TIMESTAMP 0x10
defmacro 22:4d895e732765 1240
defmacro 22:4d895e732765 1241 #define GOT_NAME 0x20
defmacro 22:4d895e732765 1242 #define GOT_LATITUDE 0x40
defmacro 22:4d895e732765 1243 #define GOT_LONGITUDE 0x80
defmacro 22:4d895e732765 1244 #define GOT_ELEVATION 0x100
defmacro 22:4d895e732765 1245 #define GOT_TIMESTAMP 0x200
defmacro 22:4d895e732765 1246
defmacro 22:4d895e732765 1247 #define GOT_LOCATION (GOT_NAME | GOT_LATITUDE | GOT_LONGITUDE | GOT_ELEVATION | GOT_TIMESTAMP)
defmacro 22:4d895e732765 1248 #define TEST_GOT_LOCATION(state_) (((state_) & GOT_LOCATION) == GOT_LOCATION)
defmacro 22:4d895e732765 1249
defmacro 22:4d895e732765 1250 #define TEST_IS_NAME(state_) (((state_) & (WAITING_NAME | GOT_NAME)) == WAITING_NAME)
defmacro 22:4d895e732765 1251 #define TEST_IS_LATITUDE(state_) (((state_) & (WAITING_LATITUDE | GOT_LATITUDE)) \
defmacro 22:4d895e732765 1252 == WAITING_LATITUDE)
defmacro 22:4d895e732765 1253 #define TEST_IS_LONGITUDE(state_) (((state_) & (WAITING_LONGITUDE | GOT_LONGITUDE)) \
defmacro 22:4d895e732765 1254 == WAITING_LONGITUDE)
defmacro 22:4d895e732765 1255 #define TEST_IS_ELEVATION(state_) (((state_) & (WAITING_ELEVATION | GOT_ELEVATION)) \
defmacro 22:4d895e732765 1256 == WAITING_ELEVATION)
defmacro 22:4d895e732765 1257 #define TEST_IS_TIMESTAMP(state_) (((state_) & (WAITING_TIMESTAMP | GOT_TIMESTAMP)) \
defmacro 22:4d895e732765 1258 == WAITING_TIMESTAMP)
defmacro 22:4d895e732765 1259
defmacro 22:4d895e732765 1260 static void on_location_key_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1261 jsonlite_token* token) {
defmacro 22:4d895e732765 1262 location_parsing_context_state* state =
defmacro 22:4d895e732765 1263 (location_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1264 if (strncmp((const char*) token->start, "waypoints", 9) == 0) {
defmacro 22:4d895e732765 1265 // only parses those locations in waypoints, skip the outer one
defmacro 22:4d895e732765 1266 state->state = 0;
defmacro 22:4d895e732765 1267 } else if (strncmp((const char*) token->start, "name", 4) == 0) {
defmacro 22:4d895e732765 1268 state->state |= WAITING_NAME;
defmacro 22:4d895e732765 1269 } else if (strncmp((const char*) token->start, "latitude", 8) == 0) {
defmacro 22:4d895e732765 1270 state->state |= WAITING_LATITUDE;
defmacro 22:4d895e732765 1271 } else if (strncmp((const char*) token->start, "longitude", 9) == 0) {
defmacro 22:4d895e732765 1272 state->state |= WAITING_LONGITUDE;
defmacro 22:4d895e732765 1273 } else if (strncmp((const char*) token->start, "elevation", 9) == 0) {
defmacro 22:4d895e732765 1274 state->state |= WAITING_ELEVATION;
defmacro 22:4d895e732765 1275 } else if (strncmp((const char*) token->start, "timestamp", 9) == 0) {
defmacro 22:4d895e732765 1276 state->state |= WAITING_TIMESTAMP;
defmacro 22:4d895e732765 1277 }
defmacro 22:4d895e732765 1278 }
defmacro 22:4d895e732765 1279
defmacro 22:4d895e732765 1280 static void on_location_string_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1281 jsonlite_token* token) {
defmacro 22:4d895e732765 1282 location_parsing_context_state* state =
defmacro 22:4d895e732765 1283 (location_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1284
defmacro 22:4d895e732765 1285 if (TEST_IS_NAME(state->state)) {
defmacro 22:4d895e732765 1286 strncpy(state->name_str, (const char*) token->start,
defmacro 22:4d895e732765 1287 MIN(token->end - token->start, LOCATION_BUF_LEN));
defmacro 22:4d895e732765 1288 state->name_str[MIN(token->end - token->start, LOCATION_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1289 state->state |= GOT_NAME;
defmacro 22:4d895e732765 1290 } else if (TEST_IS_LATITUDE(state->state)) {
defmacro 22:4d895e732765 1291 state->latitude = atof((const char*) token->start);
defmacro 22:4d895e732765 1292 state->state |= GOT_LATITUDE;
defmacro 22:4d895e732765 1293 } else if (TEST_IS_LONGITUDE(state->state)) {
defmacro 22:4d895e732765 1294 state->longitude = atof((const char*) token->start);
defmacro 22:4d895e732765 1295 state->state |= GOT_LONGITUDE;
defmacro 22:4d895e732765 1296 } else if (TEST_IS_ELEVATION(state->state)) {
defmacro 22:4d895e732765 1297 state->elevation = atof((const char*) token->start);
defmacro 22:4d895e732765 1298 state->state |= GOT_ELEVATION;
defmacro 22:4d895e732765 1299 } else if (TEST_IS_TIMESTAMP(state->state)) {
defmacro 22:4d895e732765 1300 strncpy(state->timestamp_str, (const char*) token->start,
defmacro 22:4d895e732765 1301 MIN(token->end - token->start, LOCATION_BUF_LEN));
defmacro 22:4d895e732765 1302 state->timestamp_str[MIN(token->end - token->start, LOCATION_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1303 state->state |= GOT_TIMESTAMP;
defmacro 22:4d895e732765 1304 }
defmacro 22:4d895e732765 1305
defmacro 22:4d895e732765 1306 if (TEST_GOT_LOCATION(state->state)) {
defmacro 22:4d895e732765 1307 state->callback(state->name_str, state->latitude, state->longitude,
defmacro 22:4d895e732765 1308 state->elevation, state->timestamp_str, state->index++,
defmacro 22:4d895e732765 1309 state->context);
defmacro 22:4d895e732765 1310 state->state = 0;
defmacro 22:4d895e732765 1311 }
defmacro 22:4d895e732765 1312 }
defmacro 22:4d895e732765 1313
defmacro 22:4d895e732765 1314 #ifndef M2X_COMMAND_BUF_LEN
defmacro 22:4d895e732765 1315 #define M2X_COMMAND_BUF_LEN 40
defmacro 22:4d895e732765 1316 #endif /* M2X_COMMAND_BUF_LEN */
defmacro 22:4d895e732765 1317
defmacro 22:4d895e732765 1318 typedef struct {
defmacro 22:4d895e732765 1319 uint8_t state;
defmacro 22:4d895e732765 1320 char id_str[M2X_COMMAND_BUF_LEN + 1];
defmacro 22:4d895e732765 1321 char name_str[M2X_COMMAND_BUF_LEN + 1];
defmacro 22:4d895e732765 1322 int index;
defmacro 22:4d895e732765 1323
defmacro 22:4d895e732765 1324 m2x_command_read_callback callback;
defmacro 22:4d895e732765 1325 void* context;
defmacro 22:4d895e732765 1326 } m2x_command_parsing_context_state;
defmacro 22:4d895e732765 1327
defmacro 22:4d895e732765 1328 #define M2X_COMMAND_WAITING_ID 0x1
defmacro 22:4d895e732765 1329 #define M2X_COMMAND_GOT_ID 0x2
defmacro 22:4d895e732765 1330 #define M2X_COMMAND_WAITING_NAME 0x4
defmacro 22:4d895e732765 1331 #define M2X_COMMAND_GOT_NAME 0x8
defmacro 22:4d895e732765 1332
defmacro 22:4d895e732765 1333 #define M2X_COMMAND_GOT_COMMAND (M2X_COMMAND_GOT_ID | M2X_COMMAND_GOT_NAME)
defmacro 22:4d895e732765 1334 #define M2X_COMMAND_TEST_GOT_COMMAND(state_) \
defmacro 22:4d895e732765 1335 (((state_) & M2X_COMMAND_GOT_COMMAND) == M2X_COMMAND_GOT_COMMAND)
defmacro 22:4d895e732765 1336
defmacro 22:4d895e732765 1337 #define M2X_COMMAND_TEST_IS_ID(state_) \
defmacro 22:4d895e732765 1338 (((state_) & (M2X_COMMAND_WAITING_ID | M2X_COMMAND_GOT_ID)) == \
defmacro 22:4d895e732765 1339 M2X_COMMAND_WAITING_ID)
defmacro 22:4d895e732765 1340 #define M2X_COMMAND_TEST_IS_NAME(state_) \
defmacro 22:4d895e732765 1341 (((state_) & (M2X_COMMAND_WAITING_NAME | M2X_COMMAND_GOT_NAME)) == \
defmacro 22:4d895e732765 1342 M2X_COMMAND_WAITING_NAME)
defmacro 22:4d895e732765 1343
defmacro 22:4d895e732765 1344 static void m2x_on_command_key_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1345 jsonlite_token* token)
defmacro 22:4d895e732765 1346 {
defmacro 22:4d895e732765 1347 m2x_command_parsing_context_state* state =
defmacro 22:4d895e732765 1348 (m2x_command_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1349 if (strncmp((const char*) token->start, "id", 2) == 0) {
defmacro 22:4d895e732765 1350 state->state |= M2X_COMMAND_WAITING_ID;
defmacro 22:4d895e732765 1351 } else if (strncmp((const char*) token->start, "name", 4) == 0) {
defmacro 22:4d895e732765 1352 state->state |= M2X_COMMAND_WAITING_NAME;
defmacro 22:4d895e732765 1353 }
defmacro 22:4d895e732765 1354 }
defmacro 22:4d895e732765 1355
defmacro 22:4d895e732765 1356 static void m2x_on_command_value_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1357 jsonlite_token* token)
defmacro 22:4d895e732765 1358 {
defmacro 22:4d895e732765 1359 m2x_command_parsing_context_state* state =
defmacro 22:4d895e732765 1360 (m2x_command_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1361 if (M2X_COMMAND_TEST_IS_ID(state->state)) {
defmacro 22:4d895e732765 1362 strncpy(state->id_str, (const char*) token->start,
defmacro 22:4d895e732765 1363 MIN(token->end - token->start, M2X_COMMAND_BUF_LEN));
defmacro 22:4d895e732765 1364 state->id_str[MIN(token->end - token->start, M2X_COMMAND_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1365 state->state |= M2X_COMMAND_GOT_ID;
defmacro 22:4d895e732765 1366 } else if (M2X_COMMAND_TEST_IS_NAME(state->state)) {
defmacro 22:4d895e732765 1367 strncpy(state->name_str, (const char*) token->start,
defmacro 22:4d895e732765 1368 MIN(token->end - token->start, M2X_COMMAND_BUF_LEN));
defmacro 22:4d895e732765 1369 state->name_str[MIN(token->end - token->start, M2X_COMMAND_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1370 state->state |= M2X_COMMAND_GOT_NAME;
defmacro 22:4d895e732765 1371 }
defmacro 22:4d895e732765 1372
defmacro 22:4d895e732765 1373 if (M2X_COMMAND_TEST_GOT_COMMAND(state->state)) {
defmacro 22:4d895e732765 1374 state->callback(state->id_str, state->name_str,
defmacro 22:4d895e732765 1375 state->index++, state->context);
defmacro 22:4d895e732765 1376 state->state = 0;
defmacro 22:4d895e732765 1377 }
defmacro 22:4d895e732765 1378 }
defmacro 22:4d895e732765 1379
defmacro 22:4d895e732765 1380 int M2XStreamClient::listStreamValues(const char* deviceId, const char* streamName,
defmacro 22:4d895e732765 1381 stream_value_read_callback callback, void* context,
defmacro 22:4d895e732765 1382 const char* query) {
defmacro 22:4d895e732765 1383 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 1384 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 1385 _client->print("GET ");
defmacro 22:4d895e732765 1386 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 1387 _client->print("/v2/devices/");
defmacro 22:4d895e732765 1388 _client->print(deviceId);
defmacro 22:4d895e732765 1389 _client->print("/streams/");
defmacro 22:4d895e732765 1390 print_encoded_string(_client, streamName);
defmacro 22:4d895e732765 1391 _client->print("/values");
defmacro 22:4d895e732765 1392
defmacro 22:4d895e732765 1393 if (query) {
defmacro 22:4d895e732765 1394 if (query[0] != '?') {
defmacro 22:4d895e732765 1395 _client->print('?');
defmacro 22:4d895e732765 1396 }
defmacro 22:4d895e732765 1397 _client->print(query);
defmacro 22:4d895e732765 1398 }
defmacro 22:4d895e732765 1399
JMF 25:67079c11d1ea 1400 _client->println(" HTTP/1.1");
defmacro 22:4d895e732765 1401 writeHttpHeader(-1);
defmacro 22:4d895e732765 1402 } else {
defmacro 22:4d895e732765 1403 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 1404 return E_NOCONNECTION;
defmacro 22:4d895e732765 1405 }
defmacro 22:4d895e732765 1406 int status = readStatusCode(false);
defmacro 22:4d895e732765 1407 if (status == 200) {
defmacro 22:4d895e732765 1408 readStreamValue(callback, context);
defmacro 22:4d895e732765 1409 }
defmacro 22:4d895e732765 1410
defmacro 22:4d895e732765 1411 close();
defmacro 22:4d895e732765 1412 return status;
defmacro 22:4d895e732765 1413 }
defmacro 22:4d895e732765 1414
defmacro 22:4d895e732765 1415 int M2XStreamClient::readLocation(const char* deviceId,
defmacro 22:4d895e732765 1416 location_read_callback callback,
defmacro 22:4d895e732765 1417 void* context) {
defmacro 22:4d895e732765 1418 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 1419 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 1420 _client->print("GET ");
defmacro 22:4d895e732765 1421 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 1422 _client->print("/v2/devices/");
defmacro 22:4d895e732765 1423 _client->print(deviceId);
JMF 25:67079c11d1ea 1424 _client->println("/location HTTP/1.1");
defmacro 22:4d895e732765 1425
defmacro 22:4d895e732765 1426 writeHttpHeader(-1);
defmacro 22:4d895e732765 1427 } else {
defmacro 22:4d895e732765 1428 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 1429 return E_NOCONNECTION;
defmacro 22:4d895e732765 1430 }
defmacro 22:4d895e732765 1431 int status = readStatusCode(false);
defmacro 22:4d895e732765 1432 if (status == 200) {
defmacro 22:4d895e732765 1433 readLocation(callback, context);
defmacro 22:4d895e732765 1434 }
defmacro 22:4d895e732765 1435
defmacro 22:4d895e732765 1436 close();
defmacro 22:4d895e732765 1437 return status;
defmacro 22:4d895e732765 1438 }
defmacro 22:4d895e732765 1439
JMF 23:f32837239193 1440 int M2XStreamClient::deleteLocations(const char* deviceId,
JMF 23:f32837239193 1441 const char* from, const char* end) {
JMF 23:f32837239193 1442 if (_client->connect(_host, _port)) {
JMF 23:f32837239193 1443 DBGLN("%s", "Connected to M2X server!");
JMF 23:f32837239193 1444 int length = write_delete_values(&_null_print, from, end);
JMF 23:f32837239193 1445 writeDeleteLocationHeader(deviceId, length);
JMF 23:f32837239193 1446 write_delete_values(_client, from, end);
JMF 23:f32837239193 1447 } else {
JMF 23:f32837239193 1448 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
JMF 23:f32837239193 1449 return E_NOCONNECTION;
JMF 23:f32837239193 1450 }
JMF 23:f32837239193 1451
JMF 23:f32837239193 1452 return readStatusCode(true);
JMF 23:f32837239193 1453 }
JMF 23:f32837239193 1454
defmacro 22:4d895e732765 1455 int M2XStreamClient::listCommands(const char* deviceId,
defmacro 22:4d895e732765 1456 m2x_command_read_callback callback,
defmacro 22:4d895e732765 1457 void* context,
defmacro 22:4d895e732765 1458 const char* query) {
defmacro 22:4d895e732765 1459 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 1460 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 1461 _client->print("GET ");
defmacro 22:4d895e732765 1462 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 1463 _client->print("/v2/devices/");
defmacro 22:4d895e732765 1464 _client->print(deviceId);
defmacro 22:4d895e732765 1465 _client->print("/commands");
defmacro 22:4d895e732765 1466
defmacro 22:4d895e732765 1467 if (query) {
defmacro 22:4d895e732765 1468 if (query[0] != '?') {
defmacro 22:4d895e732765 1469 _client->print('?');
defmacro 22:4d895e732765 1470 }
defmacro 22:4d895e732765 1471 _client->print(query);
defmacro 22:4d895e732765 1472 }
defmacro 22:4d895e732765 1473
JMF 25:67079c11d1ea 1474 _client->println(" HTTP/1.1");
defmacro 22:4d895e732765 1475 writeHttpHeader(-1);
defmacro 22:4d895e732765 1476 } else {
defmacro 22:4d895e732765 1477 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 1478 return E_NOCONNECTION;
defmacro 22:4d895e732765 1479 }
defmacro 22:4d895e732765 1480 int status = readStatusCode(false);
defmacro 22:4d895e732765 1481 if (status == 200) {
defmacro 22:4d895e732765 1482 readCommand(callback, context);
defmacro 22:4d895e732765 1483 }
defmacro 22:4d895e732765 1484
defmacro 22:4d895e732765 1485 close();
defmacro 22:4d895e732765 1486 return status;
defmacro 22:4d895e732765 1487 }
defmacro 22:4d895e732765 1488
defmacro 22:4d895e732765 1489
defmacro 22:4d895e732765 1490 int M2XStreamClient::readStreamValue(stream_value_read_callback callback,
defmacro 22:4d895e732765 1491 void* context) {
defmacro 22:4d895e732765 1492 const int BUF_LEN = 64;
defmacro 22:4d895e732765 1493 char buf[BUF_LEN];
defmacro 22:4d895e732765 1494
defmacro 22:4d895e732765 1495 int length = readContentLength();
defmacro 22:4d895e732765 1496 if (length < 0) {
defmacro 22:4d895e732765 1497 close();
defmacro 22:4d895e732765 1498 return length;
defmacro 22:4d895e732765 1499 }
defmacro 22:4d895e732765 1500
defmacro 22:4d895e732765 1501 int index = skipHttpHeader();
defmacro 22:4d895e732765 1502 if (index != E_OK) {
defmacro 22:4d895e732765 1503 close();
defmacro 22:4d895e732765 1504 return index;
defmacro 22:4d895e732765 1505 }
defmacro 22:4d895e732765 1506 index = 0;
defmacro 22:4d895e732765 1507
defmacro 22:4d895e732765 1508 stream_parsing_context_state state;
defmacro 22:4d895e732765 1509 state.state = state.index = 0;
defmacro 22:4d895e732765 1510 state.callback = callback;
defmacro 22:4d895e732765 1511 state.context = context;
defmacro 22:4d895e732765 1512
defmacro 22:4d895e732765 1513 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
defmacro 22:4d895e732765 1514 cbs.key_found = on_stream_key_found;
defmacro 22:4d895e732765 1515 cbs.number_found = on_stream_number_found;
defmacro 22:4d895e732765 1516 cbs.string_found = on_stream_string_found;
defmacro 22:4d895e732765 1517 cbs.context.client_state = &state;
defmacro 22:4d895e732765 1518
defmacro 22:4d895e732765 1519 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
defmacro 22:4d895e732765 1520 jsonlite_parser_set_callback(p, &cbs);
defmacro 22:4d895e732765 1521
defmacro 22:4d895e732765 1522 jsonlite_result result = jsonlite_result_unknown;
defmacro 22:4d895e732765 1523 while (index < length) {
defmacro 22:4d895e732765 1524 int i = 0;
defmacro 22:4d895e732765 1525
defmacro 22:4d895e732765 1526 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 1527 while ((i < BUF_LEN) && _client->available()) {
defmacro 22:4d895e732765 1528 buf[i++] = _client->read();
defmacro 22:4d895e732765 1529 DBG("%c", buf[i - 1]);
defmacro 22:4d895e732765 1530 }
defmacro 22:4d895e732765 1531 DBGLNEND;
defmacro 22:4d895e732765 1532
defmacro 22:4d895e732765 1533 if ((!_client->connected()) &&
defmacro 22:4d895e732765 1534 (!_client->available()) &&
defmacro 22:4d895e732765 1535 ((index + i) < length)) {
defmacro 22:4d895e732765 1536 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1537 close();
defmacro 22:4d895e732765 1538 return E_NOCONNECTION;
defmacro 22:4d895e732765 1539 }
defmacro 22:4d895e732765 1540
defmacro 22:4d895e732765 1541 result = jsonlite_parser_tokenize(p, buf, i);
defmacro 22:4d895e732765 1542 if ((result != jsonlite_result_ok) &&
defmacro 22:4d895e732765 1543 (result != jsonlite_result_end_of_stream)) {
defmacro 22:4d895e732765 1544 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1545 close();
defmacro 22:4d895e732765 1546 return E_JSON_INVALID;
defmacro 22:4d895e732765 1547 }
defmacro 22:4d895e732765 1548
defmacro 22:4d895e732765 1549 index += i;
defmacro 22:4d895e732765 1550 }
defmacro 22:4d895e732765 1551
defmacro 22:4d895e732765 1552 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1553 close();
defmacro 22:4d895e732765 1554 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
defmacro 22:4d895e732765 1555 }
defmacro 22:4d895e732765 1556
defmacro 22:4d895e732765 1557 int M2XStreamClient::readLocation(location_read_callback callback,
defmacro 22:4d895e732765 1558 void* context) {
defmacro 22:4d895e732765 1559 const int BUF_LEN = 40;
defmacro 22:4d895e732765 1560 char buf[BUF_LEN];
defmacro 22:4d895e732765 1561
defmacro 22:4d895e732765 1562 int length = readContentLength();
defmacro 22:4d895e732765 1563 if (length < 0) {
defmacro 22:4d895e732765 1564 close();
defmacro 22:4d895e732765 1565 return length;
defmacro 22:4d895e732765 1566 }
defmacro 22:4d895e732765 1567
defmacro 22:4d895e732765 1568 int index = skipHttpHeader();
defmacro 22:4d895e732765 1569 if (index != E_OK) {
defmacro 22:4d895e732765 1570 close();
defmacro 22:4d895e732765 1571 return index;
defmacro 22:4d895e732765 1572 }
defmacro 22:4d895e732765 1573 index = 0;
defmacro 22:4d895e732765 1574
defmacro 22:4d895e732765 1575 location_parsing_context_state state;
defmacro 22:4d895e732765 1576 state.state = state.index = 0;
defmacro 22:4d895e732765 1577 state.callback = callback;
defmacro 22:4d895e732765 1578 state.context = context;
defmacro 22:4d895e732765 1579
defmacro 22:4d895e732765 1580 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
defmacro 22:4d895e732765 1581 cbs.key_found = on_location_key_found;
defmacro 22:4d895e732765 1582 cbs.string_found = on_location_string_found;
defmacro 22:4d895e732765 1583 cbs.context.client_state = &state;
defmacro 22:4d895e732765 1584
defmacro 22:4d895e732765 1585 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
defmacro 22:4d895e732765 1586 jsonlite_parser_set_callback(p, &cbs);
defmacro 22:4d895e732765 1587
defmacro 22:4d895e732765 1588 jsonlite_result result = jsonlite_result_unknown;
defmacro 22:4d895e732765 1589 while (index < length) {
defmacro 22:4d895e732765 1590 int i = 0;
defmacro 22:4d895e732765 1591
defmacro 22:4d895e732765 1592 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 1593 while ((i < BUF_LEN) && _client->available()) {
defmacro 22:4d895e732765 1594 buf[i++] = _client->read();
defmacro 22:4d895e732765 1595 DBG("%c", buf[i - 1]);
defmacro 22:4d895e732765 1596 }
defmacro 22:4d895e732765 1597 DBGLNEND;
defmacro 22:4d895e732765 1598
defmacro 22:4d895e732765 1599 if ((!_client->connected()) &&
defmacro 22:4d895e732765 1600 (!_client->available()) &&
defmacro 22:4d895e732765 1601 ((index + i) < length)) {
defmacro 22:4d895e732765 1602 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1603 close();
defmacro 22:4d895e732765 1604 return E_NOCONNECTION;
defmacro 22:4d895e732765 1605 }
defmacro 22:4d895e732765 1606
defmacro 22:4d895e732765 1607 result = jsonlite_parser_tokenize(p, buf, i);
defmacro 22:4d895e732765 1608 if ((result != jsonlite_result_ok) &&
defmacro 22:4d895e732765 1609 (result != jsonlite_result_end_of_stream)) {
defmacro 22:4d895e732765 1610 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1611 close();
defmacro 22:4d895e732765 1612 return E_JSON_INVALID;
defmacro 22:4d895e732765 1613 }
defmacro 22:4d895e732765 1614
defmacro 22:4d895e732765 1615 index += i;
defmacro 22:4d895e732765 1616 }
defmacro 22:4d895e732765 1617
defmacro 22:4d895e732765 1618 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1619 close();
defmacro 22:4d895e732765 1620 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
defmacro 22:4d895e732765 1621 }
defmacro 22:4d895e732765 1622
defmacro 22:4d895e732765 1623 int M2XStreamClient::readCommand(m2x_command_read_callback callback,
defmacro 22:4d895e732765 1624 void* context) {
defmacro 22:4d895e732765 1625 const int BUF_LEN = 60;
defmacro 22:4d895e732765 1626 char buf[BUF_LEN];
defmacro 22:4d895e732765 1627
defmacro 22:4d895e732765 1628 int length = readContentLength();
defmacro 22:4d895e732765 1629 if (length < 0) {
defmacro 22:4d895e732765 1630 close();
defmacro 22:4d895e732765 1631 return length;
defmacro 22:4d895e732765 1632 }
defmacro 22:4d895e732765 1633
defmacro 22:4d895e732765 1634 int index = skipHttpHeader();
defmacro 22:4d895e732765 1635 if (index != E_OK) {
defmacro 22:4d895e732765 1636 close();
defmacro 22:4d895e732765 1637 return index;
defmacro 22:4d895e732765 1638 }
defmacro 22:4d895e732765 1639 index = 0;
defmacro 22:4d895e732765 1640
defmacro 22:4d895e732765 1641 m2x_command_parsing_context_state state;
defmacro 22:4d895e732765 1642 state.state = 0;
defmacro 22:4d895e732765 1643 state.index = 0;
defmacro 22:4d895e732765 1644 state.callback = callback;
defmacro 22:4d895e732765 1645 state.context = context;
defmacro 22:4d895e732765 1646
defmacro 22:4d895e732765 1647 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
defmacro 22:4d895e732765 1648 cbs.key_found = m2x_on_command_key_found;
defmacro 22:4d895e732765 1649 cbs.string_found = m2x_on_command_value_found;
defmacro 22:4d895e732765 1650 cbs.context.client_state = &state;
defmacro 22:4d895e732765 1651
defmacro 22:4d895e732765 1652 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
defmacro 22:4d895e732765 1653 jsonlite_parser_set_callback(p, &cbs);
defmacro 22:4d895e732765 1654
defmacro 22:4d895e732765 1655 jsonlite_result result = jsonlite_result_unknown;
defmacro 22:4d895e732765 1656 while (index < length) {
defmacro 22:4d895e732765 1657 int i = 0;
defmacro 22:4d895e732765 1658
defmacro 22:4d895e732765 1659 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 1660 while ((i < BUF_LEN) && _client->available()) {
defmacro 22:4d895e732765 1661 buf[i++] = _client->read();
defmacro 22:4d895e732765 1662 DBG("%c", buf[i - 1]);
defmacro 22:4d895e732765 1663 }
defmacro 22:4d895e732765 1664 DBGLNEND;
defmacro 22:4d895e732765 1665
defmacro 22:4d895e732765 1666 if ((!_client->connected()) &&
defmacro 22:4d895e732765 1667 (!_client->available()) &&
defmacro 22:4d895e732765 1668 ((index + i) < length)) {
defmacro 22:4d895e732765 1669 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1670 close();
defmacro 22:4d895e732765 1671 return E_NOCONNECTION;
defmacro 22:4d895e732765 1672 }
defmacro 22:4d895e732765 1673
defmacro 22:4d895e732765 1674 result = jsonlite_parser_tokenize(p, buf, i);
defmacro 22:4d895e732765 1675 if ((result != jsonlite_result_ok) &&
defmacro 22:4d895e732765 1676 (result != jsonlite_result_end_of_stream)) {
defmacro 22:4d895e732765 1677 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1678 close();
defmacro 22:4d895e732765 1679 return E_JSON_INVALID;
defmacro 22:4d895e732765 1680 }
defmacro 22:4d895e732765 1681
defmacro 22:4d895e732765 1682 index += i;
defmacro 22:4d895e732765 1683 }
defmacro 22:4d895e732765 1684
defmacro 22:4d895e732765 1685 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1686 close();
defmacro 22:4d895e732765 1687 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
defmacro 22:4d895e732765 1688 }
defmacro 22:4d895e732765 1689
defmacro 22:4d895e732765 1690 #endif /* M2X_ENABLE_READER */
jb8414 0:f479e4f4db0e 1691
jb8414 0:f479e4f4db0e 1692 #endif /* M2XStreamClient_h */