Modified M2XStreamClient
Fork of M2XStreamClient-JMF by
M2XStreamClient.h@26:b4cca6806ac3, 2017-10-26 (annotated)
- 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?
User | Revision | Line number | New 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 */ |