Modified M2XStreamClient

Fork of M2XStreamClient-JMF by Jim Flynn

Committer:
citrusbyte
Date:
Fri Jan 01 15:48:18 2016 +0000
Revision:
19:4dfa28d37b8f
Parent:
18:510895884768
Child:
21:6878944d2ce2
Use correct M2XStreamClient.cpp file

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
jb8414 0:f479e4f4db0e 4 #define MIN(a, b) (((a) > (b))?(b):(a))
jb8414 0:f479e4f4db0e 5
jb8414 0:f479e4f4db0e 6 #define MBED_PLATFORM
jb8414 0:f479e4f4db0e 7
jb8414 0:f479e4f4db0e 8 #ifdef ARDUINO_PLATFORM
jb8414 0:f479e4f4db0e 9 #include "Arduino.h"
jb8414 0:f479e4f4db0e 10
citrusbyte 15:2610823f7f2e 11 #define USER_AGENT "User-Agent: M2X Arduino Client/2.0.2"
jb8414 0:f479e4f4db0e 12 #endif
jb8414 0:f479e4f4db0e 13
jb8414 0:f479e4f4db0e 14 #ifdef MBED_PLATFORM
jb8414 0:f479e4f4db0e 15 #include "mbed.h"
jb8414 0:f479e4f4db0e 16
citrusbyte 18:510895884768 17 #define USER_AGENT "User-Agent: M2X Mbed Client/2.1.0"
jb8414 0:f479e4f4db0e 18 #endif
jb8414 0:f479e4f4db0e 19
jb8414 0:f479e4f4db0e 20 #include "Client.h"
jb8414 0:f479e4f4db0e 21 #include "NullPrint.h"
jb8414 0:f479e4f4db0e 22
jb8414 0:f479e4f4db0e 23 #ifdef DEBUG
jb8414 0:f479e4f4db0e 24 #ifdef ARDUINO_PLATFORM
jb8414 0:f479e4f4db0e 25 #define DBG(fmt_, data_) Serial.print(data_)
jb8414 0:f479e4f4db0e 26 #define DBGLN(fmt_, data_) Serial.println(data_)
jb8414 0:f479e4f4db0e 27 #define DBGLNEND Serial.println()
jb8414 0:f479e4f4db0e 28 #endif // ARDUINO_PLATFORM
jb8414 0:f479e4f4db0e 29
jb8414 0:f479e4f4db0e 30 #ifdef MBED_PLATFORM
jb8414 0:f479e4f4db0e 31 #define DBG(fmt_, data_) printf((fmt_), (data_))
jb8414 0:f479e4f4db0e 32 #define DBGLN(fmt_, data_) printf((fmt_), (data_)); printf("\n")
jb8414 0:f479e4f4db0e 33 #define DBGLNEND printf("\n")
jb8414 0:f479e4f4db0e 34 #endif // MBED_PLATFORM
jb8414 0:f479e4f4db0e 35 #else
jb8414 0:f479e4f4db0e 36 #define DBG(fmt_, data_)
jb8414 0:f479e4f4db0e 37 #define DBGLN(fmt_, data_)
jb8414 0:f479e4f4db0e 38 #define DBGLNEND
jb8414 0:f479e4f4db0e 39 #endif // DEBUG
jb8414 0:f479e4f4db0e 40
jb8414 0:f479e4f4db0e 41 #define HEX(t_) ((char) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0')))
jb8414 0:f479e4f4db0e 42 #define MAX_DOUBLE_DIGITS 7
jb8414 0:f479e4f4db0e 43
jb8414 0:f479e4f4db0e 44 static const int E_OK = 0;
jb8414 0:f479e4f4db0e 45 static const int E_NOCONNECTION = -1;
jb8414 0:f479e4f4db0e 46 static const int E_DISCONNECTED = -2;
jb8414 0:f479e4f4db0e 47 static const int E_NOTREACHABLE = -3;
jb8414 0:f479e4f4db0e 48 static const int E_INVALID = -4;
jb8414 0:f479e4f4db0e 49 static const int E_JSON_INVALID = -5;
citrusbyte 16:7903152de19f 50 static const int E_BUFFER_TOO_SMALL = -6;
citrusbyte 16:7903152de19f 51 static const int E_TIMESTAMP_ERROR = -8;
citrusbyte 16:7903152de19f 52
citrusbyte 16:7903152de19f 53 static inline bool m2x_status_is_success(int status) {
citrusbyte 16:7903152de19f 54 return (status == E_OK) || (status >= 200 && status <= 299);
citrusbyte 16:7903152de19f 55 }
citrusbyte 16:7903152de19f 56
citrusbyte 16:7903152de19f 57 static inline bool m2x_status_is_client_error(int status) {
citrusbyte 16:7903152de19f 58 return status >= 400 && status <= 499;
citrusbyte 16:7903152de19f 59 }
citrusbyte 16:7903152de19f 60
citrusbyte 16:7903152de19f 61 static inline bool m2x_status_is_server_error(int status) {
citrusbyte 16:7903152de19f 62 return status >= 500 && status <= 599;
citrusbyte 16:7903152de19f 63 }
citrusbyte 16:7903152de19f 64
citrusbyte 16:7903152de19f 65 static inline bool m2x_status_is_error(int status) {
citrusbyte 16:7903152de19f 66 return m2x_status_is_client_error(status) ||
citrusbyte 16:7903152de19f 67 m2x_status_is_server_error(status);
citrusbyte 16:7903152de19f 68 }
jb8414 0:f479e4f4db0e 69
citrusbyte 10:4ce9eba38dbe 70 /*
citrusbyte 10:4ce9eba38dbe 71 * +type+ indicates the value type: 1 for string, 2 for number
citrusbyte 10:4ce9eba38dbe 72 * NOTE that the value type here only contains a hint on how
citrusbyte 10:4ce9eba38dbe 73 * you can use the value. Even though 2 is returned, the value
citrusbyte 10:4ce9eba38dbe 74 * is still stored in (const char *), and atoi/atof is needed to
citrusbyte 10:4ce9eba38dbe 75 * get the actual value
citrusbyte 10:4ce9eba38dbe 76 */
jb8414 0:f479e4f4db0e 77 typedef void (*stream_value_read_callback)(const char* at,
jb8414 0:f479e4f4db0e 78 const char* value,
jb8414 0:f479e4f4db0e 79 int index,
citrusbyte 10:4ce9eba38dbe 80 void* context,
citrusbyte 10:4ce9eba38dbe 81 int type);
jb8414 0:f479e4f4db0e 82
jb8414 0:f479e4f4db0e 83 typedef void (*location_read_callback)(const char* name,
jb8414 0:f479e4f4db0e 84 double latitude,
jb8414 0:f479e4f4db0e 85 double longitude,
jb8414 0:f479e4f4db0e 86 double elevation,
jb8414 0:f479e4f4db0e 87 const char* timestamp,
jb8414 0:f479e4f4db0e 88 int index,
jb8414 0:f479e4f4db0e 89 void* context);
jb8414 0:f479e4f4db0e 90
jb8414 0:f479e4f4db0e 91 class M2XStreamClient {
jb8414 0:f479e4f4db0e 92 public:
jb8414 0:f479e4f4db0e 93 static const char* kDefaultM2XHost;
jb8414 0:f479e4f4db0e 94 static const int kDefaultM2XPort = 80;
jb8414 0:f479e4f4db0e 95
jb8414 0:f479e4f4db0e 96 M2XStreamClient(Client* client,
jb8414 0:f479e4f4db0e 97 const char* key,
jb8414 0:f479e4f4db0e 98 int case_insensitive = 1,
jb8414 0:f479e4f4db0e 99 const char* host = kDefaultM2XHost,
NetArc 14:205076b587fe 100 int port = kDefaultM2XPort,
NetArc 14:205076b587fe 101 const char* path_prefix = NULL);
jb8414 0:f479e4f4db0e 102
citrusbyte 10:4ce9eba38dbe 103 // Push data stream value using PUT request, returns the HTTP status code
jb8414 0:f479e4f4db0e 104 template <class T>
citrusbyte 13:0d574742208f 105 int updateStreamValue(const char* deviceId, const char* streamName, T value);
jb8414 0:f479e4f4db0e 106
jb8414 0:f479e4f4db0e 107 // Post multiple values to M2X all at once.
citrusbyte 13:0d574742208f 108 // +deviceId+ - id of the device to post values
jb8414 0:f479e4f4db0e 109 // +streamNum+ - Number of streams to post
jb8414 0:f479e4f4db0e 110 // +names+ - Array of stream names, the length of the array should
jb8414 0:f479e4f4db0e 111 // be exactly +streamNum+
jb8414 0:f479e4f4db0e 112 // +counts+ - Array of +streamNum+ length, each item in this array
jb8414 0:f479e4f4db0e 113 // containing the number of values we want to post for each stream
jb8414 0:f479e4f4db0e 114 // +ats+ - Timestamps for each value, the length of this array should
jb8414 0:f479e4f4db0e 115 // be the some of all values in +counts+, for the first +counts[0]+
jb8414 0:f479e4f4db0e 116 // items, the values belong to the first stream, for the following
jb8414 0:f479e4f4db0e 117 // +counts[1]+ number of items, the values belong to the second stream,
citrusbyte 10:4ce9eba38dbe 118 // etc. Notice that timestamps are required here: you must provide
citrusbyte 10:4ce9eba38dbe 119 // a timestamp for each value posted.
jb8414 0:f479e4f4db0e 120 // +values+ - Values to post. This works the same way as +ats+, the
jb8414 0:f479e4f4db0e 121 // first +counts[0]+ number of items contain values to post to the first
jb8414 0:f479e4f4db0e 122 // stream, the succeeding +counts[1]+ number of items contain values
jb8414 0:f479e4f4db0e 123 // for the second stream, etc. The length of this array should be
jb8414 0:f479e4f4db0e 124 // the sum of all values in +counts+ array.
jb8414 0:f479e4f4db0e 125 template <class T>
citrusbyte 13:0d574742208f 126 int postDeviceUpdates(const char* deviceId, int streamNum,
citrusbyte 13:0d574742208f 127 const char* names[], const int counts[],
citrusbyte 13:0d574742208f 128 const char* ats[], T values[]);
jb8414 0:f479e4f4db0e 129
citrusbyte 19:4dfa28d37b8f 130 // Post multiple values of a single device at once.
citrusbyte 19:4dfa28d37b8f 131 // +deviceId+ - id of the device to post values
citrusbyte 19:4dfa28d37b8f 132 // +streamNum+ - Number of streams to post
citrusbyte 19:4dfa28d37b8f 133 // +names+ - Array of stream names, the length of the array should
citrusbyte 19:4dfa28d37b8f 134 // be exactly +streamNum+
citrusbyte 19:4dfa28d37b8f 135 // +values+ - Array of values to post, the length of the array should
citrusbyte 19:4dfa28d37b8f 136 // be exactly +streamNum+. Notice that the array of +values+ should
citrusbyte 19:4dfa28d37b8f 137 // match the array of +names+, and that the ith value in +values+ is
citrusbyte 19:4dfa28d37b8f 138 // exactly the value to post for the ith stream name in +names+
citrusbyte 19:4dfa28d37b8f 139 template <class T>
citrusbyte 19:4dfa28d37b8f 140 int postSingleDeviceUpdate(const char* deviceId, int streamNum,
citrusbyte 19:4dfa28d37b8f 141 const char* names[], T values[],
citrusbyte 19:4dfa28d37b8f 142 const char* at = NULL);
citrusbyte 19:4dfa28d37b8f 143
jb8414 0:f479e4f4db0e 144 // Fetch values for a particular data stream. Since memory is
jb8414 0:f479e4f4db0e 145 // very limited on an Arduino, we cannot parse and get all the
jb8414 0:f479e4f4db0e 146 // data points in memory. Instead, we use callbacks here: whenever
jb8414 0:f479e4f4db0e 147 // a new data point is parsed, we call the callback using the values,
jb8414 0:f479e4f4db0e 148 // after that, the values will be thrown away to make space for new
jb8414 0:f479e4f4db0e 149 // values.
jb8414 0:f479e4f4db0e 150 // Note that you can also pass in a user-specified context in this
jb8414 0:f479e4f4db0e 151 // function, this context will be passed to the callback function
jb8414 0:f479e4f4db0e 152 // each time we get a data point.
jb8414 0:f479e4f4db0e 153 // For each data point, the callback will be called once. The HTTP
jb8414 0:f479e4f4db0e 154 // status code will be returned. And the content is only parsed when
jb8414 0:f479e4f4db0e 155 // the status code is 200.
citrusbyte 13:0d574742208f 156 int listStreamValues(const char* deviceId, const char* streamName,
citrusbyte 13:0d574742208f 157 stream_value_read_callback callback, void* context,
citrusbyte 13:0d574742208f 158 const char* query = NULL);
jb8414 0:f479e4f4db0e 159
jb8414 0:f479e4f4db0e 160 // Update datasource location
jb8414 0:f479e4f4db0e 161 // NOTE: On an Arduino Uno and other ATMEGA based boards, double has
jb8414 0:f479e4f4db0e 162 // 4-byte (32 bits) precision, which is the same as float. So there's
jb8414 0:f479e4f4db0e 163 // no natural double-precision floating number on these boards. With
jb8414 0:f479e4f4db0e 164 // a float value, we have a precision of roughly 7 digits, that means
jb8414 0:f479e4f4db0e 165 // either 5 or 6 digits after the floating point. According to wikipedia,
jb8414 0:f479e4f4db0e 166 // a difference of 0.00001 will give us ~1.1132m distance. If this
jb8414 0:f479e4f4db0e 167 // precision is good for you, you can use the double-version we provided
jb8414 0:f479e4f4db0e 168 // here. Otherwise, you may need to use the string-version and do the
jb8414 0:f479e4f4db0e 169 // actual conversion by yourselves.
jb8414 0:f479e4f4db0e 170 // However, with an Arduino Due board, double has 8-bytes (64 bits)
jb8414 0:f479e4f4db0e 171 // precision, which means you are free to use the double-version only
jb8414 0:f479e4f4db0e 172 // without any precision problems.
jb8414 0:f479e4f4db0e 173 // Returned value is the http status code.
jb8414 0:f479e4f4db0e 174 template <class T>
citrusbyte 13:0d574742208f 175 int updateLocation(const char* deviceId, const char* name,
jb8414 0:f479e4f4db0e 176 T latitude, T longitude, T elevation);
jb8414 0:f479e4f4db0e 177
citrusbyte 13:0d574742208f 178 // Read location information for a device. Also used callback to process
jb8414 0:f479e4f4db0e 179 // data points for memory reasons. The HTTP status code is returned,
jb8414 0:f479e4f4db0e 180 // response is only parsed when the HTTP status code is 200
citrusbyte 13:0d574742208f 181 int readLocation(const char* deviceId, location_read_callback callback,
jb8414 0:f479e4f4db0e 182 void* context);
citrusbyte 10:4ce9eba38dbe 183
citrusbyte 10:4ce9eba38dbe 184 // Delete values from a data stream
citrusbyte 10:4ce9eba38dbe 185 // You will need to provide from and end date/time strings in the ISO8601
citrusbyte 10:4ce9eba38dbe 186 // format "yyyy-mm-ddTHH:MM:SS.SSSZ" where
citrusbyte 10:4ce9eba38dbe 187 // yyyy: the year
citrusbyte 10:4ce9eba38dbe 188 // mm: the month
citrusbyte 10:4ce9eba38dbe 189 // dd: the day
citrusbyte 10:4ce9eba38dbe 190 // HH: the hour (24 hour format)
citrusbyte 10:4ce9eba38dbe 191 // MM: the minute
citrusbyte 10:4ce9eba38dbe 192 // SS.SSS: the seconds (to the millisecond)
citrusbyte 10:4ce9eba38dbe 193 // NOTE: the time is given in Zulu (GMT)
citrusbyte 10:4ce9eba38dbe 194 // M2X will delete all values within the from to end date/time range.
citrusbyte 10:4ce9eba38dbe 195 // The status code is 204 on success and 400 on a bad request (e.g. the
citrusbyte 10:4ce9eba38dbe 196 // timestamp is not in ISO8601 format or the from timestamp is not less than
citrusbyte 10:4ce9eba38dbe 197 // or equal to the end timestamp.
citrusbyte 13:0d574742208f 198 int deleteValues(const char* deviceId, const char* streamName,
citrusbyte 10:4ce9eba38dbe 199 const char* from, const char* end);
citrusbyte 16:7903152de19f 200
citrusbyte 16:7903152de19f 201 // Fetches current timestamp in seconds from M2X server. Since we
citrusbyte 16:7903152de19f 202 // are using signed 32-bit integer as return value, this will only
citrusbyte 16:7903152de19f 203 // return valid results before 03:14:07 UTC on 19 January 2038. If
citrusbyte 16:7903152de19f 204 // the device is supposed to work after that, this function should
citrusbyte 16:7903152de19f 205 // not be used.
citrusbyte 16:7903152de19f 206 //
citrusbyte 16:7903152de19f 207 // The returned value will contain the status code(positive values)
citrusbyte 16:7903152de19f 208 // or the error code(negative values).
citrusbyte 16:7903152de19f 209 // In case of success, the current timestamp will be filled in the
citrusbyte 16:7903152de19f 210 // +ts+ pointer passed in as argument.
citrusbyte 16:7903152de19f 211 //
citrusbyte 16:7903152de19f 212 // NOTE: although returning uint32_t can give us a larger space,
citrusbyte 16:7903152de19f 213 // we prefer to cope with the unix convention here.
citrusbyte 16:7903152de19f 214 int getTimestamp32(int32_t* ts);
citrusbyte 16:7903152de19f 215
citrusbyte 16:7903152de19f 216 // Fetches current timestamp in seconds from M2X server.
citrusbyte 16:7903152de19f 217 // This function will return the timestamp as an integer literal
citrusbyte 16:7903152de19f 218 // in the provided buffer. Hence there's no problem working after
citrusbyte 16:7903152de19f 219 // 03:14:07 UTC on 19 January 2038. The drawback part here, is that
citrusbyte 16:7903152de19f 220 // you will have to work with 64-bit integer, which is not available
citrusbyte 16:7903152de19f 221 // on certain platform(such as Arduino), a bignum library or alike
citrusbyte 16:7903152de19f 222 // is needed in this case.
citrusbyte 16:7903152de19f 223 //
citrusbyte 16:7903152de19f 224 // Notice +bufferLength+ is supposed to contain the length of the
citrusbyte 16:7903152de19f 225 // buffer when calling this function. It is also the caller's
citrusbyte 16:7903152de19f 226 // responsibility to ensure the buffer is big enough, otherwise
citrusbyte 16:7903152de19f 227 // the library will return an error indicating the buffer is too
citrusbyte 16:7903152de19f 228 // small.
citrusbyte 16:7903152de19f 229 // While this is not accurate all the time, one trick here is to
citrusbyte 16:7903152de19f 230 // pass in 0 as the bufferLength, in which case we will always return
citrusbyte 16:7903152de19f 231 // the buffer-too-small error. However, the correct buffer length
citrusbyte 16:7903152de19f 232 // can be found this way so a secound execution is most likely to work
citrusbyte 16:7903152de19f 233 // (unless we are at the edge of the buffer length increasing, for
citrusbyte 16:7903152de19f 234 // example, when the timestamp jumps from 9999999999 to 10000000000,
citrusbyte 16:7903152de19f 235 // which is highly unlikely to happend). However, given that the
citrusbyte 16:7903152de19f 236 // maximum 64-bit integer can be stored in 19 bytes, there's not
citrusbyte 16:7903152de19f 237 // much need to use this trick.)
citrusbyte 16:7903152de19f 238 //
citrusbyte 16:7903152de19f 239 // The returned value will contain the status code(positive values)
citrusbyte 16:7903152de19f 240 // or the error code(negative values).
citrusbyte 16:7903152de19f 241 // In case of success, the current timestamp will be filled in the
citrusbyte 16:7903152de19f 242 // passed +buffer+ pointer, and the actual used buffer length will
citrusbyte 16:7903152de19f 243 // be returned in +bufferLength+ pointer.
citrusbyte 16:7903152de19f 244 // NOTE: as long as we can read the returned buffer length, it will
citrusbyte 16:7903152de19f 245 // be used to fill in the +bufferLength+ variable even though other
citrusbyte 16:7903152de19f 246 // errors occur(buffer is not enough, network is shutdown before
citrusbyte 16:7903152de19f 247 // reading the whole buffer, etc.)
citrusbyte 16:7903152de19f 248 int getTimestamp(char* buffer, int* bufferLength);
jb8414 0:f479e4f4db0e 249 private:
jb8414 0:f479e4f4db0e 250 Client* _client;
jb8414 0:f479e4f4db0e 251 const char* _key;
jb8414 0:f479e4f4db0e 252 int _case_insensitive;
jb8414 0:f479e4f4db0e 253 const char* _host;
jb8414 0:f479e4f4db0e 254 int _port;
NetArc 14:205076b587fe 255 const char* _path_prefix;
jb8414 0:f479e4f4db0e 256 NullPrint _null_print;
jb8414 0:f479e4f4db0e 257
jb8414 0:f479e4f4db0e 258 // Writes the HTTP header part for updating a stream value
citrusbyte 13:0d574742208f 259 void writePutHeader(const char* deviceId,
citrusbyte 10:4ce9eba38dbe 260 const char* streamName,
citrusbyte 10:4ce9eba38dbe 261 int contentLength);
citrusbyte 10:4ce9eba38dbe 262 // Writes the HTTP header part for deleting stream values
citrusbyte 13:0d574742208f 263 void writeDeleteHeader(const char* deviceId,
citrusbyte 10:4ce9eba38dbe 264 const char* streamName,
citrusbyte 10:4ce9eba38dbe 265 int contentLength);
jb8414 0:f479e4f4db0e 266 // Writes HTTP header lines including M2X API Key, host, content
jb8414 0:f479e4f4db0e 267 // type and content length(if the body exists)
jb8414 0:f479e4f4db0e 268 void writeHttpHeader(int contentLength);
jb8414 0:f479e4f4db0e 269 // Parses HTTP response header and return the content length.
jb8414 0:f479e4f4db0e 270 // Note that this function does not parse all http headers, as long
jb8414 0:f479e4f4db0e 271 // as the content length is found, this function will return
jb8414 0:f479e4f4db0e 272 int readContentLength();
jb8414 0:f479e4f4db0e 273 // Skips all HTTP response header part. Return minus value in case
jb8414 0:f479e4f4db0e 274 // the connection is closed before we got all headers
jb8414 0:f479e4f4db0e 275 int skipHttpHeader();
jb8414 0:f479e4f4db0e 276 // Parses and returns the HTTP status code, note this function will
jb8414 0:f479e4f4db0e 277 // return immediately once it gets the status code
jb8414 0:f479e4f4db0e 278 int readStatusCode(bool closeClient);
jb8414 0:f479e4f4db0e 279 // Waits for a certain string pattern in the HTTP header, and returns
jb8414 0:f479e4f4db0e 280 // once the pattern is found. In the pattern, you can use '*' to denote
jb8414 0:f479e4f4db0e 281 // any character
jb8414 0:f479e4f4db0e 282 int waitForString(const char* str);
jb8414 0:f479e4f4db0e 283 // Closes the connection
jb8414 0:f479e4f4db0e 284 void close();
jb8414 0:f479e4f4db0e 285 // Parses JSON response of stream value API, and calls callback function
jb8414 0:f479e4f4db0e 286 // once we get a data point
jb8414 0:f479e4f4db0e 287 int readStreamValue(stream_value_read_callback callback, void* context);
jb8414 0:f479e4f4db0e 288 // Parses JSON response of location API, and calls callback function once
jb8414 0:f479e4f4db0e 289 // we get a data point
jb8414 0:f479e4f4db0e 290 int readLocation(location_read_callback callback, void* context);
jb8414 0:f479e4f4db0e 291 };
jb8414 0:f479e4f4db0e 292
jb8414 0:f479e4f4db0e 293 #include "M2XStreamClient_template.h"
citrusbyte 16:7903152de19f 294 #include "TimeService.h"
jb8414 0:f479e4f4db0e 295
jb8414 0:f479e4f4db0e 296 #endif /* M2XStreamClient_h */