The KPN SenML library helps you create and parse senml documents in both json and cbor format. The library can be used for sending sensor data and receiving actuator commands.

Fork of kpn_senml by KPN IoT

Committer:
kpniot
Date:
Sun May 27 14:31:44 2018 +0000
Revision:
2:9b44be6e79ac
Parent:
0:a9259748d982
try to fix repo

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kpniot 0:a9259748d982 1 /* _ __ ____ _ _
kpniot 0:a9259748d982 2 * | |/ / | _ \ | \ | |
kpniot 0:a9259748d982 3 * | ' / | |_) | | \| |
kpniot 0:a9259748d982 4 * | . \ | __/ | |\ |
kpniot 0:a9259748d982 5 * |_|\_\ |_| |_| \_|
kpniot 0:a9259748d982 6 *
kpniot 0:a9259748d982 7 * (c) 2018 KPN
kpniot 0:a9259748d982 8 * License: MIT License.
kpniot 0:a9259748d982 9 * Author: Jan Bogaerts
kpniot 0:a9259748d982 10 *
kpniot 0:a9259748d982 11 * pack (document) without base values
kpniot 0:a9259748d982 12 */
kpniot 0:a9259748d982 13
kpniot 0:a9259748d982 14 #include <senml_pack.h>
kpniot 0:a9259748d982 15 #include <senml_base.h>
kpniot 0:a9259748d982 16 #include <senml_helpers.h>
kpniot 0:a9259748d982 17 #include <senml_json_parser.h>
kpniot 0:a9259748d982 18 #include <senml_cbor_parser.h>
kpniot 0:a9259748d982 19 #include <senml_JsonStreamingParser.h>
kpniot 0:a9259748d982 20 #include <math.h>
kpniot 0:a9259748d982 21 #include <cbor.h>
kpniot 0:a9259748d982 22 #include <senml_logging.h>
kpniot 0:a9259748d982 23
kpniot 0:a9259748d982 24
kpniot 0:a9259748d982 25
kpniot 0:a9259748d982 26 //todo: inline these:
kpniot 0:a9259748d982 27 void SenMLPack::setBaseName(const char* name)
kpniot 0:a9259748d982 28 {
kpniot 0:a9259748d982 29 this->_bn = name;
kpniot 0:a9259748d982 30 }
kpniot 0:a9259748d982 31
kpniot 0:a9259748d982 32 const char* SenMLPack::getBaseName()
kpniot 0:a9259748d982 33 {
kpniot 0:a9259748d982 34 return this->_bn.c_str();
kpniot 0:a9259748d982 35 }
kpniot 0:a9259748d982 36
kpniot 0:a9259748d982 37 void SenMLPack::setBaseUnit(SenMLUnit unit)
kpniot 0:a9259748d982 38 {
kpniot 0:a9259748d982 39 this->_bu = unit;
kpniot 0:a9259748d982 40 }
kpniot 0:a9259748d982 41
kpniot 0:a9259748d982 42
kpniot 0:a9259748d982 43 void SenMLPack::setBaseTime(double time)
kpniot 0:a9259748d982 44 {
kpniot 0:a9259748d982 45 double prev = this->_bt;
kpniot 0:a9259748d982 46 this->_bt = time; //set before asking children -> could be better to do it afterwards?
kpniot 0:a9259748d982 47 SenMLBase *item = this->_start;
kpniot 0:a9259748d982 48 while(item){
kpniot 0:a9259748d982 49 item->adjustToBaseTime(prev, time);
kpniot 0:a9259748d982 50 item = item->getNext();
kpniot 0:a9259748d982 51 }
kpniot 0:a9259748d982 52 }
kpniot 0:a9259748d982 53
kpniot 0:a9259748d982 54
kpniot 0:a9259748d982 55 void SenMLPack::setLast(SenMLBase* value)
kpniot 0:a9259748d982 56 {
kpniot 0:a9259748d982 57 if(value == this) //if we become the last item in the list, then the list is empty.
kpniot 0:a9259748d982 58 this->_end = NULL;
kpniot 0:a9259748d982 59 else
kpniot 0:a9259748d982 60 this->_end = value;
kpniot 0:a9259748d982 61 }
kpniot 0:a9259748d982 62
kpniot 0:a9259748d982 63
kpniot 0:a9259748d982 64 bool SenMLPack::add(SenMLBase* item)
kpniot 0:a9259748d982 65 {
kpniot 0:a9259748d982 66 if(item->getNext() != NULL){
kpniot 0:a9259748d982 67 log_debug("already in list");
kpniot 0:a9259748d982 68 return false;
kpniot 0:a9259748d982 69 }
kpniot 0:a9259748d982 70
kpniot 0:a9259748d982 71 SenMLBase* last = this->_end;
kpniot 0:a9259748d982 72 if(last){
kpniot 0:a9259748d982 73 last->setNext(item);
kpniot 0:a9259748d982 74 item->setPrev(last);
kpniot 0:a9259748d982 75 }
kpniot 0:a9259748d982 76 else{
kpniot 0:a9259748d982 77 this->_start = item;
kpniot 0:a9259748d982 78 item->setPrev(this);
kpniot 0:a9259748d982 79 }
kpniot 0:a9259748d982 80 this->_end = item;
kpniot 0:a9259748d982 81 return true;
kpniot 0:a9259748d982 82 }
kpniot 0:a9259748d982 83
kpniot 0:a9259748d982 84 bool SenMLPack::clear()
kpniot 0:a9259748d982 85 {
kpniot 0:a9259748d982 86 SenMLBase *item = this->_start;
kpniot 0:a9259748d982 87 while(item){
kpniot 0:a9259748d982 88 if(item->isPack()) //if it's a pack element, it also needs to clear out it's children.
kpniot 0:a9259748d982 89 ((SenMLPack*)item)->clear();
kpniot 0:a9259748d982 90 item->setPrev(NULL);
kpniot 0:a9259748d982 91 SenMLBase *next = item->getNext();
kpniot 0:a9259748d982 92 item->setNext(NULL);
kpniot 0:a9259748d982 93 item = next;
kpniot 0:a9259748d982 94 }
kpniot 0:a9259748d982 95 this->setNext(NULL);
kpniot 0:a9259748d982 96 this->setPrev(NULL);
kpniot 0:a9259748d982 97 this->_end = NULL;
kpniot 0:a9259748d982 98 this->_start = NULL;
kpniot 0:a9259748d982 99 return true;
kpniot 0:a9259748d982 100 }
kpniot 0:a9259748d982 101
kpniot 0:a9259748d982 102 void SenMLPack::fromJson(Stream *source, SenMLStreamMethod format)
kpniot 0:a9259748d982 103 {
kpniot 0:a9259748d982 104 JsonStreamingParser parser;
kpniot 0:a9259748d982 105 SenMLJsonListener listener(this);
kpniot 0:a9259748d982 106
kpniot 0:a9259748d982 107 parser.setListener(&listener);
kpniot 0:a9259748d982 108 char data;
kpniot 0:a9259748d982 109 if(format == SENML_RAW) {
kpniot 0:a9259748d982 110 #ifdef __MBED__
kpniot 0:a9259748d982 111 data = source->getc();
kpniot 0:a9259748d982 112 #else
kpniot 0:a9259748d982 113 data = source->read();
kpniot 0:a9259748d982 114 #endif
kpniot 0:a9259748d982 115 }
kpniot 0:a9259748d982 116 else{
kpniot 0:a9259748d982 117 data = readHexChar(source);
kpniot 0:a9259748d982 118 }
kpniot 0:a9259748d982 119
kpniot 0:a9259748d982 120 while(data != -1){
kpniot 0:a9259748d982 121 parser.parse(data);
kpniot 0:a9259748d982 122 if(format == SENML_RAW){
kpniot 0:a9259748d982 123 #ifdef __MBED__
kpniot 0:a9259748d982 124 data = source->getc();
kpniot 0:a9259748d982 125 #else
kpniot 0:a9259748d982 126 data = source->read();
kpniot 0:a9259748d982 127 #endif
kpniot 0:a9259748d982 128 }
kpniot 0:a9259748d982 129 else
kpniot 0:a9259748d982 130 data = readHexChar(source);
kpniot 0:a9259748d982 131 }
kpniot 0:a9259748d982 132 // when we get here, all the data is stored in the document and callbacks have been called.
kpniot 0:a9259748d982 133 }
kpniot 0:a9259748d982 134
kpniot 0:a9259748d982 135 void SenMLPack::fromJson(const char *source)
kpniot 0:a9259748d982 136 {
kpniot 0:a9259748d982 137 JsonStreamingParser parser;
kpniot 0:a9259748d982 138 SenMLJsonListener listener(this);
kpniot 0:a9259748d982 139
kpniot 0:a9259748d982 140 parser.setListener(&listener);
kpniot 0:a9259748d982 141 for(int i = 0; source[i] != 0; i++){
kpniot 0:a9259748d982 142 parser.parse(source[i]);
kpniot 0:a9259748d982 143 }
kpniot 0:a9259748d982 144 // when we get here, all the data is stored in the document and callbacks have been called.
kpniot 0:a9259748d982 145 }
kpniot 0:a9259748d982 146
kpniot 0:a9259748d982 147 void SenMLPack::fromCbor(Stream* source, SenMLStreamMethod format)
kpniot 0:a9259748d982 148 {
kpniot 0:a9259748d982 149 SenMLCborParser parser(this, format);
kpniot 0:a9259748d982 150 parser.parse(source);
kpniot 0:a9259748d982 151 }
kpniot 0:a9259748d982 152
kpniot 0:a9259748d982 153 void SenMLPack::fromCbor(char* source, int length, SenMLStreamMethod format)
kpniot 0:a9259748d982 154 {
kpniot 0:a9259748d982 155 SenMLCborParser parser(this, format);
kpniot 0:a9259748d982 156 parser.parse(source, length);
kpniot 0:a9259748d982 157 }
kpniot 0:a9259748d982 158
kpniot 0:a9259748d982 159
kpniot 0:a9259748d982 160 void SenMLPack::toJson(Stream *dest, SenMLStreamMethod format)
kpniot 0:a9259748d982 161 {
kpniot 0:a9259748d982 162 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.
kpniot 0:a9259748d982 163 _streamCtx = &renderTo;
kpniot 0:a9259748d982 164 this->setupStreamCtx(dest, format);
kpniot 0:a9259748d982 165 this->internalToJson();
kpniot 0:a9259748d982 166 }
kpniot 0:a9259748d982 167
kpniot 0:a9259748d982 168 void SenMLPack::toJson(char *dest, int length, SenMLStreamMethod format)
kpniot 0:a9259748d982 169 {
kpniot 0:a9259748d982 170 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.
kpniot 0:a9259748d982 171 _streamCtx = &renderTo;
kpniot 0:a9259748d982 172 this->setupStreamCtx(dest, length, format);
kpniot 0:a9259748d982 173 this->internalToJson();
kpniot 0:a9259748d982 174 }
kpniot 0:a9259748d982 175
kpniot 0:a9259748d982 176 //render the content of the current object to json data (string)
kpniot 0:a9259748d982 177 void SenMLPack::internalToJson()
kpniot 0:a9259748d982 178 {
kpniot 0:a9259748d982 179 printText("[", 1);
kpniot 0:a9259748d982 180 this->contentToJson();
kpniot 0:a9259748d982 181 printText("]", 1);
kpniot 0:a9259748d982 182 }
kpniot 0:a9259748d982 183
kpniot 0:a9259748d982 184
kpniot 0:a9259748d982 185 void SenMLPack::fieldsToJson()
kpniot 0:a9259748d982 186 {
kpniot 0:a9259748d982 187 int bnLength = this->_bn.length();
kpniot 0:a9259748d982 188 if(bnLength > 0){
kpniot 0:a9259748d982 189 printText("\"bn\":\"", 6);
kpniot 0:a9259748d982 190 printText(this->_bn.c_str(), bnLength);
kpniot 0:a9259748d982 191 printText("\"", 1);
kpniot 0:a9259748d982 192 }
kpniot 0:a9259748d982 193 if(this->_bu){
kpniot 0:a9259748d982 194 printText(",\"bu\":\"", 7);
kpniot 0:a9259748d982 195 printUnit(this->_bu);
kpniot 0:a9259748d982 196 printText("\"", 1);
kpniot 0:a9259748d982 197 }
kpniot 0:a9259748d982 198 if(!isnan(this->_bt)){
kpniot 0:a9259748d982 199 printText(",\"bt\":", 6);
kpniot 0:a9259748d982 200 printDouble(this->_bt, SENML_MAX_DOUBLE_PRECISION);
kpniot 0:a9259748d982 201 }
kpniot 0:a9259748d982 202 }
kpniot 0:a9259748d982 203
kpniot 0:a9259748d982 204 void SenMLPack::contentToJson()
kpniot 0:a9259748d982 205 {
kpniot 0:a9259748d982 206 printText("{", 1);
kpniot 0:a9259748d982 207 this->fieldsToJson();
kpniot 0:a9259748d982 208 SenMLBase *next = this->_start;
kpniot 0:a9259748d982 209 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.
kpniot 0:a9259748d982 210 printText(",", 1);
kpniot 0:a9259748d982 211 next->fieldsToJson();
kpniot 0:a9259748d982 212 next = next->getNext();
kpniot 0:a9259748d982 213 }
kpniot 0:a9259748d982 214 printText("}", 1);
kpniot 0:a9259748d982 215 while(next){
kpniot 0:a9259748d982 216 printText(",", 1);
kpniot 0:a9259748d982 217 next->contentToJson();
kpniot 0:a9259748d982 218 next = next->getNext();
kpniot 0:a9259748d982 219 }
kpniot 0:a9259748d982 220 }
kpniot 0:a9259748d982 221
kpniot 0:a9259748d982 222
kpniot 0:a9259748d982 223
kpniot 0:a9259748d982 224 void SenMLPack::setupStreamCtx(char *dest, int length, SenMLStreamMethod format)
kpniot 0:a9259748d982 225 {
kpniot 0:a9259748d982 226 _streamCtx->data.blob.data = dest;
kpniot 0:a9259748d982 227 _streamCtx->data.blob.length = length;
kpniot 0:a9259748d982 228 _streamCtx->data.blob.curPos = 0;
kpniot 0:a9259748d982 229 _streamCtx->dataAsBlob = true;
kpniot 0:a9259748d982 230 _streamCtx->format = format;
kpniot 0:a9259748d982 231 _streamCtx->baseValue.baseUint = 0; //by default, there is no base value or sum
kpniot 0:a9259748d982 232 _streamCtx->baseSum.baseUint = 0;
kpniot 0:a9259748d982 233 _streamCtx->baseDataType = CBOR_TYPE_DATA; //data never adjusts for basevalue, so this is safe.
kpniot 0:a9259748d982 234 }
kpniot 0:a9259748d982 235
kpniot 0:a9259748d982 236 void SenMLPack::setupStreamCtx(Stream *dest, SenMLStreamMethod format)
kpniot 0:a9259748d982 237 {
kpniot 0:a9259748d982 238 _streamCtx->data.stream = dest;
kpniot 0:a9259748d982 239 _streamCtx->format = format;
kpniot 0:a9259748d982 240 _streamCtx->dataAsBlob = false;
kpniot 0:a9259748d982 241 _streamCtx->baseValue.baseUint = 0; //by default, there is no base value or sum
kpniot 0:a9259748d982 242 _streamCtx->baseSum.baseUint = 0;
kpniot 0:a9259748d982 243 _streamCtx->baseDataType = CBOR_TYPE_DATA; //data never adjusts for basevalue, so this is safe.
kpniot 0:a9259748d982 244 }
kpniot 0:a9259748d982 245
kpniot 0:a9259748d982 246 int SenMLPack::toCbor(Stream *dest, SenMLStreamMethod format)
kpniot 0:a9259748d982 247 {
kpniot 0:a9259748d982 248 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.
kpniot 0:a9259748d982 249 _streamCtx = &renderTo;
kpniot 0:a9259748d982 250 this->setupStreamCtx(dest, format);
kpniot 0:a9259748d982 251 int res = cbor_serialize_array(this->getArrayLength());
kpniot 0:a9259748d982 252 res += this->contentToCbor();
kpniot 0:a9259748d982 253 return res;
kpniot 0:a9259748d982 254 }
kpniot 0:a9259748d982 255
kpniot 0:a9259748d982 256 int SenMLPack::toCbor(char *dest, int length, SenMLStreamMethod format)
kpniot 0:a9259748d982 257 {
kpniot 0:a9259748d982 258 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.
kpniot 0:a9259748d982 259 _streamCtx = &renderTo;
kpniot 0:a9259748d982 260 this->setupStreamCtx(dest, length, format);
kpniot 0:a9259748d982 261 int res = cbor_serialize_array(this->getArrayLength());
kpniot 0:a9259748d982 262 res += this->contentToCbor();
kpniot 0:a9259748d982 263 return res;
kpniot 0:a9259748d982 264 }
kpniot 0:a9259748d982 265
kpniot 0:a9259748d982 266 int SenMLPack::contentToCbor()
kpniot 0:a9259748d982 267 {
kpniot 0:a9259748d982 268 int length = cbor_serialize_map(this->getFieldLength());
kpniot 0:a9259748d982 269
kpniot 0:a9259748d982 270 int res = this->fieldsToCbor();
kpniot 0:a9259748d982 271 SenMLBase *next = this->_start;
kpniot 0:a9259748d982 272 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.
kpniot 0:a9259748d982 273 res += next->fieldsToCbor();
kpniot 0:a9259748d982 274 next = next->getNext();
kpniot 0:a9259748d982 275 }
kpniot 0:a9259748d982 276
kpniot 0:a9259748d982 277 while(next){
kpniot 0:a9259748d982 278 if(next->isPack() == false)
kpniot 0:a9259748d982 279 res += next->contentToCbor();
kpniot 0:a9259748d982 280 else
kpniot 0:a9259748d982 281 res += ((SenMLPack*)next)->contentToCbor();
kpniot 0:a9259748d982 282 next = next->getNext();
kpniot 0:a9259748d982 283 }
kpniot 0:a9259748d982 284 return res;
kpniot 0:a9259748d982 285 }
kpniot 0:a9259748d982 286
kpniot 0:a9259748d982 287 int SenMLPack::getArrayLength()
kpniot 0:a9259748d982 288 {
kpniot 0:a9259748d982 289 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.
kpniot 0:a9259748d982 290 SenMLBase *next = this->_start;
kpniot 0:a9259748d982 291 while(next){ //we can only inline the first record. If the first item is a Pack (child device), then don't inline it.
kpniot 0:a9259748d982 292 result += next->getArrayLength(); //custom record implementations may wrap multiple records.
kpniot 0:a9259748d982 293 next = next->getNext();
kpniot 0:a9259748d982 294 }
kpniot 0:a9259748d982 295 if(result == 0) //if there are no items in this pack, then we still render 1 array element, that of the pack itself.
kpniot 0:a9259748d982 296 result = 1;
kpniot 0:a9259748d982 297 return result;
kpniot 0:a9259748d982 298 }
kpniot 0:a9259748d982 299
kpniot 0:a9259748d982 300
kpniot 0:a9259748d982 301 int SenMLPack::getFieldLength()
kpniot 0:a9259748d982 302 {
kpniot 0:a9259748d982 303 int result = 0;
kpniot 0:a9259748d982 304 if(this->_bn.length() > 0 ) result++;
kpniot 0:a9259748d982 305 if(this->_bu) result++;
kpniot 0:a9259748d982 306 if(!isnan(this->_bt)) result++;
kpniot 0:a9259748d982 307
kpniot 0:a9259748d982 308 SenMLBase *next = this->_start;
kpniot 0:a9259748d982 309 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.
kpniot 0:a9259748d982 310 result += next->getFieldLength();
kpniot 0:a9259748d982 311 next = next->getNext();
kpniot 0:a9259748d982 312 }
kpniot 0:a9259748d982 313 return result;
kpniot 0:a9259748d982 314 }
kpniot 0:a9259748d982 315
kpniot 0:a9259748d982 316 int SenMLPack::fieldsToCbor()
kpniot 0:a9259748d982 317 {
kpniot 0:a9259748d982 318 int res = 0 ;
kpniot 0:a9259748d982 319 if(this->_bn.length() > 0 ){
kpniot 0:a9259748d982 320 res += cbor_serialize_int(SENML_CBOR_BN_LABEL);
kpniot 0:a9259748d982 321 res += cbor_serialize_unicode_string(this->_bn.c_str());
kpniot 0:a9259748d982 322 }
kpniot 0:a9259748d982 323 if(this->_bu){
kpniot 0:a9259748d982 324 res += cbor_serialize_int(SENML_CBOR_BU_LABEL);
kpniot 0:a9259748d982 325 res += cbor_serialize_unicode_string(senml_units_names[this->_bu]);
kpniot 0:a9259748d982 326 }
kpniot 0:a9259748d982 327 if(!isnan(this->_bt)){
kpniot 0:a9259748d982 328 res += cbor_serialize_int(SENML_CBOR_BT_LABEL);
kpniot 0:a9259748d982 329 res += cbor_serialize_double(this->_bt);
kpniot 0:a9259748d982 330 }
kpniot 0:a9259748d982 331 return res;
kpniot 0:a9259748d982 332 }
kpniot 0:a9259748d982 333
kpniot 0:a9259748d982 334
kpniot 0:a9259748d982 335
kpniot 0:a9259748d982 336
kpniot 0:a9259748d982 337
kpniot 0:a9259748d982 338
kpniot 0:a9259748d982 339