Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of kpn_senml by
senml_pack.h
00001 /* _ __ ____ _ _ 00002 * | |/ / | _ \ | \ | | 00003 * | ' / | |_) | | \| | 00004 * | . \ | __/ | |\ | 00005 * |_|\_\ |_| |_| \_| 00006 * 00007 * (c) 2018 KPN 00008 * License: MIT License. 00009 * Author: Jan Bogaerts 00010 * 00011 * pack (document) without base values headers 00012 */ 00013 00014 #ifndef SENMLPACK 00015 #define SENMLPACK 00016 00017 #ifdef __MBED__ 00018 #include "mbed.h" 00019 #include <string> 00020 using namespace std; 00021 #define String string 00022 #endif 00023 00024 #include <senml_base.h> 00025 00026 00027 #define PACK_ACTUATOR_SIGNATURE void (*callback)(const char*, const char*, const void*, int, SenMLDataType) 00028 00029 /** 00030 * SenMLPack represents a single senml document that can be sent or received. 00031 * 00032 * A senmlPack object has SenMLRecords and/or other SenMLPack objects as children. These 00033 * represent the data that the object contains. 00034 * A SenMLRecord represents a single value, while child SenMLPacks represent data of other 00035 * devices. When a SenMLPack contains other SenMLPack objects, the root object represents a 00036 * gateway. 00037 * 00038 * A SenMLPack object is able to render and parse the data to/from a json string or to/from binary 00039 * CBOR data. Both rendering and parsing can be done either directly from a stream (like an UART 00040 * connected to a modem), or from a memory buffer. 00041 * Rendering to and parsing from a stream is useful for devices that have extreme low memory available. 00042 * Almost no buffers are used in this case. Error handling is limited in this case though. 00043 * The parser and generator are able to render in the native format (strings for json, binary data for 00044 * cbor) and as a hex string. This is especially useful when directly working on a stream: some modems 00045 * (ex lora) accept instructions with data in HEX format. 00046 * 00047 * example: 00048 * 00049 * @code 00050 * #include <kpn_senml.h> 00051 * 00052 * SenMLPack doc("device_name"); 00053 * 00054 * void setup(){ 00055 * Serial.begin(57600); 00056 * senMLSetLogger(&Serial); 00057 * delay(1000); 00058 * Serial.println("start"); 00059 * } 00060 00061 * void loop(){ 00062 * int val = 10; //just give it some value 00063 * SenMLFloatRecord rec("temp", SENML_UNIT_DEGREES_CELSIUS, val); 00064 * doc.add(&rec); 00065 * doc.toJson(&Serial); //as text 00066 * Serial.println(); 00067 * doc.toJson(&Serial, SENML_HEX); //in hex format (often used in communication with lora modems) 00068 * Serial.println(); 00069 * delay(1000); 00070 * } 00071 00072 * @endcode 00073 * 00074 */ 00075 class SenMLPack: public SenMLBase 00076 { 00077 friend class SenMLJsonListener; 00078 friend class SenMLCborParser; 00079 friend class SenMLBase; 00080 public: 00081 00082 /** 00083 * create a SenMLPack object. 00084 */ 00085 SenMLPack(): _bu(SENML_UNIT_NONE), 00086 _bt(NAN), 00087 _end(NULL) {}; 00088 00089 /** 00090 * create a SenMLPack object. 00091 * @param baseName the string that will be prepended to all records in this pack. 00092 * Is used to represent the name of the device. 00093 */ 00094 SenMLPack(const char* baseName): _bn(baseName), //mbed compiler doesnt support delegating constructors 00095 _bu(SENML_UNIT_NONE), 00096 _bt(NAN), 00097 _end(NULL) {}; 00098 00099 /** 00100 * create a SenMLPack object. 00101 * @param baseName the string that will be prepended to all records in this pack. 00102 * Is used to represent the name of the device. 00103 * @param baseUnit the unit name that will be used by default if the record doesnt 00104 * not define one. 00105 */ 00106 SenMLPack(const char* baseName, SenMLUnit baseUnit): _bn(baseName), 00107 _bu(baseUnit), 00108 _bt(NAN), 00109 _end(NULL) {}; 00110 00111 /** 00112 * create a SenMLPack object. 00113 * @param baseName the string that will be prepended to all records in this pack. 00114 * Is used to represent the name of the device. 00115 * @param baseUnit the unit name that will be used by default if the record doesnt 00116 * not define one. 00117 * @param baseTime the time that will be added to each record. When specified, each 00118 * record that does not specify a time, will receive this time. When 00119 * the record does have a time, the baseTime of the pack is added to it, 00120 * so the time of the record becomes relative to that of the pack. 00121 */ 00122 SenMLPack(const char* baseName, SenMLUnit baseUnit, double baseTime): _bn(baseName), 00123 _bu(baseUnit), 00124 _bt(baseTime), 00125 _end(NULL) {}; 00126 00127 /** 00128 * create a SenMLPack object. 00129 * @param callback a function that will be called while parsing incomming data, when no 00130 * record can be found that matches any of the defined ones in the object. 00131 * The parameters of the callback must be: 00132 * const char* packName the name of the pack that the record belongs to. 00133 * The data is for a child SenMLPack when this 00134 * field is different then the name of the root pack. 00135 * const char* recordName the name of the record 00136 * const void* value a pointer to the memory blob that contains the actual value. 00137 * int size the size of the memory blobl 00138 * SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..) 00139 */ 00140 SenMLPack(PACK_ACTUATOR_SIGNATURE): _bn(""), 00141 _bu(SENML_UNIT_NONE), 00142 _bt(NAN), 00143 _end(NULL), 00144 callback(callback) {}; 00145 00146 /** 00147 * create a SenMLPack object. 00148 * @param baseName the string that will be prepended to all records in this pack. 00149 * Is used to represent the name of the device. 00150 * @param callback a function that will be called while parsing incomming data, when no 00151 * record can be found that matches any of the defined ones in the object. 00152 * The parameters of the callback must be: 00153 * const char* packName the name of the pack that the record belongs to. 00154 * The data is for a child SenMLPack when this 00155 * field is different then the name of the root pack. 00156 * const char* recordName the name of the record 00157 * const void* value a pointer to the memory blob that contains the actual value. 00158 * int size the size of the memory blobl 00159 * SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..) 00160 */ 00161 SenMLPack(const char* baseName, PACK_ACTUATOR_SIGNATURE): _bn(baseName), 00162 _bu(SENML_UNIT_NONE), 00163 _bt(NAN), 00164 _end(NULL), 00165 callback(callback) {}; 00166 00167 /** 00168 * create a SenMLPack object. 00169 * @param baseName the string that will be prepended to all records in this pack. 00170 * Is used to represent the name of the device. 00171 * @param baseUnit the unit name that will be used by default if the record doesnt 00172 * not define one. 00173 * @param callback a function that will be called while parsing incomming data, when no 00174 * record can be found that matches any of the defined ones in the object. 00175 * The parameters of the callback must be: 00176 * const char* packName the name of the pack that the record belongs to. 00177 * The data is for a child SenMLPack when this 00178 * field is different then the name of the root pack. 00179 * const char* recordName the name of the record 00180 * const void* value a pointer to the memory blob that contains the actual value. 00181 * int size the size of the memory blobl 00182 * SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..) 00183 */ 00184 SenMLPack(const char* baseName, SenMLUnit baseUnit, PACK_ACTUATOR_SIGNATURE): _bn(baseName), 00185 _bu(baseUnit), 00186 _bt(NAN), 00187 _end(NULL), 00188 callback(callback) {}; 00189 00190 /** 00191 * create a SenMLPack object. 00192 * @param baseName the string that will be prepended to all records in this pack. 00193 * Is used to represent the name of the device. 00194 * @param baseUnit the unit name that will be used by default if the record doesnt 00195 * not define one. 00196 * @param baseTime the time that will be added to each record. When specified, each 00197 * record that does not specify a time, will receive this time. When 00198 * the record does have a time, the baseTime of the pack is added to it, 00199 * so the time of the record becomes relative to that of the pack. 00200 * @param callback a function that will be called while parsing incomming data, when no 00201 * record can be found that matches any of the defined ones in the object. 00202 * The parameters of the callback must be: 00203 * const char* packName the name of the pack that the record belongs to. 00204 * The data is for a child SenMLPack when this 00205 * field is different then the name of the root pack. 00206 * const char* recordName the name of the record 00207 * const void* value a pointer to the memory blob that contains the actual value. 00208 * int size the size of the memory blobl 00209 * SenMLDataType dataType: defines how to interprete the memory blob (ex: pointer to integer,..) 00210 */ 00211 SenMLPack(const char* baseName, SenMLUnit baseUnit, double baseTime, PACK_ACTUATOR_SIGNATURE): _bn(baseName), 00212 _bu(baseUnit), 00213 _bt(baseTime), 00214 _end(NULL), 00215 callback(callback) {}; 00216 00217 /** 00218 * destroys the SenMLPack object. 00219 */ 00220 ~SenMLPack(){}; 00221 00222 /** 00223 * render the content of the current object to json data (string). 00224 * This function is ideal for devices with low memory usage but offers less control over the rendering process. 00225 * @param dest the destination stream to where the data will be rendered without buffering it in memory 00226 * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods. 00227 * @returns none 00228 */ 00229 void toJson(Stream* dest, SenMLStreamMethod format=SENML_RAW); 00230 00231 /** 00232 * render the content of the current object to json data (string). 00233 * This function renders the data to a memory buffer. If the buffer is full before the entire object is 00234 * rendered, an error will be written to the debug stream. 00235 * @param dest a memory buffer to which the data will be rendred. 00236 * @param length the length of the memory buffer. 00237 * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods. 00238 * @returns none 00239 */ 00240 void toJson(char *dest, int length, SenMLStreamMethod format=SENML_RAW); 00241 00242 /** 00243 * render the content of the current object to cbor data (binary). 00244 * This function is ideal for devices with low memory usage but offers less control over the rendering process. 00245 * @param dest the destination stream to where the data will be rendered without buffering it in memory 00246 * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods. 00247 * @returns nr of bytes that were rendered 00248 */ 00249 int toCbor(Stream* dest, SenMLStreamMethod format=SENML_RAW); 00250 00251 /** 00252 * render the content of the current object to cbor data (binary). 00253 * This function renders the data to a memory buffer. If the buffer is full before the entire object is 00254 * rendered, an error will be written to the debug stream. 00255 * @param dest a memory buffer to which the data will be rendred. 00256 * @param length the length of the memory buffer. 00257 * @param format determins how the data will be rendered. See SenMLStreamMethod for possible methods. 00258 * @returns nr of bytes that were rendered 00259 */ 00260 int toCbor(char *dest, int length, SenMLStreamMethod format=SENML_RAW); 00261 00262 /** 00263 * read and parse a senml json string from the specified source and, for each registered actuator, call the 00264 * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 00265 * will be called, if present. 00266 * This method is ideal for devices with very littel ram memory. It will block on most devices if there is 00267 * no more input to be read from the stream and the end of the json structure is not yet reached. 00268 * Note: on mbed systems, the blocking nature is not garanteed. Instead, if no more data is available before 00269 * the end is reached, parsing will fail. 00270 * @param source the source stream to read the data from. 00271 * @param format determins how the data will be read (ex: as normal text or in HEX format). 00272 * See SenMLStreamMethod for possible methods. 00273 * @returns none 00274 */ 00275 void fromJson(Stream* source, SenMLStreamMethod format=SENML_RAW); 00276 00277 /** 00278 * parse a senml json string from the specified source and, for each registered actuator, call the 00279 * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 00280 * will be called, if present. 00281 * This method takes a string stored in memory as input. The json must be fully defined. It is up to the 00282 * caller to transform it to a regular text string, if needed (ex: lora devices might send it in hex format). 00283 * @param source the source string to use as input. This must be null terminated. 00284 * @returns none 00285 */ 00286 void fromJson(const char* source); 00287 00288 /** 00289 * read and parse senml cbor from the specified source and, for each registered actuator, call the 00290 * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 00291 * will be called, if present. 00292 * This method is ideal for devices with very littel ram memory. It will block on most decices if there is no 00293 * more input to be read from the stream and the end of the cbor structure is not yet reached. 00294 * Note: on mbed systems, the blocking nature is not garanteed. Instead, if no more data is available before 00295 * the end is reached, parsing will fail. 00296 * @param source the source stream to read the data from. 00297 * @param format determins how the data will be read (ex: as normal binary or in HEX format). 00298 * See SenMLStreamMethod for possible methods. 00299 * @returns none 00300 */ 00301 void fromCbor(Stream* source, SenMLStreamMethod format=SENML_RAW); 00302 00303 /** 00304 * parse senml cbor from the specified memory and, for each registered actuator, call the 00305 * appropriate event on the actuator itself, for others, the callback function PACK_ACTUATOR_SIGNATURE 00306 * will be called, if present. 00307 * This method takes a memory blob as input. The data must be fully defined. 00308 * @param source the source data to use as input. 00309 * @param length the length of the source data. 00310 * @param format determins how the data will be read (ex: as normal binary or in HEX format). 00311 * See SenMLStreamMethod for possible methods. 00312 * @returns none 00313 */ 00314 void fromCbor(char* source, int length, SenMLStreamMethod format); 00315 00316 /** 00317 * assign a basename to the SenMLPack object. This represents the name of the device. 00318 * see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info. 00319 * Every SenMLPack object must have a basename. This field will always be rendered in the output, even if the 00320 * string is empty. 00321 * @param name an immutable string that will be used to represent the name of the device. An internal 00322 * copy of the value will be made. 00323 * @returns none 00324 */ 00325 void setBaseName(const char* name); 00326 00327 /** 00328 * Get the base name. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info. 00329 * @returns the name of the device as an immutable string. 00330 */ 00331 const char* getBaseName(); 00332 00333 /** 00334 * Set the base unit that will be used as the default unit for all records that don't define their own unit. 00335 * see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info. 00336 * Set to SENML_UNIT_NONE for ommiting the base unit from the output (default). 00337 * @param unit the unit to use as default. See SenMLUnit for all supported unit names. 00338 * @returns none 00339 */ 00340 void setBaseUnit(SenMLUnit unit); 00341 00342 /** 00343 * Get the base unit. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info. 00344 * @returns a SenMLUnit enum value that is used as the default unit for records that don't define a unit of their own. 00345 */ 00346 inline SenMLUnit getBaseUnit() { return this->_bu; }; 00347 00348 00349 /** 00350 * Set the base time. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info. 00351 * @param time the value to use as base time. set bt to NaN if the field should not be included in the output. 00352 * @returns none 00353 */ 00354 void setBaseTime(double time); 00355 00356 /** 00357 * Get the base time. see the spec on [base fields](https://tools.ietf.org/html/draft-ietf-core-senml-13#section-4.1) for more info. 00358 * @returns a double value that is used as the default unit for records that don't define a unit of their own. 00359 * if no base time is set, NaN will be returned. 00360 */ 00361 inline double getBaseTime() { return this->_bt; }; 00362 00363 /** 00364 * Adds the specified SenML object to the document. The item will be appended to the end of the linked list. 00365 * The item being added, can be a regular SenMLRecord or another SenMLPack object if you want to send data 00366 * for multiple devices in 1 SenML message. 00367 * Check the result of the function to see if the operation was successful or not. Possible reasons for failure: 00368 * - if the item being added is already part of a document. 00369 * @param item a pointer to a SenMlRecord or SenMLPack that needs to be added to the document. 00370 * @returns true upon success, otherwise false. 00371 */ 00372 bool add(SenMLBase* item); 00373 00374 /** 00375 * Clear out the document and remove all the children. Children aren't destroyed, this is up to the developer. 00376 * @returns true (at the moment, the function does not yet return false as it doesn't detect any errors) 00377 */ 00378 bool clear(); 00379 00380 /** 00381 * get the first recrod of in this pack element. 00382 * @returns null when this object is empty (has no children), otherwise, the first item (SenMLRecord or SenMLPack) 00383 * of the list. 00384 */ 00385 inline SenMLBase* getFirst() { return this->_start; }; 00386 00387 00388 /** 00389 * renders all the fields to json, without the starting and ending brackets. 00390 * Inheriters can extend this function if they want to add extra fields to the json output 00391 * note: this is public so that custom implementations for the record object can use other objects 00392 * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for lat, lon & alt. 00393 * @returns: None 00394 */ 00395 virtual void fieldsToJson(); 00396 00397 /** 00398 * renders all the fields to cbor format. renders all the fields of the object without the length info 00399 * at the beginning 00400 * note: this is public so that custom implementations for the record object can use other objects 00401 * internally and render to json using this function (ex: coordinatesRecord using 3 floatRecrods for 00402 * lat, lon & alt. 00403 * @returns: The number of bytes that were written. 00404 */ 00405 virtual int fieldsToCbor(); 00406 00407 protected: 00408 00409 00410 //derived classes can use this function to see if the root object (getRoot) is a SenMLPack 00411 //class or not. 00412 virtual bool isPack() { return true; } 00413 00414 //when the user did not register a specific record actuator, this will be called. 00415 inline void actuate(const char* pack, const char* record, const void* value, int valueLength, SenMLDataType dataType) 00416 { 00417 if(this->callback) 00418 this->callback(pack, record, value, valueLength, dataType); 00419 }; 00420 00421 //store a ref to the last item in the list for quick link operations 00422 void setLast(SenMLBase* value); 00423 00424 //renders the content of the pack object without [] 00425 virtual int contentToCbor(); 00426 00427 00428 //calculates the nr of items that there will be in the json array in senml representation 00429 //this is used for rendering cbor which needs to declare the nr of elements in an array. 00430 virtual int getArrayLength(); 00431 00432 virtual void setupStreamCtx(char *dest, int length, SenMLStreamMethod format); 00433 00434 virtual void setupStreamCtx(Stream *dest, SenMLStreamMethod format); 00435 00436 private: 00437 String _bn; 00438 SenMLUnit _bu; 00439 double _bt; 00440 SenMLBase *_end; //keeps track of the end of the list 00441 SenMLBase *_start; //keeps track of the start of the list 00442 PACK_ACTUATOR_SIGNATURE; //for registering actuator callbacks. 00443 00444 00445 //renders the content of the pack object without [] 00446 virtual void contentToJson(); 00447 00448 //calculates the nr of json fields that this object uses in a senml structure 00449 virtual int getFieldLength(); 00450 00451 00452 void internalToJson(); 00453 00454 inline char readHexChar(Stream *source){ 00455 #ifdef __MBED__ 00456 unsigned char first = source->getc(); 00457 unsigned char second = source->getc(); 00458 #else 00459 unsigned char first = source->read(); 00460 unsigned char second = source->read(); 00461 #endif 00462 first = (first < '9') ? first - '0' : first - '7'; 00463 second = (second < '9') ? second - '0' : second - '7'; 00464 return (16 * first) + second; 00465 }; 00466 }; 00467 00468 00469 #endif // SENMLPACK 00470 00471 00472 00473 00474 00475 00476 00477
Generated on Tue Jul 12 2022 23:07:22 by
