Added function deleteLocations to delete the location history.

Dependents:   WNCInterface_M2Xdemo ATT_WNCInterface_Info WNCInterface_HTTP_example Public_IoT_M2X_Cellular_Demo

Fork of M2XStreamClient by AT&T M2X Team

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers M2XStreamClient.h Source File

M2XStreamClient.h

00001 #ifndef M2XStreamClient_h
00002 #define M2XStreamClient_h
00003 
00004 #if (!defined(ARDUINO_PLATFORM)) && (!defined(ESP8266_PLATFORM)) && (!defined(MBED_PLATFORM))
00005 #error "Platform definition is missing!"
00006 #endif
00007 
00008 #define M2X_VERSION "2.2.0"
00009 
00010 #ifdef ARDUINO_PLATFORM
00011 #include "m2x-arduino.h"
00012 #endif  /* ARDUINO_PLATFORM */
00013 
00014 #ifdef ESP8266_PLATFORM
00015 #include "m2x-esp8266.h"
00016 #endif /* ESP8266_PLATFORM */
00017 
00018 #ifdef MBED_PLATFORM
00019 #include "m2x-mbed.h"
00020 #endif /* MBED_PLATFORM */
00021 
00022 /* If we don't have DBG defined, provide dump implementation */
00023 #ifndef DBG
00024 #define DBG(fmt_, data_)
00025 #define DBGLN(fmt_, data_)
00026 #define DBGLNEND
00027 #endif  /* DBG */
00028 
00029 #define MIN(a, b) (((a) > (b))?(b):(a))
00030 #define TO_HEX(t_) ((char) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0')))
00031 #define MAX_DOUBLE_DIGITS 7
00032 
00033 /* For tolower */
00034 #include <ctype.h>
00035 
00036 static const int E_OK = 0;
00037 static const int E_NOCONNECTION = -1;
00038 static const int E_DISCONNECTED = -2;
00039 static const int E_NOTREACHABLE = -3;
00040 static const int E_INVALID = -4;
00041 static const int E_JSON_INVALID = -5;
00042 static const int E_BUFFER_TOO_SMALL = -6;
00043 static const int E_TIMESTAMP_ERROR = -8;
00044 
00045 static const char* DEFAULT_M2X_HOST = "api-m2x.att.com";
00046 static const int DEFAULT_M2X_PORT = 80;
00047 
00048 static inline bool m2x_status_is_success(int status) {
00049   return (status == E_OK) || (status >= 200 && status <= 299);
00050 }
00051 
00052 static inline bool m2x_status_is_client_error(int status) {
00053   return status >= 400 && status <= 499;
00054 }
00055 
00056 static inline bool m2x_status_is_server_error(int status) {
00057   return status >= 500 && status <= 599;
00058 }
00059 
00060 static inline bool m2x_status_is_error(int status) {
00061   return m2x_status_is_client_error(status) ||
00062       m2x_status_is_server_error(status);
00063 }
00064 
00065 // Null Print class used to calculate length to print
00066 class NullPrint : public Print {
00067 public:
00068   size_t counter;
00069 
00070   virtual size_t write(uint8_t b) {
00071     counter++;
00072     return 1;
00073   }
00074 
00075   virtual size_t write(const uint8_t* buf, size_t size) {
00076     counter += size;
00077     return size;
00078   }
00079 };
00080 
00081 // Encodes and prints string using Percent-encoding specified
00082 // in RFC 1738, Section 2.2
00083 static inline int print_encoded_string(Print* print, const char* str) {
00084   int bytes = 0;
00085   for (int i = 0; str[i] != 0; i++) {
00086     if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
00087         ((str[i] >= 'a') && (str[i] <= 'z')) ||
00088         ((str[i] >= '0') && (str[i] <= '9')) ||
00089         (str[i] == '-') || (str[i] == '_') ||
00090         (str[i] == '.') || (str[i] == '~')) {
00091       bytes += print->print(str[i]);
00092     } else {
00093       // Encode all other characters
00094       bytes += print->print('%');
00095       bytes += print->print(TO_HEX(str[i] / 16));
00096       bytes += print->print(TO_HEX(str[i] % 16));
00097     }
00098   }
00099   return bytes;
00100 }
00101 
00102 #ifdef M2X_ENABLE_READER
00103 /*
00104  * +type+ indicates the value type: 1 for string, 2 for number
00105  * NOTE that the value type here only contains a hint on how
00106  * you can use the value. Even though 2 is returned, the value
00107  * is still stored in (const char *), and atoi/atof is needed to
00108  * get the actual value
00109  */
00110 typedef void (*stream_value_read_callback)(const char* at,
00111                                            const char* value,
00112                                            int index,
00113                                            void* context,
00114                                            int type);
00115 
00116 typedef void (*location_read_callback)(const char* name,
00117                                        double latitude,
00118                                        double longitude,
00119                                        double elevation,
00120                                        const char* timestamp,
00121                                        int index,
00122                                        void* context);
00123                                        
00124 typedef void (*m2x_command_read_callback)(const char* id,
00125                                           const char* name,
00126                                           int index,
00127                                           void *context);
00128 #endif  /* M2X_ENABLE_READER */
00129 
00130 typedef void (*m2x_fill_data_callback)(Print *print, void *context);
00131 
00132 class M2XStreamClient {
00133 public:
00134   M2XStreamClient(Client* client,
00135                   const char* key,
00136                   void (* idlefunc)(void) = NULL,
00137                   int case_insensitive = 1,
00138                   const char* host = DEFAULT_M2X_HOST,
00139                   int port = DEFAULT_M2X_PORT,
00140                   const char* path_prefix = NULL);
00141 
00142   // Push data stream value using PUT request, returns the HTTP status code
00143   // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
00144   // the device ID here.
00145   template <class T>
00146   int updateStreamValue(const char* deviceId, const char* streamName, T value);
00147 
00148   // Post multiple values to M2X all at once.
00149   // +deviceId+ - id of the device to post values
00150   // +streamNum+ - Number of streams to post
00151   // +names+ - Array of stream names, the length of the array should
00152   // be exactly +streamNum+
00153   // +counts+ - Array of +streamNum+ length, each item in this array
00154   // containing the number of values we want to post for each stream
00155   // +ats+ - Timestamps for each value, the length of this array should
00156   // be the some of all values in +counts+, for the first +counts[0]+
00157   // items, the values belong to the first stream, for the following
00158   // +counts[1]+ number of items, the values belong to the second stream,
00159   // etc. Notice that timestamps are required here: you must provide
00160   // a timestamp for each value posted.
00161   // +values+ - Values to post. This works the same way as +ats+, the
00162   // first +counts[0]+ number of items contain values to post to the first
00163   // stream, the succeeding +counts[1]+ number of items contain values
00164   // for the second stream, etc. The length of this array should be
00165   // the sum of all values in +counts+ array.
00166   // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
00167   // the device ID here.
00168   template <class T>
00169   int postDeviceUpdates(const char* deviceId, int streamNum,
00170                         const char* names[], const int counts[],
00171                         const char* ats[], T values[]);
00172 
00173   // Post multiple values of a single device at once.
00174   // +deviceId+ - id of the device to post values
00175   // +streamNum+ - Number of streams to post
00176   // +names+ - Array of stream names, the length of the array should
00177   // be exactly +streamNum+
00178   // +values+ - Array of values to post, the length of the array should
00179   // be exactly +streamNum+. Notice that the array of +values+ should
00180   // match the array of +names+, and that the ith value in +values+ is
00181   // exactly the value to post for the ith stream name in +names+
00182   // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
00183   // the device ID here.
00184   template <class T>
00185   int postDeviceUpdate(const char* deviceId, int streamNum,
00186                        const char* names[], T values[],
00187                        const char* at = NULL);
00188 
00189 #ifdef M2X_ENABLE_READER
00190   // Fetch values for a particular data stream. Since memory is
00191   // very limited on an Arduino, we cannot parse and get all the
00192   // data points in memory. Instead, we use callbacks here: whenever
00193   // a new data point is parsed, we call the callback using the values,
00194   // after that, the values will be thrown away to make space for new
00195   // values.
00196   // Note that you can also pass in a user-specified context in this
00197   // function, this context will be passed to the callback function
00198   // each time we get a data point.
00199   // For each data point, the callback will be called once. The HTTP
00200   // status code will be returned. And the content is only parsed when
00201   // the status code is 200.
00202   int listStreamValues(const char* deviceId, const char* streamName,
00203                        stream_value_read_callback callback, void* context,
00204                        const char* query = NULL);
00205 #endif  /* M2X_ENABLE_READER */
00206 
00207   // Update datasource location
00208   // NOTE: On an Arduino Uno and other ATMEGA based boards, double has
00209   // 4-byte (32 bits) precision, which is the same as float. So there's
00210   // no natural double-precision floating number on these boards. With
00211   // a float value, we have a precision of roughly 7 digits, that means
00212   // either 5 or 6 digits after the floating point. According to wikipedia,
00213   // a difference of 0.00001 will give us ~1.1132m distance. If this
00214   // precision is good for you, you can use the double-version we provided
00215   // here. Otherwise, you may need to use the string-version and do the
00216   // actual conversion by yourselves.
00217   // However, with an Arduino Due board, double has 8-bytes (64 bits)
00218   // precision, which means you are free to use the double-version only
00219   // without any precision problems.
00220   // Returned value is the http status code.
00221   // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
00222   // the device ID here.
00223   template <class T>
00224   int updateLocation(const char* deviceId, const char* name,
00225                      T latitude, T longitude, T elevation);
00226 
00227 #ifdef M2X_ENABLE_READER
00228   // Read location information for a device. Also used callback to process
00229   // data points for memory reasons. The HTTP status code is returned,
00230   // response is only parsed when the HTTP status code is 200
00231   int readLocation(const char* deviceId, location_read_callback callback,
00232                    void* context);
00233 
00234   // Delete location information for a device. The HTTP status code is 
00235   // returned,response is only parsed when the HTTP status code is 200
00236   int deleteLocations(const char* deviceId,
00237                    const char* from, const char* end);
00238 
00239 #endif  /* M2X_ENABLE_READER */
00240 
00241   // Delete values from a data stream
00242   // You will need to provide from and end date/time strings in the ISO8601
00243   // format "yyyy-mm-ddTHH:MM:SS.SSSZ" where
00244   //   yyyy: the year
00245   //   mm: the month
00246   //   dd: the day
00247   //   HH: the hour (24 hour format)
00248   //   MM: the minute
00249   //   SS.SSS: the seconds (to the millisecond)
00250   // NOTE: the time is given in Zulu (GMT)
00251   // M2X will delete all values within the from to end date/time range.
00252   // The status code is 204 on success and 400 on a bad request (e.g. the
00253   // timestamp is not in ISO8601 format or the from timestamp is not less than
00254   // or equal to the end timestamp.
00255   int deleteValues(const char* deviceId, const char* streamName,
00256                    const char* from, const char* end);
00257 
00258 #ifdef M2X_ENABLE_READER
00259   // Fetch commands available for this device, notice that for memory constraints,
00260   // we only keep ID and name of the command received here.
00261   // You can tweak the command receiving via the query parameter.
00262   int listCommands(const char* deviceId,
00263                    m2x_command_read_callback callback, void* context,
00264                    const char* query = NULL);
00265 #endif  /* M2X_ENABLE_READER */
00266 
00267   // Mark a command as processed.
00268   // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Processed
00269   // To make sure the minimal amount of memory is needed, this API works with
00270   // a callback function. The callback function is then used to fill the request
00271   // data, note that in order to correctly set the content length in HTTP header,
00272   // the callback function will be called twice, the caller must make sure both
00273   // calls fill the Print object with exactly the same data.
00274   // If you have a pre-allocated buffer filled with the data to post, you can
00275   // use markCommandProcessedWithData API below.
00276   int markCommandProcessed(const char* deviceId, const char* commandId,
00277                            m2x_fill_data_callback callback, void *context);
00278 
00279   // Mark a command as processed with a data buffer
00280   // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Processed
00281   // This is exactly like markCommandProcessed, except that a buffer is use to
00282   // contain the data to post as the request body.
00283   int markCommandProcessedWithData(const char* deviceId, const char* commandId,
00284                                    const char* data);
00285 
00286   // Mark a command as rejected.
00287   // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Rejected
00288   // To make sure the minimal amount of memory is needed, this API works with
00289   // a callback function. The callback function is then used to fill the request
00290   // data, note that in order to correctly set the content length in HTTP header,
00291   // the callback function will be called twice, the caller must make sure both
00292   // calls fill the Print object with exactly the same data.
00293   // If you have a pre-allocated buffer filled with the data to post, you can
00294   // use markCommandRejectedWithData API below.
00295   int markCommandRejected(const char* deviceId, const char* commandId,
00296                           m2x_fill_data_callback callback, void *context);
00297 
00298   // Mark a command as rejected with a data buffer
00299   // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Rejected
00300   // This is exactly like markCommandRejected, except that a buffer is use to
00301   // contain the data to post as the request body.
00302   int markCommandRejectedWithData(const char* deviceId, const char* commandId,
00303                                   const char* data);
00304 
00305   // Fetches current timestamp in seconds from M2X server. Since we
00306   // are using signed 32-bit integer as return value, this will only
00307   // return valid results before 03:14:07 UTC on 19 January 2038. If
00308   // the device is supposed to work after that, this function should
00309   // not be used.
00310   //
00311   // The returned value will contain the status code(positive values)
00312   // or the error code(negative values).
00313   // In case of success, the current timestamp will be filled in the
00314   // +ts+ pointer passed in as argument.
00315   //
00316   // NOTE: although returning uint32_t can give us a larger space,
00317   // we prefer to cope with the unix convention here.
00318   int getTimestamp32(int32_t* ts);
00319 
00320   // Fetches current timestamp in seconds from M2X server.
00321   // This function will return the timestamp as an integer literal
00322   // in the provided buffer. Hence there's no problem working after
00323   // 03:14:07 UTC on 19 January 2038. The drawback part here, is that
00324   // you will have to work with 64-bit integer, which is not available
00325   // on certain platform(such as Arduino), a bignum library or alike
00326   // is needed in this case.
00327   //
00328   // Notice +bufferLength+ is supposed to contain the length of the
00329   // buffer when calling this function. It is also the caller's
00330   // responsibility to ensure the buffer is big enough, otherwise
00331   // the library will return an error indicating the buffer is too
00332   // small.
00333   // While this is not accurate all the time, one trick here is to
00334   // pass in 0 as the bufferLength, in which case we will always return
00335   // the buffer-too-small error. However, the correct buffer length
00336   // can be found this way so a secound execution is most likely to work
00337   // (unless we are at the edge of the buffer length increasing, for
00338   // example, when the timestamp jumps from 9999999999 to 10000000000,
00339   // which is highly unlikely to happend). However, given that the
00340   // maximum 64-bit integer can be stored in 19 bytes, there's not
00341   // much need to use this trick.)
00342   //
00343   // The returned value will contain the status code(positive values)
00344   // or the error code(negative values).
00345   // In case of success, the current timestamp will be filled in the
00346   // passed +buffer+ pointer, and the actual used buffer length will
00347   // be returned in +bufferLength+ pointer.
00348   // NOTE: as long as we can read the returned buffer length, it will
00349   // be used to fill in the +bufferLength+ variable even though other
00350   // errors occur(buffer is not enough, network is shutdown before
00351   // reading the whole buffer, etc.)
00352   int getTimestamp(char* buffer, int* bufferLength);
00353 private:
00354   Client* _client;
00355   const char* _key;
00356   int _case_insensitive;
00357   const char* _host;
00358   int _port;
00359   void (* _idlefunc)(void);
00360   const char* _path_prefix;
00361   NullPrint _null_print;
00362 
00363   // Writes the HTTP header part for updating a stream value
00364   void writePutHeader(const char* deviceId,
00365                       const char* streamName,
00366                       int contentLength);
00367   // Writes the HTTP header part for deleting stream values
00368   void writeDeleteHeader(const char* deviceId,
00369                          const char* streamName,
00370                          int contentLength);
00371   void writeDeleteLocationHeader(const char* deviceId,
00372                          int contentLength);
00373                          
00374   // Writes HTTP header lines including M2X API Key, host, content
00375   // type and content length(if the body exists)
00376   void writeHttpHeader(int contentLength);
00377   // Parses HTTP response header and return the content length.
00378   // Note that this function does not parse all http headers, as long
00379   // as the content length is found, this function will return
00380   int readContentLength();
00381   // Skips all HTTP response header part. Return minus value in case
00382   // the connection is closed before we got all headers
00383   int skipHttpHeader();
00384   // Parses and returns the HTTP status code, note this function will
00385   // return immediately once it gets the status code
00386   int readStatusCode(bool closeClient);
00387   // Waits for a certain string pattern in the HTTP header, and returns
00388   // once the pattern is found. In the pattern, you can use '*' to denote
00389   // any character
00390   int waitForString(const char* str);
00391   // Closes the connection
00392   void close();
00393 
00394 #ifdef M2X_ENABLE_READER
00395   // Parses JSON response of stream value API, and calls callback function
00396   // once we get a data point
00397   int readStreamValue(stream_value_read_callback callback, void* context);
00398   // Parses JSON response of location API, and calls callback function once
00399   // we get a data point
00400   int readLocation(location_read_callback callback, void* context);
00401   // Parses JSON response of command API, and calls callback function once
00402   // we get a data point
00403   int readCommand(m2x_command_read_callback callback, void* context);
00404 #endif  /* M2X_ENABLE_READER */
00405 };
00406 
00407 
00408 // A ISO8601 timestamp generation service for M2X.
00409 // It uses the Time API provided by the M2X server to initialize
00410 // clock, then uses millis() function provided by Arduino to calculate
00411 // time advancements so as to reduce API query times.
00412 //
00413 // Right now, this service only works with 32-bit timestamp, meaning that
00414 // this service won't work after 03:14:07 UTC on 19 January 2038. However,
00415 // a similar service that uses 64-bit timestamp can be implemented following
00416 // the logic here.
00417 class TimeService {
00418 public:
00419   TimeService(M2XStreamClient* client);
00420 
00421   // Initialize the time service. Notice the TimeService instance is only
00422   // working after calling this function successfully.
00423   int init();
00424 
00425   // Reset the internal recorded time by calling M2X Time API again. Normally,
00426   // you don't need to call this manually. TimeService will handle Arduino clock
00427   // overflow automatically
00428   int reset();
00429 
00430   // Fills ISO8601 formatted timestamp into the buffer provided. +length+ should
00431   // contains the maximum supported length of the buffer when calling. For now,
00432   // the buffer should be able to store 25 characters for a full ISO8601 formatted
00433   // timestamp, otherwise, an error will be returned.
00434   int getTimestamp(char* buffer, int* length);
00435 private:
00436   M2XStreamClient* _client;
00437   int32_t _server_timestamp;
00438   uint32_t _local_last_milli;
00439   M2XTimer _timer;
00440 };
00441 
00442 
00443 // Implementations
00444 M2XStreamClient::M2XStreamClient(Client* client,
00445                                  const char* key,
00446                                  void (* idlefunc)(void),
00447                                  int case_insensitive,
00448                                  const char* host,
00449                                  int port,
00450                                  const char* path_prefix) : _client(client),
00451                                              _key(key),
00452                                              _idlefunc(idlefunc),
00453                                              _case_insensitive(case_insensitive),
00454                                              _host(host),
00455                                              _port(port),
00456                                              _path_prefix(path_prefix),
00457                                              _null_print() {
00458 }
00459 
00460 template <class T>
00461 int M2XStreamClient::updateStreamValue(const char* deviceId, const char* streamName, T value) {
00462   if (_client->connect(_host, _port)) {
00463     DBGLN("%s", "Connected to M2X server!");
00464     writePutHeader(deviceId, streamName,
00465                    //  for {"value": and }
00466                    _null_print.print(value) + 12);
00467     _client->print("{\"value\":\"");
00468     _client->print(value);
00469     _client->print("\"}");
00470   } else {
00471     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00472     return E_NOCONNECTION;
00473   }
00474 
00475   return readStatusCode(true);
00476 }
00477 
00478 template <class T>
00479 inline int write_multiple_values(Print* print, int streamNum,
00480                                  const char* names[], const int counts[],
00481                                  const char* ats[], T values[]) {
00482   int bytes = 0, value_index = 0;
00483   bytes += print->print("{\"values\":{");
00484   for (int i = 0; i < streamNum; i++) {
00485     bytes += print->print("\"");
00486     bytes += print->print(names[i]);
00487     bytes += print->print("\":[");
00488     for (int j = 0; j < counts[i]; j++) {
00489       bytes += print->print("{\"timestamp\": \"");
00490       bytes += print->print(ats[value_index]);
00491       bytes += print->print("\",\"value\": \"");
00492       bytes += print->print(values[value_index]);
00493       bytes += print->print("\"}");
00494       if (j < counts[i] - 1) { bytes += print->print(","); }
00495       value_index++;
00496     }
00497     bytes += print->print("]");
00498     if (i < streamNum - 1) { bytes += print->print(","); }
00499   }
00500   bytes += print->print("}}");
00501   return bytes;
00502 }
00503 
00504 template <class T>
00505 int M2XStreamClient::postDeviceUpdates(const char* deviceId, int streamNum,
00506                                        const char* names[], const int counts[],
00507                                        const char* ats[], T values[]) {
00508   if (_client->connect(_host, _port)) {
00509     DBGLN("%s", "Connected to M2X server!");
00510     int length = write_multiple_values(&_null_print, streamNum, names,
00511                                        counts, ats, values);
00512     _client->print("POST ");
00513     if (_path_prefix) { _client->print(_path_prefix); }
00514     _client->print("/v2/devices/");
00515     _client->print(deviceId);
00516     _client->println("/updates HTTP/1.1");
00517     writeHttpHeader(length);
00518     write_multiple_values(_client, streamNum, names, counts, ats, values);
00519   } else {
00520     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00521     return E_NOCONNECTION;
00522   }
00523   return readStatusCode(true);
00524 }
00525 
00526 template <class T>
00527 inline int write_single_device_values(Print* print, int streamNum,
00528                                       const char* names[], T values[],
00529                                       const char* at) {
00530   int bytes = 0;
00531   bytes += print->print("{\"values\":{");
00532   for (int i = 0; i < streamNum; i++) {
00533     bytes += print->print("\"");
00534     bytes += print->print(names[i]);
00535     bytes += print->print("\": \"");
00536     bytes += print->print(values[i]);
00537     bytes += print->print("\"");
00538     if (i < streamNum - 1) { bytes += print->print(","); }
00539   }
00540   bytes += print->print("}");
00541   if (at != NULL) {
00542     bytes += print->print(",\"timestamp\":\"");
00543     bytes += print->print(at);
00544     bytes += print->print("\"");
00545   }
00546   bytes += print->print("}");
00547   return bytes;
00548 }
00549 
00550 template <class T>
00551 int M2XStreamClient::postDeviceUpdate(const char* deviceId, int streamNum,
00552                                       const char* names[], T values[],
00553                                       const char* at) {
00554   if (_client->connect(_host, _port)) {
00555     DBGLN("%s", "Connected to M2X server!");
00556     int length = write_single_device_values(&_null_print, streamNum, names,
00557                                             values, at);
00558     _client->print("POST ");
00559     if (_path_prefix) { _client->print(_path_prefix); }
00560     _client->print("/v2/devices/");
00561     _client->print(deviceId);
00562     _client->println("/update HTTP/1.1");
00563     writeHttpHeader(length);
00564     write_single_device_values(_client, streamNum, names, values, at);
00565   } else {
00566     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00567     return E_NOCONNECTION;
00568   }
00569   return readStatusCode(true);
00570 }
00571 
00572 template <class T>
00573 static int write_location_data(Print* print, const char* name,
00574                                T latitude, T longitude,
00575                                T elevation) {
00576   int bytes = 0;
00577   bytes += print->print("{\"name\":\"");
00578   bytes += print->print(name);
00579   bytes += print->print("\",\"latitude\":\"");
00580   bytes += print->print(latitude);
00581   bytes += print->print("\",\"longitude\":\"");
00582   bytes += print->print(longitude);
00583   bytes += print->print("\",\"elevation\":\"");
00584   bytes += print->print(elevation);
00585   bytes += print->print("\"}");
00586   return bytes;
00587 }
00588 
00589 static int write_location_data(Print* print, const char* name,
00590                                double latitude, double longitude,
00591                                double elevation) {
00592   int bytes = 0;
00593   bytes += print->print("{\"name\":\"");
00594   bytes += print->print(name);
00595   bytes += print->print("\",\"latitude\":\"");
00596   bytes += print->print(latitude, MAX_DOUBLE_DIGITS);
00597   bytes += print->print("\",\"longitude\":\"");
00598   bytes += print->print(longitude, MAX_DOUBLE_DIGITS);
00599   bytes += print->print("\",\"elevation\":\"");
00600   bytes += print->print(elevation);
00601   bytes += print->print("\"}");
00602   return bytes;
00603 }
00604 
00605 template <class T>
00606 int M2XStreamClient::updateLocation(const char* deviceId,
00607                                     const char* name,
00608                                     T latitude,
00609                                     T longitude,
00610                                     T elevation) {
00611   if (_client->connect(_host, _port)) {
00612     DBGLN("%s", "Connected to M2X server!");
00613 
00614     int length = write_location_data(&_null_print, name, latitude, longitude,
00615                                      elevation);
00616     _client->print("PUT ");
00617     if (_path_prefix) { _client->print(_path_prefix); }
00618     _client->print("/v2/devices/");
00619     _client->print(deviceId);
00620     _client->println("/location HTTP/1.1");
00621 
00622     writeHttpHeader(length);
00623     write_location_data(_client, name, latitude, longitude, elevation);
00624   } else {
00625     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00626     return E_NOCONNECTION;
00627   }
00628   return readStatusCode(true);
00629 }
00630 
00631 static inline int write_delete_values(Print* print, const char* from,
00632                                       const char* end) {
00633   int bytes = 0;
00634   bytes += print->print("{\"from\":\"");
00635   bytes += print->print(from);
00636   bytes += print->print("\",\"end\":\"");
00637   bytes += print->print(end);
00638   bytes += print->print("\"}");
00639   return bytes;
00640 }
00641 
00642 int M2XStreamClient::deleteValues(const char* deviceId, const char* streamName,
00643                                   const char* from, const char* end) {
00644   if (_client->connect(_host, _port)) {
00645     DBGLN("%s", "Connected to M2X server!");
00646     int length = write_delete_values(&_null_print, from, end);
00647     writeDeleteHeader(deviceId, streamName, length);
00648     write_delete_values(_client, from, end);
00649   } else {
00650     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00651     return E_NOCONNECTION;
00652   }
00653 
00654   return readStatusCode(true);
00655 }
00656 
00657 int M2XStreamClient::markCommandProcessed(const char* deviceId,
00658                                           const char* commandId,
00659                                           m2x_fill_data_callback callback,
00660                                           void *context) {
00661   if (_client->connect(_host, _port)) {
00662     DBGLN("%s", "Connected to M2X server!");
00663     _null_print.counter = 0;
00664     callback(&_null_print, context);
00665     int length = _null_print.counter;
00666     _client->print("POST ");
00667     if (_path_prefix) { _client->print(_path_prefix); }
00668     _client->print("/v2/devices/");
00669     _client->print(deviceId);
00670     _client->print("/commands/");
00671     _client->print(commandId);
00672     _client->print("/process");
00673     _client->println(" HTTP/1.1");
00674     writeHttpHeader(length);
00675     callback(_client, context);
00676   } else {
00677     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00678     return E_NOCONNECTION;
00679   }
00680   return readStatusCode(true);
00681 }
00682 
00683 static void m2x_fixed_buffer_filling_callback(Print *print, void *context) {
00684   if (context) {
00685     print->print((const char *) context);
00686   }
00687 }
00688 
00689 int M2XStreamClient::markCommandProcessedWithData(const char* deviceId,
00690                                                   const char* commandId,
00691                                                   const char* data) {
00692   return markCommandProcessed(deviceId, commandId,
00693                               m2x_fixed_buffer_filling_callback, (void *) data);
00694 }
00695 
00696 int M2XStreamClient::markCommandRejected(const char* deviceId,
00697                                          const char* commandId,
00698                                          m2x_fill_data_callback callback,
00699                                          void *context) {
00700   if (_client->connect(_host, _port)) {
00701     DBGLN("%s", "Connected to M2X server!");
00702     _null_print.counter = 0;
00703     callback(&_null_print, context);
00704     int length = _null_print.counter;
00705     _client->print("POST ");
00706     if (_path_prefix) { _client->print(_path_prefix); }
00707     _client->print("/v2/devices/");
00708     _client->print(deviceId);
00709     _client->print("/commands/");
00710     _client->print(commandId);
00711     _client->print("/reject");
00712     _client->println(" HTTP/1.1");
00713     writeHttpHeader(length);
00714     callback(_client, context);
00715   } else {
00716     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00717     return E_NOCONNECTION;
00718   }
00719   return readStatusCode(true);
00720 }
00721 
00722 int M2XStreamClient::markCommandRejectedWithData(const char* deviceId,
00723                                                  const char* commandId,
00724                                                  const char* data) {
00725   return markCommandRejected(deviceId, commandId,
00726                              m2x_fixed_buffer_filling_callback, (void *) data);
00727 }
00728 
00729 int M2XStreamClient::getTimestamp32(int32_t *ts) {
00730   // The maximum value of signed 64-bit integer is 0x7fffffffffffffff,
00731   // which is 9223372036854775807. It consists of 19 characters, so a
00732   // buffer of 20 is definitely enough here
00733   int length = 20;
00734   char buffer[20];
00735   int status = getTimestamp(buffer, &length);
00736   if (status == 200) {
00737     int32_t result = 0;
00738     for (int i = 0; i < length; i++) {
00739       result = result * 10 + (buffer[i] - '0');
00740     }
00741     if (ts != NULL) { *ts = result; }
00742   }
00743   return status;
00744 }
00745 
00746 int M2XStreamClient::getTimestamp(char* buffer, int *bufferLength) {
00747   if (bufferLength == NULL) { return E_INVALID; }
00748   if (_client->connect(_host, _port)) {
00749     DBGLN("%s", "Connected to M2X server!");
00750     _client->print("GET ");
00751     if (_path_prefix) { _client->print(_path_prefix); }
00752     _client->println("/v2/time/seconds HTTP/1.1");
00753 
00754     writeHttpHeader(-1);
00755   } else {
00756     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
00757     return E_NOCONNECTION;
00758   }
00759   int status = readStatusCode(false);
00760   if (status == 200) {
00761     int length = readContentLength();
00762     if (length < 0) {
00763       close();
00764       return length;
00765     }
00766     if (*bufferLength < length) {
00767       *bufferLength = length;
00768       return E_BUFFER_TOO_SMALL;
00769     }
00770     *bufferLength = length;
00771     int index = skipHttpHeader();
00772     if (index != E_OK) {
00773       close();
00774       return index;
00775     }
00776     index = 0;
00777     while (index < length) {
00778       DBG("%s", "Received Data: ");
00779       while ((index < length) && _client->available()) {
00780         buffer[index++] = _client->read();
00781         DBG("%c", buffer[index - 1]);
00782       }
00783       DBGLNEND;
00784 
00785       if ((!_client->connected()) &&
00786           (index < length)) {
00787         close();
00788         return E_NOCONNECTION;
00789       }
00790 
00791       if (_idlefunc!=NULL) {
00792         _idlefunc();
00793       } else {
00794         delay(200);
00795       }
00796     }
00797   }
00798   close();
00799   return status;
00800 }
00801 
00802 
00803 void M2XStreamClient::writePutHeader(const char* deviceId,
00804                                      const char* streamName,
00805                                      int contentLength) {
00806   _client->print("PUT ");
00807   if (_path_prefix) { _client->print(_path_prefix); }
00808   _client->print("/v2/devices/");
00809   _client->print(deviceId);
00810   _client->print("/streams/");
00811   print_encoded_string(_client, streamName);
00812   _client->println("/value HTTP/1.1");
00813 
00814   writeHttpHeader(contentLength);
00815 }
00816 
00817 void M2XStreamClient::writeDeleteHeader(const char* deviceId,
00818                                         const char* streamName,
00819                                         int contentLength) {
00820   _client->print("DELETE ");
00821   if (_path_prefix) { _client->print(_path_prefix); }
00822   _client->print("/v2/devices/");
00823   _client->print(deviceId);
00824   _client->print("/streams/");
00825   print_encoded_string(_client, streamName);
00826   _client->print("/values");
00827   _client->println(" HTTP/1.1");
00828 
00829   writeHttpHeader(contentLength);
00830 }
00831 
00832 void M2XStreamClient::writeDeleteLocationHeader(const char* deviceId,
00833                                         int contentLength) {
00834   _client->print("DELETE ");
00835   if (_path_prefix) { _client->print(_path_prefix); }
00836   _client->print("/v2/devices/");
00837   _client->print(deviceId);
00838   _client->print("/location/waypoints/");
00839   _client->println(" HTTP/1.1");
00840 
00841   writeHttpHeader(contentLength);
00842 }
00843 
00844 
00845 void M2XStreamClient::writeHttpHeader(int contentLength) {
00846   _client->println(USER_AGENT);
00847   _client->print("X-M2X-KEY: ");
00848   _client->println(_key);
00849 
00850   _client->print("Host: ");
00851   print_encoded_string(_client, _host);
00852   if (_port != DEFAULT_M2X_PORT) {
00853     _client->print(":");
00854     // port is an integer, does not need encoding
00855     _client->print(_port);
00856   }
00857   _client->println();
00858 
00859   if (contentLength > 0) {
00860     _client->println("Content-Type: application/json");
00861     DBG("%s", "Content Length: ");
00862     DBGLN("%d", contentLength);
00863 
00864     _client->print("Content-Length: ");
00865     _client->println(contentLength);
00866   }
00867   _client->println();
00868 }
00869 
00870 int M2XStreamClient::waitForString(const char* str) {
00871   int currentIndex = 0;
00872   if (str[currentIndex] == '\0') return E_OK;
00873 
00874   while (true) {
00875     while (_client->available()) {
00876       char c = _client->read();
00877       DBG("%c", c);
00878 
00879       int cmp;
00880       if (_case_insensitive) {
00881         cmp = tolower(c) - tolower(str[currentIndex]);
00882       } else {
00883         cmp = c - str[currentIndex];
00884       }
00885 
00886       if ((str[currentIndex] == '*') || (cmp == 0)) {
00887         currentIndex++;
00888         if (str[currentIndex] == '\0') {
00889           return E_OK;
00890         }
00891       } else {
00892         // start from the beginning
00893         currentIndex = 0;
00894       }
00895     }
00896 
00897     if (!_client->connected()) {
00898       DBGLN("%s", "ERROR: The client is disconnected from the server!");
00899 
00900       close();
00901       return E_DISCONNECTED;
00902     }
00903     if (_idlefunc!=NULL)
00904       _idlefunc();
00905     else
00906       delay(200);
00907   }
00908   // never reached here
00909   return E_NOTREACHABLE;
00910 }
00911 
00912 int M2XStreamClient::readStatusCode(bool closeClient) {
00913   int responseCode = 0;
00914   int ret = waitForString("HTTP/*.* ");
00915   if (ret != E_OK) {
00916     if (closeClient) close();
00917     return ret;
00918   }
00919 
00920   // ret is not needed from here(since it must be E_OK), so we can use it
00921   // as a regular variable now.
00922   ret = 0;
00923   while (true) {
00924     while (_client->available()) {
00925       char c = _client->read();
00926       DBG("%c", c);
00927 
00928       responseCode = responseCode * 10 + (c - '0');
00929       ret++;
00930       if (ret == 3) {
00931         if (closeClient) close();
00932         return responseCode;
00933       }
00934     }
00935 
00936     if (!_client->connected()) {
00937       DBGLN("%s", "ERROR: The client is disconnected from the server!");
00938 
00939       if (closeClient) close();
00940       return E_DISCONNECTED;
00941     }
00942     if (_idlefunc!=NULL)
00943       _idlefunc();
00944     else
00945       delay(200);
00946   }
00947 
00948   // never reached here
00949   return E_NOTREACHABLE;
00950 }
00951 
00952 int M2XStreamClient::readContentLength() {
00953   int ret = waitForString("Content-Length: ");
00954   if (ret != E_OK) {
00955     return ret;
00956   }
00957 
00958   // From now on, ret is not needed, we can use it
00959   // to keep the final result
00960   ret = 0;
00961   while (true) {
00962     while (_client->available()) {
00963       char c = _client->read();
00964       DBG("%c", c);
00965 
00966       if ((c == '\r') || (c == '\n')) {
00967         return (ret == 0) ? (E_INVALID) : (ret);
00968       } else {
00969         ret = ret * 10 + (c - '0');
00970       }
00971     }
00972 
00973     if (!_client->connected()) {
00974       DBGLN("%s", "ERROR: The client is disconnected from the server!");
00975 
00976       return E_DISCONNECTED;
00977     }
00978     if (_idlefunc!=NULL)
00979       _idlefunc();
00980     else
00981       delay(200);
00982   }
00983 
00984   // never reached here
00985   return E_NOTREACHABLE;
00986 }
00987 
00988 int M2XStreamClient::skipHttpHeader() {
00989   return waitForString("\n\r\n");
00990 }
00991 
00992 void M2XStreamClient::close() {
00993   // Eats up buffered data before closing
00994   _client->flush();
00995   _client->stop();
00996 }
00997 
00998 static inline int fill_iso8601_timestamp(int32_t seconds, int32_t milli,
00999                                          char* buffer, int* length);
01000 
01001 TimeService::TimeService(M2XStreamClient* client) : _client(client) {
01002 }
01003 
01004 int TimeService::init() {
01005   _timer.start();
01006   return reset();
01007 }
01008 
01009 int TimeService::reset() {
01010   int32_t ts;
01011   int status = _client->getTimestamp32(&ts);
01012 
01013   if (m2x_status_is_success(status)) {
01014     _server_timestamp = ts;
01015     _local_last_milli = _timer.read_ms();
01016   }
01017 
01018   return status;
01019 }
01020 
01021 int TimeService::getTimestamp(char* buffer, int* length) {
01022   uint32_t now = _timer.read_ms();
01023   if (now < _local_last_milli) {
01024     // In case of a timestamp overflow(happens once every 50 days on
01025     // Arduino), we reset the server timestamp recorded.
01026     // NOTE: while on an Arduino this might be okay, the situation is worse
01027     // on mbed, see the notes in m2x-mbed.h for details
01028     int status = reset();
01029     if (!m2x_status_is_success(status)) { return status; }
01030     now = _timer.read_ms();
01031   }
01032   if (now < _local_last_milli) {
01033     // We have already reseted the timestamp, so this cannot happen
01034     // (an HTTP request can take longer than 50 days to finished? You
01035     // must be kidding here). Something else must be wrong here
01036     return E_TIMESTAMP_ERROR;
01037   }
01038   uint32_t diff = now - _local_last_milli;
01039   _local_last_milli = now;
01040   _server_timestamp += (int32_t) (diff / 1000); // Milliseconds to seconds
01041   return fill_iso8601_timestamp(_server_timestamp, (int32_t) (diff % 1000),
01042                                 buffer, length);
01043 }
01044 
01045 #define SIZE_ISO_8601 25
01046 static inline bool is_leap_year(int16_t y) {
01047   return ((1970 + y) > 0) &&
01048       !((1970 + y) % 4) &&
01049       (((1970 + y) % 100) || !((1970 + y) % 400));
01050 }
01051 static inline int32_t days_in_year(int16_t y) {
01052   return is_leap_year(y) ? 366 : 365;
01053 }
01054 static const uint8_t MONTH_DAYS[]={31,28,31,30,31,30,31,31,30,31,30,31};
01055 
01056 static inline int fill_iso8601_timestamp(int32_t timestamp, int32_t milli,
01057                                          char* buffer, int* length) {
01058   int16_t year;
01059   int8_t month, month_length;
01060   int32_t day;
01061   int8_t hour, minute, second;
01062 
01063   if (*length < SIZE_ISO_8601) {
01064     *length = SIZE_ISO_8601;
01065     return E_BUFFER_TOO_SMALL;
01066   }
01067 
01068   second = timestamp % 60;
01069   timestamp /= 60; // now it is minutes
01070 
01071   minute = timestamp % 60;
01072   timestamp /= 60; // now it is hours
01073 
01074   hour = timestamp % 24;
01075   timestamp /= 24; // now it is days
01076 
01077   year = 0;
01078   day = 0;
01079   while ((day += days_in_year(year)) <= timestamp) {
01080     year++;
01081   }
01082   day -= days_in_year(year);
01083   timestamp -= day; // now it is days in this year, starting at 0
01084 
01085   day = 0;
01086   month_length = 0;
01087   for (month = 0; month < 12; month++) {
01088     if (month == 1) {
01089       // February
01090       month_length = is_leap_year(year) ? 29 : 28;
01091     } else {
01092       month_length = MONTH_DAYS[month];
01093     }
01094 
01095     if (timestamp >= month_length) {
01096       timestamp -= month_length;
01097     } else {
01098       break;
01099     }
01100   }
01101   year = 1970 + year;
01102   month++; // offset by 1
01103   day = timestamp + 1;
01104 
01105   int i = 0, j = 0;
01106 
01107   // NOTE: It seems the snprintf implementation in Arduino has bugs,
01108   // we have to manually piece the string together here.
01109 #define INT_TO_STR(v_, width_) \
01110   for (j = 0; j < (width_); j++) { \
01111     buffer[i + (width_) - 1 - j] = '0' + ((v_) % 10); \
01112     (v_) /= 10; \
01113   } \
01114   i += (width_)
01115 
01116   INT_TO_STR(year, 4);
01117   buffer[i++] = '-';
01118   INT_TO_STR(month, 2);
01119   buffer[i++] = '-';
01120   INT_TO_STR(day, 2);
01121   buffer[i++] = 'T';
01122   INT_TO_STR(hour, 2);
01123   buffer[i++] = ':';
01124   INT_TO_STR(minute, 2);
01125   buffer[i++] = ':';
01126   INT_TO_STR(second, 2);
01127   buffer[i++] = '.';
01128   INT_TO_STR(milli, 3);
01129   buffer[i++] = 'Z';
01130   buffer[i++] = '\0';
01131 
01132 #undef INT_TO_STR
01133 
01134   *length = i;
01135   return E_OK;
01136 }
01137 
01138 /* Reader functions */
01139 #ifdef M2X_ENABLE_READER
01140 
01141 // Data structures and functions used to parse stream values
01142 
01143 #define STREAM_BUF_LEN 32
01144 
01145 typedef struct {
01146   uint8_t state;
01147   char at_str[STREAM_BUF_LEN + 1];
01148   char value_str[STREAM_BUF_LEN + 1];
01149   int index;
01150 
01151   stream_value_read_callback callback;
01152   void* context;
01153 } stream_parsing_context_state;
01154 
01155 #define WAITING_AT 0x1
01156 #define GOT_AT 0x2
01157 #define WAITING_VALUE 0x4
01158 #define GOT_VALUE 0x8
01159 
01160 #define GOT_STREAM (GOT_AT | GOT_VALUE)
01161 #define TEST_GOT_STREAM(state_) (((state_) & GOT_STREAM) == GOT_STREAM)
01162 
01163 #define TEST_IS_AT(state_) (((state_) & (WAITING_AT | GOT_AT)) == WAITING_AT)
01164 #define TEST_IS_VALUE(state_) (((state_) & (WAITING_VALUE | GOT_VALUE)) == \
01165                                WAITING_VALUE)
01166 
01167 static void on_stream_key_found(jsonlite_callback_context* context,
01168                                 jsonlite_token* token)
01169 {
01170   stream_parsing_context_state* state =
01171       (stream_parsing_context_state*) context->client_state;
01172   if (strncmp((const char*) token->start, "timestamp", 9) == 0) {
01173     state->state |= WAITING_AT;
01174   } else if ((strncmp((const char*) token->start, "value", 5) == 0) &&
01175              (token->start[5] != 's')) { // get rid of "values"
01176     state->state |= WAITING_VALUE;
01177   }
01178 }
01179 
01180 static void on_stream_value_found(jsonlite_callback_context* context,
01181                                   jsonlite_token* token,
01182                                   int type)
01183 {
01184   stream_parsing_context_state* state =
01185       (stream_parsing_context_state*) context->client_state;
01186 
01187   if (TEST_IS_AT(state->state)) {
01188     strncpy(state->at_str, (const char*) token->start,
01189             MIN(token->end - token->start, STREAM_BUF_LEN));
01190     state->at_str[MIN(token->end - token->start, STREAM_BUF_LEN)] = '\0';
01191     state->state |= GOT_AT;
01192   } else if (TEST_IS_VALUE(state->state)) {
01193     strncpy(state->value_str, (const char*) token->start,
01194             MIN(token->end - token->start, STREAM_BUF_LEN));
01195     state->value_str[MIN(token->end - token->start, STREAM_BUF_LEN)] = '\0';
01196     state->state |= GOT_VALUE;
01197   }
01198 
01199   if (TEST_GOT_STREAM(state->state)) {
01200     state->callback(state->at_str, state->value_str,
01201                     state->index++, state->context, type);
01202     state->state = 0;
01203   }
01204 }
01205 
01206 static void on_stream_string_found(jsonlite_callback_context* context,
01207                                    jsonlite_token* token)
01208 {
01209   on_stream_value_found(context, token, 1);
01210 }
01211 
01212 static void on_stream_number_found(jsonlite_callback_context* context,
01213                                    jsonlite_token* token)
01214 {
01215   on_stream_value_found(context, token, 2);
01216 }
01217 
01218 // Data structures and functions used to parse locations
01219 
01220 #define LOCATION_BUF_LEN 20
01221 
01222 typedef struct {
01223   uint16_t state;
01224   char name_str[LOCATION_BUF_LEN + 1];
01225   double latitude;
01226   double longitude;
01227   double elevation;
01228   char timestamp_str[LOCATION_BUF_LEN + 1];
01229   int index;
01230 
01231   location_read_callback callback;
01232   void* context;
01233 } location_parsing_context_state;
01234 
01235 #define WAITING_NAME 0x1
01236 #define WAITING_LATITUDE 0x2
01237 #define WAITING_LONGITUDE 0x4
01238 #define WAITING_ELEVATION 0x8
01239 #define WAITING_TIMESTAMP 0x10
01240 
01241 #define GOT_NAME 0x20
01242 #define GOT_LATITUDE 0x40
01243 #define GOT_LONGITUDE 0x80
01244 #define GOT_ELEVATION 0x100
01245 #define GOT_TIMESTAMP 0x200
01246 
01247 #define GOT_LOCATION (GOT_NAME | GOT_LATITUDE | GOT_LONGITUDE | GOT_ELEVATION | GOT_TIMESTAMP)
01248 #define TEST_GOT_LOCATION(state_) (((state_) & GOT_LOCATION) == GOT_LOCATION)
01249 
01250 #define TEST_IS_NAME(state_) (((state_) & (WAITING_NAME | GOT_NAME)) == WAITING_NAME)
01251 #define TEST_IS_LATITUDE(state_) (((state_) & (WAITING_LATITUDE | GOT_LATITUDE)) \
01252                                   == WAITING_LATITUDE)
01253 #define TEST_IS_LONGITUDE(state_) (((state_) & (WAITING_LONGITUDE | GOT_LONGITUDE)) \
01254                                    == WAITING_LONGITUDE)
01255 #define TEST_IS_ELEVATION(state_) (((state_) & (WAITING_ELEVATION | GOT_ELEVATION)) \
01256                                    == WAITING_ELEVATION)
01257 #define TEST_IS_TIMESTAMP(state_) (((state_) & (WAITING_TIMESTAMP | GOT_TIMESTAMP)) \
01258                                    == WAITING_TIMESTAMP)
01259 
01260 static void on_location_key_found(jsonlite_callback_context* context,
01261                                   jsonlite_token* token) {
01262   location_parsing_context_state* state =
01263       (location_parsing_context_state*) context->client_state;
01264   if (strncmp((const char*) token->start, "waypoints", 9) == 0) {
01265     // only parses those locations in waypoints, skip the outer one
01266     state->state = 0;
01267   } else if (strncmp((const char*) token->start, "name", 4) == 0) {
01268     state->state |= WAITING_NAME;
01269   } else if (strncmp((const char*) token->start, "latitude", 8) == 0) {
01270     state->state |= WAITING_LATITUDE;
01271   } else if (strncmp((const char*) token->start, "longitude", 9) == 0) {
01272     state->state |= WAITING_LONGITUDE;
01273   } else if (strncmp((const char*) token->start, "elevation", 9) == 0) {
01274     state->state |= WAITING_ELEVATION;
01275   } else if (strncmp((const char*) token->start, "timestamp", 9) == 0) {
01276     state->state |= WAITING_TIMESTAMP;
01277   }
01278 }
01279 
01280 static void on_location_string_found(jsonlite_callback_context* context,
01281                                      jsonlite_token* token) {
01282   location_parsing_context_state* state =
01283       (location_parsing_context_state*) context->client_state;
01284 
01285   if (TEST_IS_NAME(state->state)) {
01286     strncpy(state->name_str, (const char*) token->start,
01287             MIN(token->end - token->start, LOCATION_BUF_LEN));
01288     state->name_str[MIN(token->end - token->start, LOCATION_BUF_LEN)] = '\0';
01289     state->state |= GOT_NAME;
01290   } else if (TEST_IS_LATITUDE(state->state)) {
01291     state->latitude = atof((const char*) token->start);
01292     state->state |= GOT_LATITUDE;
01293   } else if (TEST_IS_LONGITUDE(state->state)) {
01294     state->longitude = atof((const char*) token->start);
01295     state->state |= GOT_LONGITUDE;
01296   } else if (TEST_IS_ELEVATION(state->state)) {
01297     state->elevation = atof((const char*) token->start);
01298     state->state |= GOT_ELEVATION;
01299   } else if (TEST_IS_TIMESTAMP(state->state)) {
01300     strncpy(state->timestamp_str, (const char*) token->start,
01301             MIN(token->end - token->start, LOCATION_BUF_LEN));
01302     state->timestamp_str[MIN(token->end - token->start, LOCATION_BUF_LEN)] = '\0';
01303     state->state |= GOT_TIMESTAMP;
01304   }
01305 
01306   if (TEST_GOT_LOCATION(state->state)) {
01307     state->callback(state->name_str, state->latitude, state->longitude,
01308                     state->elevation, state->timestamp_str, state->index++,
01309                     state->context);
01310     state->state = 0;
01311   }
01312 }
01313 
01314 #ifndef M2X_COMMAND_BUF_LEN
01315 #define M2X_COMMAND_BUF_LEN 40
01316 #endif  /* M2X_COMMAND_BUF_LEN */
01317 
01318 typedef struct {
01319   uint8_t state;
01320   char id_str[M2X_COMMAND_BUF_LEN + 1];
01321   char name_str[M2X_COMMAND_BUF_LEN + 1];
01322   int index;
01323 
01324   m2x_command_read_callback callback;
01325   void* context;
01326 } m2x_command_parsing_context_state;
01327 
01328 #define M2X_COMMAND_WAITING_ID 0x1
01329 #define M2X_COMMAND_GOT_ID 0x2
01330 #define M2X_COMMAND_WAITING_NAME 0x4
01331 #define M2X_COMMAND_GOT_NAME 0x8
01332 
01333 #define M2X_COMMAND_GOT_COMMAND (M2X_COMMAND_GOT_ID | M2X_COMMAND_GOT_NAME)
01334 #define M2X_COMMAND_TEST_GOT_COMMAND(state_) \
01335   (((state_) & M2X_COMMAND_GOT_COMMAND) == M2X_COMMAND_GOT_COMMAND)
01336 
01337 #define M2X_COMMAND_TEST_IS_ID(state_) \
01338   (((state_) & (M2X_COMMAND_WAITING_ID | M2X_COMMAND_GOT_ID)) == \
01339    M2X_COMMAND_WAITING_ID)
01340 #define M2X_COMMAND_TEST_IS_NAME(state_) \
01341   (((state_) & (M2X_COMMAND_WAITING_NAME | M2X_COMMAND_GOT_NAME)) == \
01342    M2X_COMMAND_WAITING_NAME)
01343 
01344 static void m2x_on_command_key_found(jsonlite_callback_context* context,
01345                                      jsonlite_token* token)
01346 {
01347   m2x_command_parsing_context_state* state =
01348       (m2x_command_parsing_context_state*) context->client_state;
01349   if (strncmp((const char*) token->start, "id", 2) == 0) {
01350     state->state |= M2X_COMMAND_WAITING_ID;
01351   } else if (strncmp((const char*) token->start, "name", 4) == 0) {
01352     state->state |= M2X_COMMAND_WAITING_NAME;
01353   }
01354 }
01355 
01356 static void m2x_on_command_value_found(jsonlite_callback_context* context,
01357                                        jsonlite_token* token)
01358 {
01359   m2x_command_parsing_context_state* state =
01360       (m2x_command_parsing_context_state*) context->client_state;
01361   if (M2X_COMMAND_TEST_IS_ID(state->state)) {
01362     strncpy(state->id_str, (const char*) token->start,
01363             MIN(token->end - token->start, M2X_COMMAND_BUF_LEN));
01364     state->id_str[MIN(token->end - token->start, M2X_COMMAND_BUF_LEN)] = '\0';
01365     state->state |= M2X_COMMAND_GOT_ID;
01366   } else if (M2X_COMMAND_TEST_IS_NAME(state->state)) {
01367     strncpy(state->name_str, (const char*) token->start,
01368             MIN(token->end - token->start, M2X_COMMAND_BUF_LEN));
01369     state->name_str[MIN(token->end - token->start, M2X_COMMAND_BUF_LEN)] = '\0';
01370     state->state |= M2X_COMMAND_GOT_NAME;
01371   }
01372 
01373   if (M2X_COMMAND_TEST_GOT_COMMAND(state->state)) {
01374     state->callback(state->id_str, state->name_str,
01375                     state->index++, state->context);
01376     state->state = 0;
01377   }
01378 }
01379 
01380 int M2XStreamClient::listStreamValues(const char* deviceId, const char* streamName,
01381                                       stream_value_read_callback callback, void* context,
01382                                       const char* query) {
01383   if (_client->connect(_host, _port)) {
01384     DBGLN("%s", "Connected to M2X server!");
01385     _client->print("GET ");
01386     if (_path_prefix) { _client->print(_path_prefix); }
01387     _client->print("/v2/devices/");
01388     _client->print(deviceId);
01389     _client->print("/streams/");
01390     print_encoded_string(_client, streamName);
01391     _client->print("/values");
01392 
01393     if (query) {
01394       if (query[0] != '?') {
01395         _client->print('?');
01396       }
01397       _client->print(query);
01398     }
01399 
01400     _client->println(" HTTP/1.1");
01401     writeHttpHeader(-1);
01402   } else {
01403     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
01404     return E_NOCONNECTION;
01405   }
01406   int status = readStatusCode(false);
01407   if (status == 200) {
01408     readStreamValue(callback, context);
01409   }
01410 
01411   close();
01412   return status;
01413 }
01414 
01415 int M2XStreamClient::readLocation(const char* deviceId,
01416                                   location_read_callback callback,
01417                                   void* context) {
01418   if (_client->connect(_host, _port)) {
01419     DBGLN("%s", "Connected to M2X server!");
01420     _client->print("GET ");
01421     if (_path_prefix) { _client->print(_path_prefix); }
01422     _client->print("/v2/devices/");
01423     _client->print(deviceId);
01424     _client->println("/location HTTP/1.1");
01425 
01426     writeHttpHeader(-1);
01427   } else {
01428     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
01429     return E_NOCONNECTION;
01430   }
01431   int status = readStatusCode(false);
01432   if (status == 200) {
01433     readLocation(callback, context);
01434   }
01435 
01436   close();
01437   return status;
01438 }
01439 
01440 int M2XStreamClient::deleteLocations(const char* deviceId,
01441                                   const char* from, const char* end) {
01442   if (_client->connect(_host, _port)) {
01443     DBGLN("%s", "Connected to M2X server!");
01444     int length = write_delete_values(&_null_print, from, end);
01445     writeDeleteLocationHeader(deviceId, length);
01446     write_delete_values(_client, from, end);
01447   } else {
01448     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
01449     return E_NOCONNECTION;
01450   }
01451 
01452   return readStatusCode(true);
01453 }
01454 
01455 int M2XStreamClient::listCommands(const char* deviceId,
01456                                   m2x_command_read_callback callback,
01457                                   void* context,
01458                                   const char* query) {
01459   if (_client->connect(_host, _port)) {
01460     DBGLN("%s", "Connected to M2X server!");
01461     _client->print("GET ");
01462     if (_path_prefix) { _client->print(_path_prefix); }
01463     _client->print("/v2/devices/");
01464     _client->print(deviceId);
01465     _client->print("/commands");
01466 
01467     if (query) {
01468       if (query[0] != '?') {
01469         _client->print('?');
01470       }
01471       _client->print(query);
01472     }
01473 
01474     _client->println(" HTTP/1.1");
01475     writeHttpHeader(-1);
01476   } else {
01477     DBGLN("%s", "ERROR: Cannot connect to M2X server!");
01478     return E_NOCONNECTION;
01479   }
01480   int status = readStatusCode(false);
01481   if (status == 200) {
01482     readCommand(callback, context);
01483   }
01484 
01485   close();
01486   return status;
01487 }
01488 
01489 
01490 int M2XStreamClient::readStreamValue(stream_value_read_callback callback,
01491                                      void* context) {
01492   const int BUF_LEN = 64;
01493   char buf[BUF_LEN];
01494 
01495   int length = readContentLength();
01496   if (length < 0) {
01497     close();
01498     return length;
01499   }
01500 
01501   int index = skipHttpHeader();
01502   if (index != E_OK) {
01503     close();
01504     return index;
01505   }
01506   index = 0;
01507 
01508   stream_parsing_context_state state;
01509   state.state = state.index = 0;
01510   state.callback = callback;
01511   state.context = context;
01512 
01513   jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
01514   cbs.key_found = on_stream_key_found;
01515   cbs.number_found = on_stream_number_found;
01516   cbs.string_found = on_stream_string_found;
01517   cbs.context.client_state = &state;
01518 
01519   jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
01520   jsonlite_parser_set_callback(p, &cbs);
01521 
01522   jsonlite_result result = jsonlite_result_unknown;
01523   while (index < length) {
01524     int i = 0;
01525 
01526     DBG("%s", "Received Data: ");
01527     while ((i < BUF_LEN) && _client->available()) {
01528       buf[i++] = _client->read();
01529       DBG("%c", buf[i - 1]);
01530     }
01531     DBGLNEND;
01532 
01533     if ((!_client->connected()) &&
01534         (!_client->available()) &&
01535         ((index + i) < length)) {
01536       jsonlite_parser_release(p);
01537       close();
01538       return E_NOCONNECTION;
01539     }
01540 
01541     result = jsonlite_parser_tokenize(p, buf, i);
01542     if ((result != jsonlite_result_ok) &&
01543         (result != jsonlite_result_end_of_stream)) {
01544       jsonlite_parser_release(p);
01545       close();
01546       return E_JSON_INVALID;
01547     }
01548 
01549     index += i;
01550   }
01551 
01552   jsonlite_parser_release(p);
01553   close();
01554   return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
01555 }
01556 
01557 int M2XStreamClient::readLocation(location_read_callback callback,
01558                                   void* context) {
01559   const int BUF_LEN = 40;
01560   char buf[BUF_LEN];
01561 
01562   int length = readContentLength();
01563   if (length < 0) {
01564     close();
01565     return length;
01566   }
01567 
01568   int index = skipHttpHeader();
01569   if (index != E_OK) {
01570     close();
01571     return index;
01572   }
01573   index = 0;
01574 
01575   location_parsing_context_state state;
01576   state.state = state.index = 0;
01577   state.callback = callback;
01578   state.context = context;
01579 
01580   jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
01581   cbs.key_found = on_location_key_found;
01582   cbs.string_found = on_location_string_found;
01583   cbs.context.client_state = &state;
01584 
01585   jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
01586   jsonlite_parser_set_callback(p, &cbs);
01587 
01588   jsonlite_result result = jsonlite_result_unknown;
01589   while (index < length) {
01590     int i = 0;
01591 
01592     DBG("%s", "Received Data: ");
01593     while ((i < BUF_LEN) && _client->available()) {
01594       buf[i++] = _client->read();
01595       DBG("%c", buf[i - 1]);
01596     }
01597     DBGLNEND;
01598 
01599     if ((!_client->connected()) &&
01600         (!_client->available()) &&
01601         ((index + i) < length)) {
01602       jsonlite_parser_release(p);
01603       close();
01604       return E_NOCONNECTION;
01605     }
01606 
01607     result = jsonlite_parser_tokenize(p, buf, i);
01608     if ((result != jsonlite_result_ok) &&
01609         (result != jsonlite_result_end_of_stream)) {
01610       jsonlite_parser_release(p);
01611       close();
01612       return E_JSON_INVALID;
01613     }
01614 
01615     index += i;
01616   }
01617 
01618   jsonlite_parser_release(p);
01619   close();
01620   return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
01621 }
01622 
01623 int M2XStreamClient::readCommand(m2x_command_read_callback callback,
01624                                  void* context) {
01625   const int BUF_LEN = 60;
01626   char buf[BUF_LEN];
01627 
01628   int length = readContentLength();
01629   if (length < 0) {
01630     close();
01631     return length;
01632   }
01633 
01634   int index = skipHttpHeader();
01635   if (index != E_OK) {
01636     close();
01637     return index;
01638   }
01639   index = 0;
01640 
01641   m2x_command_parsing_context_state state;
01642   state.state = 0;
01643   state.index = 0;
01644   state.callback = callback;
01645   state.context = context;
01646 
01647   jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
01648   cbs.key_found = m2x_on_command_key_found;
01649   cbs.string_found = m2x_on_command_value_found;
01650   cbs.context.client_state = &state;
01651 
01652   jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
01653   jsonlite_parser_set_callback(p, &cbs);
01654 
01655   jsonlite_result result = jsonlite_result_unknown;
01656   while (index < length) {
01657     int i = 0;
01658 
01659     DBG("%s", "Received Data: ");
01660     while ((i < BUF_LEN) && _client->available()) {
01661       buf[i++] = _client->read();
01662       DBG("%c", buf[i - 1]);
01663     }
01664     DBGLNEND;
01665 
01666     if ((!_client->connected()) &&
01667         (!_client->available()) &&
01668         ((index + i) < length)) {
01669       jsonlite_parser_release(p);
01670       close();
01671       return E_NOCONNECTION;
01672     }
01673 
01674     result = jsonlite_parser_tokenize(p, buf, i);
01675     if ((result != jsonlite_result_ok) &&
01676         (result != jsonlite_result_end_of_stream)) {
01677       jsonlite_parser_release(p);
01678       close();
01679       return E_JSON_INVALID;
01680     }
01681 
01682     index += i;
01683   }
01684 
01685   jsonlite_parser_release(p);
01686   close();
01687   return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
01688 }
01689 
01690 #endif  /* M2X_ENABLE_READER */
01691 
01692 #endif  /* M2XStreamClient_h */