KPN IoT / senml

Fork of kpn_senml by KPN IoT

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers senml_pack.cpp Source File

senml_pack.cpp

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
00012  */
00013 
00014 #include <senml_pack.h>
00015 #include <senml_base.h>
00016 #include <senml_helpers.h>
00017 #include <senml_json_parser.h>
00018 #include <senml_cbor_parser.h>
00019 #include <senml_JsonStreamingParser.h>
00020 #include <math.h>
00021 #include <cbor.h>
00022 #include <senml_logging.h>
00023 
00024 
00025 
00026 //todo: inline these:
00027 void SenMLPack::setBaseName(const char* name)
00028 {
00029     this->_bn = name;
00030 }
00031 
00032 const char* SenMLPack::getBaseName()
00033 {
00034     return this->_bn.c_str();
00035 }
00036 
00037 void SenMLPack::setBaseUnit(SenMLUnit unit)
00038 {
00039     this->_bu = unit;
00040 }
00041 
00042 
00043 void SenMLPack::setBaseTime(double time)
00044 {
00045     double prev = this->_bt;
00046     this->_bt = time;                               //set before asking children -> could be better to do it afterwards?
00047     SenMLBase *item = this->_start;
00048     while(item){
00049         item->adjustToBaseTime(prev, time);
00050         item = item->getNext();
00051     }
00052 }
00053 
00054 
00055 void SenMLPack::setLast(SenMLBase* value)
00056 {
00057     if(value == this)                           //if we become the last item in the list, then the list is empty.
00058         this->_end = NULL;
00059     else
00060         this->_end = value;
00061 }
00062 
00063 
00064 bool SenMLPack::add(SenMLBase* item)
00065 {
00066     if(item->getNext() != NULL){
00067         log_debug("already in list");
00068         return false;
00069     }
00070 
00071     SenMLBase* last = this->_end;
00072     if(last){
00073         last->setNext(item);
00074         item->setPrev(last);
00075     }
00076     else{
00077         this->_start = item;
00078         item->setPrev(this);
00079     }
00080     this->_end = item;
00081     return true;
00082 }
00083 
00084 bool SenMLPack::clear()
00085 {
00086     SenMLBase *item = this->_start;
00087     while(item){
00088         if(item->isPack())                                          //if it's a pack element, it also needs to clear out it's children.
00089             ((SenMLPack*)item)->clear();
00090         item->setPrev(NULL);
00091         SenMLBase *next = item->getNext();
00092         item->setNext(NULL);
00093         item = next;
00094     }
00095     this->setNext(NULL);
00096     this->setPrev(NULL);
00097     this->_end = NULL;
00098     this->_start = NULL;
00099     return true;
00100 }
00101 
00102 void SenMLPack::fromJson(Stream *source, SenMLStreamMethod format)
00103 {
00104     JsonStreamingParser parser;
00105     SenMLJsonListener listener(this);
00106     
00107     parser.setListener(&listener);
00108     char data;
00109     if(format == SENML_RAW) {
00110         #ifdef __MBED__
00111             data = source->getc();
00112         #else
00113             data = source->read();
00114         #endif
00115     }
00116     else{
00117         data = readHexChar(source);
00118     }
00119         
00120     while(data != -1){
00121         parser.parse(data); 
00122         if(format == SENML_RAW){
00123             #ifdef __MBED__
00124                 data = source->getc();
00125             #else
00126                 data = source->read();
00127             #endif
00128         }   
00129         else
00130             data = readHexChar(source);
00131     }
00132     // when we get here, all the data is stored in the document and callbacks have been called.
00133 }
00134 
00135 void SenMLPack::fromJson(const char *source)
00136 {
00137     JsonStreamingParser parser;
00138     SenMLJsonListener listener(this);
00139     
00140     parser.setListener(&listener);
00141     for(int i = 0; source[i] != 0; i++){
00142         parser.parse(source[i]); 
00143     }
00144     // when we get here, all the data is stored in the document and callbacks have been called.
00145 }
00146 
00147 void SenMLPack::fromCbor(Stream* source, SenMLStreamMethod format)
00148 {
00149     SenMLCborParser parser(this, format);
00150     parser.parse(source);
00151 }
00152 
00153 void SenMLPack::fromCbor(char* source, int length, SenMLStreamMethod format)
00154 {
00155     SenMLCborParser parser(this, format);
00156     parser.parse(source, length);
00157 }
00158 
00159 
00160 void SenMLPack::toJson(Stream *dest, SenMLStreamMethod format)
00161 {
00162     StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
00163     _streamCtx = &renderTo;
00164     this->setupStreamCtx(dest, format);
00165     this->internalToJson();
00166 }
00167 
00168 void SenMLPack::toJson(char *dest, int length, SenMLStreamMethod format)
00169 {
00170     StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
00171     _streamCtx = &renderTo;
00172     this->setupStreamCtx(dest, length, format);
00173     this->internalToJson();
00174 }
00175 
00176 //render the content of the current object to json data (string)
00177 void SenMLPack::internalToJson()
00178 {
00179     printText("[", 1);
00180     this->contentToJson();
00181     printText("]", 1);
00182 }
00183 
00184 
00185 void SenMLPack::fieldsToJson()
00186 {
00187     int bnLength = this->_bn.length();
00188     if(bnLength > 0){
00189         printText("\"bn\":\"", 6);
00190         printText(this->_bn.c_str(), bnLength);
00191         printText("\"", 1);
00192     }
00193     if(this->_bu){
00194         printText(",\"bu\":\"", 7);
00195         printUnit(this->_bu);
00196         printText("\"", 1);
00197     }
00198     if(!isnan(this->_bt)){
00199         printText(",\"bt\":", 6);
00200         printDouble(this->_bt, SENML_MAX_DOUBLE_PRECISION);
00201     }
00202 }
00203 
00204 void SenMLPack::contentToJson()
00205 {
00206     printText("{", 1);
00207     this->fieldsToJson();
00208     SenMLBase *next = this->_start;
00209     if(next && next->isPack() == false){                        //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
00210         printText(",", 1);
00211         next->fieldsToJson();
00212         next = next->getNext();
00213     }
00214     printText("}", 1);
00215     while(next){
00216         printText(",", 1);
00217         next->contentToJson();
00218         next = next->getNext();
00219     }
00220 }
00221 
00222 
00223 
00224 void SenMLPack::setupStreamCtx(char *dest, int length, SenMLStreamMethod format)
00225 {
00226     _streamCtx->data.blob.data = dest;
00227     _streamCtx->data.blob.length = length;
00228     _streamCtx->data.blob.curPos = 0;
00229     _streamCtx->dataAsBlob = true;
00230     _streamCtx->format = format;
00231     _streamCtx->baseValue.baseUint = 0;                                    //by default, there is no base value or sum
00232     _streamCtx->baseSum.baseUint = 0;
00233     _streamCtx->baseDataType = CBOR_TYPE_DATA;                             //data never adjusts for basevalue, so this is safe.
00234 }
00235 
00236 void SenMLPack::setupStreamCtx(Stream *dest, SenMLStreamMethod format)
00237 {
00238     _streamCtx->data.stream = dest;
00239     _streamCtx->format = format;
00240     _streamCtx->dataAsBlob = false;
00241     _streamCtx->baseValue.baseUint = 0;                                    //by default, there is no base value or sum
00242     _streamCtx->baseSum.baseUint = 0;
00243     _streamCtx->baseDataType = CBOR_TYPE_DATA;                             //data never adjusts for basevalue, so this is safe.
00244 }
00245 
00246 int SenMLPack::toCbor(Stream *dest, SenMLStreamMethod format)
00247 {
00248     StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
00249     _streamCtx = &renderTo;
00250     this->setupStreamCtx(dest, format);
00251     int res = cbor_serialize_array(this->getArrayLength());
00252     res += this->contentToCbor();
00253     return res;
00254 }
00255 
00256 int SenMLPack::toCbor(char *dest, int length, SenMLStreamMethod format)
00257 {
00258     StreamContext renderTo;                                              //set up the global record that configures the rendering. This saves us some bytes on the stack and in code by not having to pass along the values as function arguments.
00259     _streamCtx = &renderTo;
00260     this->setupStreamCtx(dest, length, format);
00261     int res = cbor_serialize_array(this->getArrayLength());
00262     res += this->contentToCbor();
00263     return res;
00264 }
00265 
00266 int SenMLPack::contentToCbor()
00267 {
00268     int length = cbor_serialize_map(this->getFieldLength());
00269 
00270     int res = this->fieldsToCbor();
00271     SenMLBase *next = this->_start;
00272     if(next && next->isPack() == false){                        //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
00273         res += next->fieldsToCbor();
00274         next = next->getNext();
00275     }
00276 
00277     while(next){
00278         if(next->isPack() == false)
00279             res += next->contentToCbor();
00280         else
00281             res += ((SenMLPack*)next)->contentToCbor();
00282         next = next->getNext();
00283     }
00284     return res;
00285 }
00286 
00287 int SenMLPack::getArrayLength()
00288 {
00289     int result = 0;                             //init to 0 cause if there is a record, the first will become part of the first element in the array, if we were to init to 1, we would have 1 record too many.
00290     SenMLBase *next = this->_start;
00291     while(next){                                //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
00292         result += next->getArrayLength();       //custom record implementations may wrap multiple records.
00293         next = next->getNext();
00294     }
00295     if(result == 0)                             //if there are no items in this pack, then we still render 1 array element, that of the pack itself.
00296         result = 1;
00297     return result;
00298 }
00299 
00300 
00301 int SenMLPack::getFieldLength()
00302 {
00303     int result = 0;                             
00304     if(this->_bn.length() > 0 ) result++;
00305     if(this->_bu) result++;
00306     if(!isnan(this->_bt)) result++;
00307 
00308     SenMLBase *next = this->_start;
00309     if(next && next->isPack() == false){        //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
00310         result += next->getFieldLength();
00311         next = next->getNext();
00312     }
00313     return result;
00314 }
00315 
00316 int SenMLPack::fieldsToCbor()
00317 {
00318     int res = 0 ;
00319     if(this->_bn.length() > 0 ){
00320         res += cbor_serialize_int(SENML_CBOR_BN_LABEL);
00321         res += cbor_serialize_unicode_string(this->_bn.c_str());
00322     }
00323     if(this->_bu){
00324         res += cbor_serialize_int(SENML_CBOR_BU_LABEL);
00325         res += cbor_serialize_unicode_string(senml_units_names[this->_bu]);
00326     }
00327     if(!isnan(this->_bt)){
00328         res += cbor_serialize_int(SENML_CBOR_BT_LABEL);
00329         res += cbor_serialize_double(this->_bt);
00330     }
00331     return res;
00332 }
00333 
00334 
00335 
00336 
00337 
00338 
00339