KPN IoT / senml

Fork of kpn_senml by KPN IoT

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers senml_pack.h Source File

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