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.
senml_cbor_parser.cpp@0:a9259748d982, 2018-05-19 (annotated)
- Committer:
- kpniot
- Date:
- Sat May 19 17:35:20 2018 +0000
- Revision:
- 0:a9259748d982
first commit
Who changed what in which revision?
User | Revision | Line number | New 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 | * parse cbor |
kpniot | 0:a9259748d982 | 12 | */ |
kpniot | 0:a9259748d982 | 13 | |
kpniot | 0:a9259748d982 | 14 | |
kpniot | 0:a9259748d982 | 15 | #include <senml_cbor_parser.h> |
kpniot | 0:a9259748d982 | 16 | #include <senml_helpers.h> |
kpniot | 0:a9259748d982 | 17 | #include <senml_binary_actuator.h> |
kpniot | 0:a9259748d982 | 18 | |
kpniot | 0:a9259748d982 | 19 | void SenMLCborParser::parse(Stream* source) |
kpniot | 0:a9259748d982 | 20 | { |
kpniot | 0:a9259748d982 | 21 | this->ctx.data.stream = source; |
kpniot | 0:a9259748d982 | 22 | this->ctx.dataAsBlob = false; |
kpniot | 0:a9259748d982 | 23 | this->internalParse(); |
kpniot | 0:a9259748d982 | 24 | } |
kpniot | 0:a9259748d982 | 25 | |
kpniot | 0:a9259748d982 | 26 | |
kpniot | 0:a9259748d982 | 27 | void SenMLCborParser::parse(char* source, int length) |
kpniot | 0:a9259748d982 | 28 | { |
kpniot | 0:a9259748d982 | 29 | this->ctx.data.blob.data = source; |
kpniot | 0:a9259748d982 | 30 | this->ctx.data.blob.curPos = 0; |
kpniot | 0:a9259748d982 | 31 | this->ctx.data.blob.length = length; |
kpniot | 0:a9259748d982 | 32 | this->ctx.dataAsBlob = true; |
kpniot | 0:a9259748d982 | 33 | this->internalParse(); |
kpniot | 0:a9259748d982 | 34 | } |
kpniot | 0:a9259748d982 | 35 | |
kpniot | 0:a9259748d982 | 36 | void SenMLCborParser::internalParse() |
kpniot | 0:a9259748d982 | 37 | { |
kpniot | 0:a9259748d982 | 38 | unsigned int read_bytes = this->processArray(); //the root element of senml input is always an array. We do this cause the readable() function on mbed doesn't work as expected, so we can't read until there is no more data, we need to read until we have valid input. |
kpniot | 0:a9259748d982 | 39 | |
kpniot | 0:a9259748d982 | 40 | if (read_bytes == 0) { |
kpniot | 0:a9259748d982 | 41 | log_debug("invalid input"); |
kpniot | 0:a9259748d982 | 42 | } |
kpniot | 0:a9259748d982 | 43 | flush(); //make certain that the input streams are empty when done. This also resets any internally stored data. If we don't do this, we can only handle 1 bad input stream, ten it breaks. |
kpniot | 0:a9259748d982 | 44 | } |
kpniot | 0:a9259748d982 | 45 | |
kpniot | 0:a9259748d982 | 46 | |
kpniot | 0:a9259748d982 | 47 | void SenMLCborParser::processDouble(double value){ |
kpniot | 0:a9259748d982 | 48 | switch (this->curLabel) |
kpniot | 0:a9259748d982 | 49 | { |
kpniot | 0:a9259748d982 | 50 | //case SENML_CBOR_S_LABEL: |
kpniot | 0:a9259748d982 | 51 | case SENML_CBOR_BV_LABEL: this->ctx.baseValue.baseDouble = value; break; |
kpniot | 0:a9259748d982 | 52 | case SENML_CBOR_V_LABEL: |
kpniot | 0:a9259748d982 | 53 | double calculated = this->ctx.baseValue.baseDouble + value; |
kpniot | 0:a9259748d982 | 54 | this->setValue(&calculated, sizeof(double), CBOR_TYPE_DOUBLE); |
kpniot | 0:a9259748d982 | 55 | break; |
kpniot | 0:a9259748d982 | 56 | } |
kpniot | 0:a9259748d982 | 57 | } |
kpniot | 0:a9259748d982 | 58 | |
kpniot | 0:a9259748d982 | 59 | unsigned int SenMLCborParser::parseNext() |
kpniot | 0:a9259748d982 | 60 | { |
kpniot | 0:a9259748d982 | 61 | switch (CBOR_TYPE) { |
kpniot | 0:a9259748d982 | 62 | case CBOR_UINT: return this->processUnsignedInt(); |
kpniot | 0:a9259748d982 | 63 | case CBOR_NEGINT: return this->processInt(); |
kpniot | 0:a9259748d982 | 64 | case CBOR_BYTES: return this->processBytes(CBOR_TYPE_DATA); |
kpniot | 0:a9259748d982 | 65 | case CBOR_TEXT: return this->processBytes(CBOR_TYPE_STRING); |
kpniot | 0:a9259748d982 | 66 | case CBOR_ARRAY: return this->processArray(); |
kpniot | 0:a9259748d982 | 67 | case CBOR_MAP: return this->processMap(); |
kpniot | 0:a9259748d982 | 68 | case CBOR_7: { |
kpniot | 0:a9259748d982 | 69 | bool boolRes; |
kpniot | 0:a9259748d982 | 70 | float floatVal; |
kpniot | 0:a9259748d982 | 71 | double doubleVal; |
kpniot | 0:a9259748d982 | 72 | size_t read_bytes; |
kpniot | 0:a9259748d982 | 73 | switch ((int)peekChar()) { |
kpniot | 0:a9259748d982 | 74 | case CBOR_FALSE: |
kpniot | 0:a9259748d982 | 75 | readChar(); //need to remove the char from the stream. |
kpniot | 0:a9259748d982 | 76 | boolRes = false; |
kpniot | 0:a9259748d982 | 77 | this->setValue((void*)&boolRes, sizeof(boolRes), CBOR_TYPE_BOOL); |
kpniot | 0:a9259748d982 | 78 | return 1; |
kpniot | 0:a9259748d982 | 79 | case CBOR_TRUE: |
kpniot | 0:a9259748d982 | 80 | readChar(); //need to remove the char from the stream. |
kpniot | 0:a9259748d982 | 81 | boolRes = true; |
kpniot | 0:a9259748d982 | 82 | this->setValue((void*)&boolRes, sizeof(boolRes), CBOR_TYPE_BOOL); |
kpniot | 0:a9259748d982 | 83 | return 1; |
kpniot | 0:a9259748d982 | 84 | case CBOR_FLOAT16: |
kpniot | 0:a9259748d982 | 85 | read_bytes = cbor_deserialize_float_half(&floatVal); |
kpniot | 0:a9259748d982 | 86 | this->processDouble(floatVal); |
kpniot | 0:a9259748d982 | 87 | return read_bytes; |
kpniot | 0:a9259748d982 | 88 | case CBOR_FLOAT32: |
kpniot | 0:a9259748d982 | 89 | read_bytes = cbor_deserialize_float(&floatVal); |
kpniot | 0:a9259748d982 | 90 | this->processDouble(floatVal); |
kpniot | 0:a9259748d982 | 91 | return read_bytes; |
kpniot | 0:a9259748d982 | 92 | case CBOR_FLOAT64: |
kpniot | 0:a9259748d982 | 93 | read_bytes = cbor_deserialize_double(&doubleVal); |
kpniot | 0:a9259748d982 | 94 | this->processDouble(doubleVal); |
kpniot | 0:a9259748d982 | 95 | return read_bytes; |
kpniot | 0:a9259748d982 | 96 | } |
kpniot | 0:a9259748d982 | 97 | } |
kpniot | 0:a9259748d982 | 98 | } |
kpniot | 0:a9259748d982 | 99 | return 0; //if we get here, something went wrong |
kpniot | 0:a9259748d982 | 100 | } |
kpniot | 0:a9259748d982 | 101 | |
kpniot | 0:a9259748d982 | 102 | void SenMLCborParser::setValue(void* value, int length, SenMLDataType type) |
kpniot | 0:a9259748d982 | 103 | { |
kpniot | 0:a9259748d982 | 104 | if(this->curRec){ |
kpniot | 0:a9259748d982 | 105 | this->curRec->actuate(value, length, type); |
kpniot | 0:a9259748d982 | 106 | } |
kpniot | 0:a9259748d982 | 107 | else { |
kpniot | 0:a9259748d982 | 108 | SenMLPack* pack = this->curPack; |
kpniot | 0:a9259748d982 | 109 | if(!pack) |
kpniot | 0:a9259748d982 | 110 | pack = this->root; |
kpniot | 0:a9259748d982 | 111 | if(pack) |
kpniot | 0:a9259748d982 | 112 | pack->actuate(this->curPackName.c_str(), this->curRecName.c_str(), value, length, type); |
kpniot | 0:a9259748d982 | 113 | } |
kpniot | 0:a9259748d982 | 114 | } |
kpniot | 0:a9259748d982 | 115 | |
kpniot | 0:a9259748d982 | 116 | void SenMLCborParser::setBinaryValue(const char* value, int length) |
kpniot | 0:a9259748d982 | 117 | { |
kpniot | 0:a9259748d982 | 118 | if(this->curRec){ |
kpniot | 0:a9259748d982 | 119 | ((SenMLBinaryActuator*)this->curRec)->actuate(value, length); |
kpniot | 0:a9259748d982 | 120 | } |
kpniot | 0:a9259748d982 | 121 | else { |
kpniot | 0:a9259748d982 | 122 | SenMLPack* pack = this->curPack; |
kpniot | 0:a9259748d982 | 123 | if(!pack) |
kpniot | 0:a9259748d982 | 124 | pack = this->root; |
kpniot | 0:a9259748d982 | 125 | if(pack) |
kpniot | 0:a9259748d982 | 126 | pack->actuate(this->curPackName.c_str(), this->curRecName.c_str(), value, length, CBOR_TYPE_DATA); |
kpniot | 0:a9259748d982 | 127 | } |
kpniot | 0:a9259748d982 | 128 | } |
kpniot | 0:a9259748d982 | 129 | |
kpniot | 0:a9259748d982 | 130 | |
kpniot | 0:a9259748d982 | 131 | unsigned int SenMLCborParser::processBytes(SenMLDataType type) |
kpniot | 0:a9259748d982 | 132 | { |
kpniot | 0:a9259748d982 | 133 | uint64_t bytes_length; //needs to be this big for decode_int |
kpniot | 0:a9259748d982 | 134 | size_t bytes_read = decode_int(&bytes_length); |
kpniot | 0:a9259748d982 | 135 | |
kpniot | 0:a9259748d982 | 136 | if(bytes_read == 0) return 0; |
kpniot | 0:a9259748d982 | 137 | char buffer[bytes_length + 1]; //need a null |
kpniot | 0:a9259748d982 | 138 | for(int i = 0; i < (int)bytes_length; i++) |
kpniot | 0:a9259748d982 | 139 | buffer[i] = readChar(); |
kpniot | 0:a9259748d982 | 140 | buffer[bytes_length] = 0; //close it, just to be save. not always needed, but it is for strings |
kpniot | 0:a9259748d982 | 141 | |
kpniot | 0:a9259748d982 | 142 | if(type == CBOR_TYPE_DATA){ //we are expecting binary data, so it has to be for a binary data value. |
kpniot | 0:a9259748d982 | 143 | if(this->curLabel == SENML_CBOR_VD_LABEL) |
kpniot | 0:a9259748d982 | 144 | this->setBinaryValue(buffer, bytes_length); |
kpniot | 0:a9259748d982 | 145 | else |
kpniot | 0:a9259748d982 | 146 | log_debug("invalid input"); |
kpniot | 0:a9259748d982 | 147 | }else{ //its text |
kpniot | 0:a9259748d982 | 148 | String value; |
kpniot | 0:a9259748d982 | 149 | switch (this->curLabel) |
kpniot | 0:a9259748d982 | 150 | { |
kpniot | 0:a9259748d982 | 151 | case SENML_CBOR_BN_LABEL: |
kpniot | 0:a9259748d982 | 152 | value = buffer; |
kpniot | 0:a9259748d982 | 153 | this->setCurrentPack(value); |
kpniot | 0:a9259748d982 | 154 | break; |
kpniot | 0:a9259748d982 | 155 | case SENML_CBOR_BU_LABEL: |
kpniot | 0:a9259748d982 | 156 | value = buffer; |
kpniot | 0:a9259748d982 | 157 | this->checkBaseUnit(value); |
kpniot | 0:a9259748d982 | 158 | break; |
kpniot | 0:a9259748d982 | 159 | case SENML_CBOR_N_LABEL: |
kpniot | 0:a9259748d982 | 160 | value = buffer; |
kpniot | 0:a9259748d982 | 161 | this->setCurrentRecord(value); |
kpniot | 0:a9259748d982 | 162 | break; |
kpniot | 0:a9259748d982 | 163 | case SENML_CBOR_VS_LABEL: |
kpniot | 0:a9259748d982 | 164 | this->setValue((void*)buffer, bytes_length, CBOR_TYPE_STRING); |
kpniot | 0:a9259748d982 | 165 | break; |
kpniot | 0:a9259748d982 | 166 | } |
kpniot | 0:a9259748d982 | 167 | } |
kpniot | 0:a9259748d982 | 168 | return bytes_read + bytes_length; |
kpniot | 0:a9259748d982 | 169 | } |
kpniot | 0:a9259748d982 | 170 | |
kpniot | 0:a9259748d982 | 171 | unsigned int SenMLCborParser::processArray() |
kpniot | 0:a9259748d982 | 172 | { |
kpniot | 0:a9259748d982 | 173 | const bool is_indefinite = (peekChar() == (CBOR_ARRAY | CBOR_VAR_FOLLOWS)); |
kpniot | 0:a9259748d982 | 174 | uint64_t array_length = 0; |
kpniot | 0:a9259748d982 | 175 | size_t read_bytes; |
kpniot | 0:a9259748d982 | 176 | |
kpniot | 0:a9259748d982 | 177 | if (is_indefinite){ |
kpniot | 0:a9259748d982 | 178 | log_debug("not supported"); |
kpniot | 0:a9259748d982 | 179 | } |
kpniot | 0:a9259748d982 | 180 | else |
kpniot | 0:a9259748d982 | 181 | read_bytes = decode_int(&array_length); |
kpniot | 0:a9259748d982 | 182 | |
kpniot | 0:a9259748d982 | 183 | size_t i = 0; |
kpniot | 0:a9259748d982 | 184 | |
kpniot | 0:a9259748d982 | 185 | while (i < array_length) { |
kpniot | 0:a9259748d982 | 186 | size_t inner_read_bytes = this->parseNext(); |
kpniot | 0:a9259748d982 | 187 | if (inner_read_bytes == 0) { |
kpniot | 0:a9259748d982 | 188 | log_debug("invalid input"); |
kpniot | 0:a9259748d982 | 189 | break; |
kpniot | 0:a9259748d982 | 190 | } |
kpniot | 0:a9259748d982 | 191 | read_bytes += inner_read_bytes; |
kpniot | 0:a9259748d982 | 192 | ++i; |
kpniot | 0:a9259748d982 | 193 | } |
kpniot | 0:a9259748d982 | 194 | |
kpniot | 0:a9259748d982 | 195 | return read_bytes; |
kpniot | 0:a9259748d982 | 196 | } |
kpniot | 0:a9259748d982 | 197 | |
kpniot | 0:a9259748d982 | 198 | |
kpniot | 0:a9259748d982 | 199 | |
kpniot | 0:a9259748d982 | 200 | |
kpniot | 0:a9259748d982 | 201 | |
kpniot | 0:a9259748d982 | 202 | |
kpniot | 0:a9259748d982 | 203 |