From 5c37674980434de544ee1993dc3d5d967413fb91 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 8 Nov 2014 15:54:14 +0100 Subject: [PATCH 1/5] fixed == --- src/libebus/symbol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libebus/symbol.h b/src/libebus/symbol.h index 02288978..1ea8dad4 100644 --- a/src/libebus/symbol.h +++ b/src/libebus/symbol.h @@ -81,7 +81,7 @@ public: * @param other the other instance. * @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols). */ - bool operator==(SymbolString other) { return (m_unescapeState==0)==(m_unescapeState==0) && m_data==other.m_data; } + bool operator==(SymbolString other) { return m_unescapeState==other.m_unescapeState && m_data==other.m_data; } /** * @brief Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary. * @param value the symbol to append. From f7cd19a0ed1022e89ccf0d025abfe1fa9d382b42 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 8 Nov 2014 16:07:10 +0100 Subject: [PATCH 2/5] fix for == --- src/lib/ebus/symbol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ebus/symbol.h b/src/lib/ebus/symbol.h index c54a1fc8..a340b930 100644 --- a/src/lib/ebus/symbol.h +++ b/src/lib/ebus/symbol.h @@ -82,7 +82,7 @@ public: * @param other the other instance. * @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols). */ - bool operator==(SymbolString other) { return (m_unescapeState==0)==(other.m_unescapeState==0) && m_data==other.m_data; } + bool operator==(SymbolString other) { return m_unescapeState==other.m_unescapeState && m_data==other.m_data; } /** * @brief Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary. * @param value the symbol to append. From 640a78761fa7f92130423978467c4b0c2e3a3d3c Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 9 Nov 2014 14:13:25 +0100 Subject: [PATCH 3/5] introduced result_t, added RESULT_ERR_EOF --- src/lib/ebus/result.cpp | 8 ++++++-- src/lib/ebus/result.h | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib/ebus/result.cpp b/src/lib/ebus/result.cpp index ba2f6008..9fa53faf 100644 --- a/src/lib/ebus/result.cpp +++ b/src/lib/ebus/result.cpp @@ -22,7 +22,7 @@ namespace libebus { -const char* getResultCodeCStr(int resultCode) { +const char* getResultCodeCStr(result_t resultCode) { switch (resultCode) { case RESULT_ERR_SEND: return "ERR_SEND: send error"; case RESULT_ERR_EXTRA_DATA: return "ERR_EXTRA_DATA: received bytes > sent bytes"; @@ -35,7 +35,11 @@ const char* getResultCodeCStr(int resultCode) { case RESULT_ERR_ESC: return "ERR_ESC: invalid escape sequence received"; case RESULT_ERR_INVALID_ARG: return "ERR_INVALID_ARG: invalid argument specified"; case RESULT_ERR_DEVICE: return "ERR_DEVICE: generic device error"; - default: return "success"; + case RESULT_ERR_EOF: return "ERR_EOF: end of input reached"; + default: + if (resultCode >= 0) + return "success"; + return "ERR: unknown error code"; } } diff --git a/src/lib/ebus/result.h b/src/lib/ebus/result.h index 7a439382..0298e62f 100644 --- a/src/lib/ebus/result.h +++ b/src/lib/ebus/result.h @@ -44,14 +44,17 @@ static const int RESULT_ERR_BUS_LOST = -8; // arbitration lost static const int RESULT_ERR_ESC = -9; // invalid escape sequence received static const int RESULT_ERR_INVALID_ARG = -10; // invalid argument static const int RESULT_ERR_DEVICE = -11; // generic device error (usually fatal) +static const int RESULT_ERR_EOF = -12; // end of input reached +/** type for result code. */ +typedef int result_t; /** * @brief Return the string corresponding to the result code. * @param resultCode the result code (see RESULT_ constants). * @return the string corresponding to the result code. */ -const char* getResultCodeCStr(int resultCode); +const char* getResultCodeCStr(result_t resultCode); } //namespace From eb8a43b909e6920dccf5946e6187873109709078 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 9 Nov 2014 14:14:12 +0100 Subject: [PATCH 4/5] solved most TODOs, introduced reference types and chains, use result_t where possible, switched to divisor instead of factor, formatting, introduced VALUE_SEPARATOR for possible switch to real CSV (later), added value range check, small optimizations --- src/lib/ebus/data.cpp | 453 ++++++++++++++++++-------------- src/lib/ebus/data.h | 82 +++--- src/lib/ebus/test/test_data.cpp | 124 +++++---- 3 files changed, 379 insertions(+), 280 deletions(-) diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index c2065727..91fcaf7e 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -32,110 +32,156 @@ namespace libebus /** the known data field types. */ static const dataType_t dataTypes[] = { - {"STR",16, bt_str, ADJ,' ', 1, 16, 0}, // >= 1 byte character string filled up with space - {"HEX",16, bt_hexstr,ADJ, 0, 2, 47, 0}, // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d - {"BDA", 4, bt_date, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) - {"BDA", 3, bt_date, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) - {"HDA", 4, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) // TODO remove duplicate of BDA - {"HDA", 3, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) // TODO remove duplicate of BDA - {"BTI", 3, bt_time,BCD|REV,0, 8, 8, 0}, // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) - {"TTM", 1, bt_time, 0, 0, 5, 5, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) - {"BDY", 1, bt_list,BCD|DAY,0, 0, 6, 0}, // weekday, "Mon" - "Sun" - {"HDY", 1, bt_list,BCD|DAY,0, 1, 7, 0}, // weekday, "Mon" - "Sun" - {"BCD", 1, bt_number,BCD|LST,0xff, 0, 0x99, 1}, // unsigned decimal in BCD, 0 - 99 - {"UCH", 1, bt_number, LST, 0xff, 0, 0xff, 1}, // unsigned integer, 0 - 255 - {"SCH", 1, bt_number, SIG, 0x80, 0x80, 0x7f, 1}, // signed integer, -128 - +127 - {"D1B", 1, bt_number, SIG, 0x80, 0x81, 0x7f, 1}, // signed integer, -127 - +127 - {"D1C", 1, bt_number, 0, 0xff, 0x00, 0xc8, 2}, // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff) - {"UIN", 2, bt_number, LST, 0xffff, 0, 0xffff, 1}, // unsigned integer, 0 - 65535 - {"SIN", 2, bt_number, SIG, 0x8000, 0x8000, 0x7fff, 1}, // signed integer, -32768 - +32767 - {"FLT", 2, bt_number, SIG, 0x8000, 0x8000, 0x7fff, 1000}, // signed number (fraction 1/1000), -32.768 - +32.767 - {"D2B", 2, bt_number, SIG, 0x8000, 0x8001, 0x7fff, 256}, // signed number (fraction 1/256), -127.99 - +127.99 - {"D2C", 2, bt_number, SIG, 0x8000, 0x8001, 0x7fff, 16}, // signed number (fraction 1/16), -2047.9 - +2047.9 - {"ULG", 4, bt_number, LST, 0xffffffff, 0, 0xffffffff, 1}, // unsigned integer, 0 - 4294967295 - {"SLG", 4, bt_number, SIG, 0x80000000, 0x80000000, 0xffffffff, 1}, // signed integer, -2147483648 - +2147483647 -}; // TODO check value range for numberdf + {"STR", 16, bt_str, ADJ, ' ', 1, 16, 0}, // >= 1 byte character string filled up with space + {"HEX", 16, bt_hexstr, ADJ, 0, 2, 47, 0}, // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d + {"BDA", 4, bt_dat, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) + {"BDA", 3, bt_dat, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) + {"HDA", 4, bt_dat, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) // TODO remove duplicate of BDA + {"HDA", 3, bt_dat, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) // TODO remove duplicate of BDA + {"BTI", 3, bt_tim, BCD|REV, 0, 8, 8, 0}, // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) + {"TTM", 1, bt_tim, 0, 0, 5, 5, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) + {"BDY", 1, bt_num, DAY|LST, 0, 0, 6, 1}, // weekday, "Mon" - "Sun" + {"HDY", 1, bt_num, DAY|LST, 0, 1, 7, 1}, // weekday, "Mon" - "Sun" + {"BCD", 1, bt_num, BCD|LST, 0xff, 0, 0x99, 1}, // unsigned decimal in BCD, 0 - 99 + {"UCH", 1, bt_num, LST, 0xff, 0, 0xff, 1}, // unsigned integer, 0 - 255 + {"SCH", 1, bt_num, SIG, 0x80, 0x80, 0x7f, 1}, // signed integer, -128 - +127 + {"D1B", 1, bt_num, SIG, 0x80, 0x81, 0x7f, 1}, // signed integer, -127 - +127 + {"D1C", 1, bt_num, 0, 0xff, 0x00, 0xc8, 2}, // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff) + {"UIN", 2, bt_num, LST, 0xffff, 0, 0xffff, 1}, // unsigned integer, 0 - 65535 + {"SIN", 2, bt_num, SIG, 0x8000, 0x8000, 0x7fff, 1}, // signed integer, -32768 - +32767 + {"FLT", 2, bt_num, SIG, 0x8000, 0x8000, 0x7fff, 1000}, // signed number (fraction 1/1000), -32.768 - +32.767 + {"D2B", 2, bt_num, SIG, 0x8000, 0x8001, 0x7fff, 256}, // signed number (fraction 1/256), -127.99 - +127.99 + {"D2C", 2, bt_num, SIG, 0x8000, 0x8001, 0x7fff, 16}, // signed number (fraction 1/16), -2047.9 - +2047.9 + {"ULG", 4, bt_num, LST, 0xffffffff, 0, 0xffffffff, 1}, // unsigned integer, 0 - 4294967295 + {"SLG", 4, bt_num, SIG, 0x80000000, 0x80000000, 0xffffffff, 1}, // signed integer, -2147483648 - +2147483647 +}; /** the week day names. */ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; +#define FIELD_SEPARATOR ';' +#define VALUE_SEPARATOR ',' -DataField* DataField::create(const unsigned char dstAddress, const bool isSetMessage, - std::vector::iterator& it, const std::vector::iterator end) { +result_t DataField::create(const unsigned char dstAddress, const bool isSetMessage, + std::vector::iterator& it, const std::vector::iterator end, + const std::map predefined, std::vector& fields, + unsigned char& nextPos) { std::string name, unit, comment; PartType partType; - float factor; - size_t baseOffset = 0, offset = 0, length = 0, maxPos = 16, offsetCnt = 0; + unsigned int divisor; + unsigned char baseOffset, offset, length, maxPos = 16, offsetCnt = 0; + std::string token; if (it == end) - return NULL; + return RESULT_ERR_EOF; + name = *it++; - if (it == end || name.size() == 0) - return NULL; + if (it == end || name.empty() == true) + return RESULT_ERR_EOF; + const char* posStr = (*it++).c_str(); - if (it == end) - return NULL; + if (it == end) { + // no more input: check for reference to predefined type + if (posStr[0] == 0) + return RESULT_ERR_EOF; + + std::map::const_iterator ref = predefined.find(name); + if (ref != predefined.end()) { + fields.push_back(ref->second); + return RESULT_OK; + } + // check for sequence of reference to predefined type + std::istringstream stream(name); + while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { + ref = predefined.find(token); + if (ref == predefined.end()) + return RESULT_ERR_INVALID_ARG; + + fields.push_back(ref->second); + } + return RESULT_OK; + } if (dstAddress == BROADCAST || isMaster(dstAddress) || (isSetMessage == true && posStr[0] <= '9') || posStr[0] == 'm') { // master data partType = pt_masterData; baseOffset = 5; // skip QQ ZZ PB SB NN - //len = command.getMasterDataLength(); if (posStr[0] == 'm') posStr++; } else if ((isSetMessage == false && posStr[0] <= '9') || posStr[0] == 's') { // slave data + partType = pt_slaveData; baseOffset = 1; - //offset = 5+command.getMasterDataLength()+3; // skip QQ ZZ PB SB NN Dx CRC ACK NN - //len = command.getSlaveDataLength(); if (posStr[0] == 's') posStr++; + } else + return RESULT_ERR_INVALID_ARG; + + if (posStr[0] == 0) { + if (nextPos == 0) + offset = baseOffset; + else if (nextPos < baseOffset) + return RESULT_ERR_INVALID_ARG; // missing pos definition + else + offset = nextPos; + length = 0; } else { - return NULL; // TODO error code: invalid pos definition - } - std::string token; - std::istringstream stream(posStr); - while (std::getline(stream, token, '-') != 0) { - if (++offsetCnt > 2) - return NULL; // TODO error code: invalid pos definition - const char* start = token.c_str(); - char* end = NULL; - unsigned int pos = strtoul(start, &end, 10)-1; // 1-based - if (end != start+strlen(start)) - return NULL; // TODO error code: invalid pos definition - if (baseOffset+pos > maxPos) - return NULL; // TODO error code: invalid pos definition - else if (offsetCnt==1) - offset = baseOffset+pos; - else if (baseOffset+pos >= offset) - length = baseOffset+pos+1-offset; - else { // wrong order e.g. 4-3 - length = offset-(baseOffset+pos+1); - offset = baseOffset+pos; + offset = 0; + length = 0; + std::istringstream stream(posStr); + while (std::getline(stream, token, '-') != 0) { + if (++offsetCnt > 2) + return RESULT_ERR_INVALID_ARG; //invalid pos definition + + const char* start = token.c_str(); + char* end = NULL; + unsigned int pos = strtoul(start, &end, 10)-1; // 1-based + if (end != start+strlen(start)) + return RESULT_ERR_INVALID_ARG; // invalid pos definition + + if (baseOffset+pos > maxPos) + return RESULT_ERR_INVALID_ARG; // invalid pos definition + + if (offsetCnt==1) + offset = baseOffset+pos; + else if (baseOffset+pos >= offset) + length = baseOffset+pos+1-offset; + else { // wrong order e.g. 4-3 + length = offset-(baseOffset+pos+1); + offset = baseOffset+pos; + } } + if (offset < baseOffset) + return RESULT_ERR_INVALID_ARG; //invalid pos definition } const char* typeStr = (*it++).c_str(); std::map values; if (it == end) - factor = 1.0; + divisor = 1; else { - std::string factorStr = *it++; - if (factorStr.empty()) - factor = 1.0; - else if (factorStr.find_first_not_of("0123456789.") == std::string::npos) - factor = static_cast(strtod(factorStr.c_str(), NULL)); + std::string divisorStr = *it++; + if (divisorStr.empty() == true) + divisor = 1; + else if (divisorStr.find_first_not_of("0123456789") == std::string::npos) { + const char* start = divisorStr.c_str(); + char* end = NULL; + divisor = strtoul(start, &end, 10); + if (end != start+strlen(start)) + return RESULT_ERR_INVALID_ARG; + } else { - factor = 1.0; - std::istringstream stream(factorStr); - while (std::getline(stream, token, ',') != 0) { + divisor = 1; + std::istringstream stream(divisorStr); + while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { const char* start = token.c_str(); char* end = NULL; unsigned int id = strtoul(start, &end, 10); if (end == NULL || end == start || *end != '=') - return NULL; // TODO error code: invalid values definition + return RESULT_ERR_INVALID_ARG; + values[id] = std::string(end+1); } } @@ -157,7 +203,7 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes if (comment.length() == 1 && comment[0] == '-') comment.clear(); } - +//TODO derive more specific subtypes for (size_t i = 0; i < sizeof(dataTypes)/sizeof(dataTypes[0]); i++) { dataType_t dataType = dataTypes[i]; if (strcasecmp(typeStr, dataType.name) == 0) { @@ -165,7 +211,7 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes if (length == 0) length = 1; // minimum length defaults to 1 else if (length > dataType.numBytes) - return NULL; // invalid length + return RESULT_ERR_INVALID_ARG; // invalid length } else if (length == 0) length = dataType.numBytes; @@ -175,34 +221,37 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes switch (dataType.type) { case bt_str: case bt_hexstr: - case bt_date: // TODO better numeric? - case bt_time: // TODO better numeric? - return new StringDataField(name, partType, offset, length, dataType, unit, comment); - case bt_list: - if (values.empty() == false) { - if (values.begin()->first < dataType.minValueOrLength) - return NULL; // invalid value id - std::map::iterator end = values.end(); - end--; - if (end->first > dataType.maxValueOrLength) - return NULL; // invalid value id - } - else if ((dataType.flags&DAY) != 0) { + case bt_dat: + case bt_tim: + fields.push_back(new StringDataField(name, partType, offset, length, dataType, unit, comment)); + nextPos = offset+length; + return RESULT_OK; + case bt_num: + if (values.empty() == true && (dataType.flags&DAY) != 0) { for (unsigned int i=0; ifirst < dataType.minValueOrLength) + return RESULT_ERR_INVALID_ARG; + std::map::iterator end = values.end(); + end--; + if (end->first > dataType.maxValueOrLength) + return RESULT_ERR_INVALID_ARG; + fields.push_back(new ValueListDataField(name, partType, offset, length, dataType, unit, comment, values)); + nextPos = offset+length; + return RESULT_OK; } } } - return NULL; // TODO error code: invalid type definition + return RESULT_ERR_INVALID_ARG; } -const std::string DataField::read(SymbolString& masterData, SymbolString& slaveData, bool verbose) +result_t DataField::read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, bool verbose) { SymbolString& input = m_partType == pt_masterData ? masterData : slaveData; switch (m_partType) { @@ -211,23 +260,24 @@ const std::string DataField::read(SymbolString& masterData, SymbolString& slaveD case pt_slaveData: break; default: - return "invalid part type"; + return RESULT_ERR_INVALID_ARG; // invalid part type } - std::ostringstream output; if (verbose) output << m_name << "="; - if (readSymbols(input, output) == false) - return "unable to parse"; + result_t result = readSymbols(input, output); + if (result != RESULT_OK) + return result; if (verbose && m_unit.length() > 0) output << " " << m_unit; if (verbose && m_comment.length() > 0) output << " [" << m_comment << "]"; - return output.str(); + + return RESULT_OK; } -bool DataField::write(const std::string& value, SymbolString& masterData, SymbolString& slaveData) +result_t DataField::write(const std::string& value, SymbolString& masterData, SymbolString& slaveData) { SymbolString& output = m_partType == pt_masterData ? masterData : slaveData; switch (m_partType) { @@ -235,24 +285,22 @@ bool DataField::write(const std::string& value, SymbolString& masterData, Symbol case pt_slaveData: break; default: - return false; // TODO error code + return RESULT_ERR_INVALID_ARG; } std::istringstream input(value); - if (writeSymbols(input, output) == false) - return false; // TODO error code - return true; + return writeSymbols(input, output); } -bool StringDataField::readSymbols(SymbolString& input, std::ostringstream& output) +result_t StringDataField::readSymbols(SymbolString& input, std::ostringstream& output) { size_t start = m_offset, end = m_offset + m_length; int incr = 1; unsigned char ch; if (end > input.size()) - return false; // TODO error not enough data available + return RESULT_ERR_INVALID_ARG; if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) end = start - 1; @@ -261,12 +309,12 @@ bool StringDataField::readSymbols(SymbolString& input, std::ostringstream& outpu } for (size_t pos = start, i = 0; pos != end; pos += incr, i++) { - if (m_length == 4 && i == 2 && m_dataType.type == bt_date) + if (m_length == 4 && i == 2 && m_dataType.type == bt_dat) continue; // skip weekday in between ch = input[pos]; if ((m_dataType.flags & BCD) != 0) { if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) - return false; // invalid BCD + return RESULT_ERR_INVALID_ARG; // invalid BCD ch = (ch >> 4) * 10 + (ch & 0x0f); } switch (m_dataType.type) { @@ -276,26 +324,27 @@ bool StringDataField::readSymbols(SymbolString& input, std::ostringstream& outpu output << std::nouppercase << std::setw(2) << std::hex << std::setfill('0') << static_cast(ch); break; - case bt_date: + case bt_dat: if (i + 1 == m_length) output << (2000+ch); else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12)) - return false; // invalid date + return RESULT_ERR_INVALID_ARG; // invalid date else output << std::setw(2) << std::setfill('0') << static_cast(ch) << "."; break; - case bt_time: + case bt_tim: if (m_length == 1) { // truncated time - if (ch > 24*6) - return false; // invalid time - output << std::setw(2) << std::setfill('0') << static_cast(ch/6) << ":" - << std::setw(2) << std::setfill('0') << static_cast((ch%6)*10); - break; + if (i == 0) { + ch /= 6; // hours + pos -= incr; // repeat for minutes + } + else + ch = (ch%6) * 10; // minutes } + if ((i == 0 && ch > 23) || (i > 0 && ch > 59)) + return RESULT_ERR_INVALID_ARG; // invalid time if (i > 0) output << ":"; - if ((i == 0 && ch > 23) || (i > 0 && ch > 59)) - return false; // invalid time output << std::setw(2) << std::setfill('0') << static_cast(ch); break; default: @@ -306,16 +355,16 @@ bool StringDataField::readSymbols(SymbolString& input, std::ostringstream& outpu } } - return true; + return RESULT_OK; } -bool StringDataField::writeSymbols(std::istringstream& input, SymbolString& output) +result_t StringDataField::writeSymbols(std::istringstream& input, SymbolString& output) { size_t start = m_offset, end = m_offset + m_length; int incr = 1; const char* str; char* strEnd; - unsigned long int value = 0, minutes = 0; + unsigned long int value = 0, hours = 0; std::string token; if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) @@ -329,60 +378,61 @@ bool StringDataField::writeSymbols(std::istringstream& input, SymbolString& outp case bt_hexstr: while (input.peek()==' ') input.get(); - token.clear(); - token.push_back(input.get()); - if (input.eof() == true) - return false; // TODO error code: invalid value - token.push_back(input.get()); - if (input.eof() == true) - return false; // TODO error code: invalid value + if (input.eof() == true) // no more digits + value = m_dataType.replacement; // fill up with replacement + else { + token.clear(); + token.push_back(input.get()); + if (input.eof() == true) + return RESULT_ERR_INVALID_ARG; + token.push_back(input.get()); + if (input.eof() == true) + return RESULT_ERR_INVALID_ARG; // invalid hex value - str = token.c_str(); - strEnd = NULL; - value = strtoul(str, &strEnd, 16); - if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value + str = token.c_str(); + strEnd = NULL; + value = strtoul(str, &strEnd, 16); + if (strEnd != str+strlen(str)) + return RESULT_ERR_INVALID_ARG; // invalid hex value + } break; - case bt_date: + case bt_dat: if (m_length == 4 && i == 2) continue; // skip weekday in between if (std::getline(input, token, '.') == 0) - return false; + return RESULT_ERR_INVALID_ARG; // incomplete str = token.c_str(); strEnd = NULL; value = strtoul(str, &strEnd, 10); if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value + return RESULT_ERR_INVALID_ARG; // invalid date part if (i + 1 == m_length && value >= 2000) value -= 2000; else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) - return false; // invalid date + return RESULT_ERR_INVALID_ARG; // invalid date part break; - case bt_time: + case bt_tim: if (std::getline(input, token, ':') == 0) - return false; + return RESULT_ERR_INVALID_ARG; // incomplete str = token.c_str(); strEnd = NULL; value = strtoul(str, &strEnd, 10); if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value - if (m_length == 1) { // truncated time - if (std::getline(input, token, ':') == 0) - return false; - str = token.c_str(); - strEnd = NULL; - minutes = strtoul(str, &strEnd, 10); - if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value - if ((minutes % 10) != 0) - return false; // invalid time - value = value*6 + (minutes / 10); - if (value > 24*6) - return false; // invalid time - break; - } + return RESULT_ERR_INVALID_ARG; // invalid time part if ((i == 0 && value > 23) || (i > 0 && value > 59)) - return false; // invalid time + return RESULT_ERR_INVALID_ARG; // invalid time part + if (m_length == 1) { // truncated time + if (i == 0) { + pos -= incr; // repeat for minutes + hours = value; + continue; + } + if ((value % 10) != 0) + return RESULT_ERR_INVALID_ARG; // invalid truncated time minutes + value = hours*6 + (value / 10); + if (value > 24*6) + return RESULT_ERR_INVALID_ARG; // invalid time + } break; default: value = input.get(); @@ -392,26 +442,26 @@ bool StringDataField::writeSymbols(std::istringstream& input, SymbolString& outp } if ((m_dataType.flags & BCD) != 0) { if (value > 99) - return false; // invalid BCD + return RESULT_ERR_INVALID_ARG; // invalid BCD value = (value/10)<<4 | (value%10); } if (value > 0xff) - return false; + return RESULT_ERR_INVALID_ARG; // value out of range output[pos] = (unsigned char)value; } - return true; + return RESULT_OK; } -bool NumericDataField::readRawValue(SymbolString& input, unsigned int& value) +result_t NumericDataField::readRawValue(SymbolString& input, unsigned int& value) { size_t start = m_offset, end = m_offset + m_length; int incr = 1; unsigned char ch; if (end > input.size()) - return false; // TODO error not enough data available + return RESULT_ERR_INVALID_ARG; // not enough data available if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) end = start - 1; @@ -425,10 +475,10 @@ bool NumericDataField::readRawValue(SymbolString& input, unsigned int& value) if ((m_dataType.flags & BCD) != 0) { if (ch == m_dataType.replacement) { value = m_dataType.replacement; - return true; + return RESULT_OK; } if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) - return false; // invalid BCD + return RESULT_ERR_INVALID_ARG; // invalid BCD ch = (ch >> 4) * 10 + (ch & 0x0f); value += ch*exp; @@ -439,10 +489,10 @@ bool NumericDataField::readRawValue(SymbolString& input, unsigned int& value) exp = exp<<8; } } - return true; + return RESULT_OK; } -bool NumericDataField::writeRawValue(unsigned int value, SymbolString& output) +result_t NumericDataField::writeRawValue(unsigned int value, SymbolString& output) { size_t start = m_offset, end = m_offset + m_length; int incr = 1; @@ -471,49 +521,49 @@ bool NumericDataField::writeRawValue(unsigned int value, SymbolString& output) output[pos] = ch; } - return true; + return RESULT_OK; } -bool NumberDataField::readSymbols(SymbolString& input, std::ostringstream& output) +result_t NumberDataField::readSymbols(SymbolString& input, std::ostringstream& output) { unsigned int value = 0; int signedValue; - if (readRawValue(input, value) == false) - return false; + result_t result = readRawValue(input, value); + if (result != RESULT_OK) + return result; if (value == m_dataType.replacement) { output << "-"; - return true; + return RESULT_OK; } - - if ((m_dataType.flags&SIG) != 0 && (value & (1 << (m_dataType.numBytes*8 - 1))) != 0) // negative signed value - if (m_dataType.numBytes == 4) - signedValue = (int)value; - else - signedValue = (int)value - (1 << (m_dataType.numBytes*8)); - else { - if (m_dataType.numBytes == 4) { - if (m_factor == 1.0) + bool negative = (m_dataType.flags&SIG) != 0 && (value & (1 << (m_dataType.numBytes*8 - 1))) != 0; + if (m_dataType.numBytes == 4) { + if (negative == false) { + if (m_divisor <= 1) output << static_cast(value); else - output << std::setprecision(3) << std::fixed << static_cast(value * m_factor); - return true; + output << std::setprecision(3) << std::fixed << static_cast(value / (float)m_divisor); + return RESULT_OK; } - - signedValue = (int)value; + signedValue = (int)value; // negative signed value } + else + if (negative) // negative signed value + signedValue = (int)value - (1 << (m_dataType.numBytes*8)); + else + signedValue = (int)value; - if (m_factor == 1.0) + if (m_divisor <= 1) output << static_cast(signedValue); else - output << std::setprecision(3) << std::fixed << static_cast(signedValue * m_factor); + output << std::setprecision(3) << std::fixed << static_cast(signedValue / (float)m_divisor); - return true; + return RESULT_OK; } -bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& output) +result_t NumberDataField::writeSymbols(std::istringstream& input, SymbolString& output) { unsigned int value; @@ -523,7 +573,7 @@ bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& outp value = m_dataType.replacement; else { char* strEnd = NULL; - if (m_factor == 1.0) { + if (m_divisor <= 1) { if ((m_dataType.flags&SIG) != 0) { int signedValue = strtol(str, &strEnd, 10); if (signedValue < 0 && m_dataType.numBytes != 4) @@ -534,17 +584,17 @@ bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& outp else value = strtoul(str, &strEnd, 10); if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value + return RESULT_ERR_INVALID_ARG; // invalid value } else { char* strEnd = NULL; double dvalue = strtod(str, &strEnd); if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value - dvalue = dvalue / m_factor + 0.5; // round + return RESULT_ERR_INVALID_ARG; // invalid value + dvalue = dvalue * m_divisor + 0.5; // round if ((m_dataType.flags&SIG) != 0) { if (dvalue < -(1LL<<(8*m_length)) || dvalue >= (1LL<<(8*m_length))) - return false; // TODO error code: invalid value + return RESULT_ERR_INVALID_ARG; // value out of range if (dvalue < 0 && m_dataType.numBytes != 4) value = (unsigned int)(dvalue + (1<<(m_dataType.numBytes*8))); else @@ -552,37 +602,50 @@ bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& outp } else { if (dvalue < 0.0 || dvalue >= (1LL<<(8*m_length))) - return false; // TODO error code: invalid value + return RESULT_ERR_INVALID_ARG; // value out of range value = (unsigned int)dvalue; } } + + if ((m_dataType.flags&SIG) != 0) { // signed value + if ((value & (1 << (m_dataType.numBytes*8 - 1))) != 0) { // negative signed value + if (value < m_dataType.minValueOrLength) + return RESULT_ERR_INVALID_ARG; // value out of range + } + else if (value > m_dataType.maxValueOrLength) + return RESULT_ERR_INVALID_ARG; // value out of range + } + else if (value < m_dataType.minValueOrLength || value > m_dataType.maxValueOrLength) + return RESULT_ERR_INVALID_ARG; // value out of range } + return writeRawValue(value, output); } -bool ValueListDataField::readSymbols(SymbolString& input, std::ostringstream& output) +result_t ValueListDataField::readSymbols(SymbolString& input, std::ostringstream& output) { unsigned int value = 0; - if (readRawValue(input, value) == false) - return false; + result_t result = readRawValue(input, value); + if (result != RESULT_OK) + return result; if (value == m_dataType.replacement) { output << "-"; - return true; + return RESULT_OK; } std::map::iterator it = m_values.find(value); if (it == m_values.end()) - return false; + return RESULT_ERR_INVALID_ARG; // value assignment not found output << it->second; - return true; + return RESULT_OK; } -bool ValueListDataField::writeSymbols(std::istringstream& input, SymbolString& output) +result_t ValueListDataField::writeSymbols(std::istringstream& input, SymbolString& output) { std::string str; input >> str; @@ -591,7 +654,7 @@ bool ValueListDataField::writeSymbols(std::istringstream& input, SymbolString& o if (it->second.compare(str) == 0) return writeRawValue(it->first, output); - return false; + return RESULT_ERR_INVALID_ARG; // value assignment not found } diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index bd5d40c1..e0e8dabe 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -21,6 +21,7 @@ #define LIBEBUS_DATA_H_ #include "symbol.h" +#include "result.h" #include #include #include @@ -39,10 +40,9 @@ enum PartType { enum BaseType { bt_str, // text string in a StringDataField bt_hexstr, // hex digit string in a StringDataField - bt_date, // date in a StringDataField - bt_time, // time in a StringDataField - bt_list, // numeric list value in a ValueListDataField - bt_number // number value in a NumberDataField + bt_dat, // date in a StringDataField + bt_tim, // time in a StringDataField + bt_num, // numeric value in a NumericDataField }; /** flags for dataType_t. */ @@ -50,7 +50,7 @@ const unsigned int ADJ = 0x01; // adjustable length, numBytes is maximum length const unsigned int BCD = 0x02; // binary representation is BCD const unsigned int REV = 0x04; // reverted binary representation (most significant byte first) const unsigned int SIG = 0x08; // signed value -const unsigned int LST = 0x10; // value list is possible (without applied factor) +const unsigned int LST = 0x10; // value list is possible (without applied divisor) const unsigned int DAY = 0x20; // default value list is week days /** the structure for defining field types with their properties. */ @@ -97,43 +97,55 @@ public: /** * @brief Factory method for creating a new instance. * @param dstAddress the destination bus address. - * @param isSetMessage whther the field is part of a set message. + * @param isSetMessage whether the field is part of a set message. * @param it the iterator to traverse for the definition parts. * @param end the iterator pointing to the end of the definition parts. + * @param predefined a map of predefined DataFields to be referenced by name. + * @param fields the vector to which created instances are added. + * @param nextPos the variable holding the next subsequent position. + * @return RESULT_OK on success, RESULT_ERR_EOF if the iterator is empty, or an error code. + * Note: the caller needs to cleanup created instances. */ - static DataField* create(const unsigned char dstAddress, const bool isSetMessage, - std::vector::iterator& it, const std::vector::iterator end); + static result_t create(const unsigned char dstAddress, const bool isSetMessage, + std::vector::iterator& it, const std::vector::iterator end, + const std::map predefined, std::vector& fields, + unsigned char& nextPos); /** * @brief Reads the value from the master or slave @a SymbolString. * @param masterData the unescaped master data @a SymbolString for reading binary data. * @param slaveData the unescaped slave data @a SymbolString for reading binary data. - * @return the formatted value as string. + * @param output the ostringstream to append the formatted value to. + * @param vervose whether to prepend the name, append the unit (if present), and append + * the comment in square brackets (if present). + * @return RESULT_OK on success, or an error code. */ - const std::string read(SymbolString& masterData, SymbolString& slaveData, bool verbose=false); + result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, + bool verbose=false); /** * @brief Writes the value to the master or slave @a SymbolString. * @param masterData the unescaped master data @a SymbolString for writing binary data. * @param slaveData the unescaped slave data @a SymbolString for writing binary data. * @param value the formatted value as string. + * @return RESULT_OK on success, or an error code. */ - bool write(const std::string& value, SymbolString& masterData, SymbolString& slaveData); + result_t write(const std::string& value, SymbolString& masterData, SymbolString& slaveData); protected: /** * @brief Internal method for reading the field from a @a SymbolString. * @param input the unescaped @a SymbolString to read the binary value from. * @param output the ostringstream to append the formatted value to. - * @return true if the value was parsed successfully. + * @return RESULT_OK on success, or an error code. */ - virtual bool readSymbols(SymbolString& input, std::ostringstream& output) = 0; + virtual result_t readSymbols(SymbolString& input, std::ostringstream& output) = 0; /** * @brief Internal method for writing the field to a @a SymbolString. * @param input the istringstream to parse the formatted value from. * @param output the unescaped @a SymbolString to write the binary value to. - * @return true if the value was formatted successfully. + * @return RESULT_OK on success, or an error code. */ - virtual bool writeSymbols(std::istringstream& input, SymbolString& output) = 0; + virtual result_t writeSymbols(std::istringstream& input, SymbolString& output) = 0; /** the field name. */ const std::string m_name; @@ -179,8 +191,8 @@ public: virtual ~StringDataField() {} protected: - virtual bool readSymbols(SymbolString& input, std::ostringstream& output); - virtual bool writeSymbols(std::istringstream& input, SymbolString& output); + virtual result_t readSymbols(SymbolString& input, std::ostringstream& output); + virtual result_t writeSymbols(std::istringstream& input, SymbolString& output); }; @@ -198,9 +210,8 @@ public: * @param offset the offset to the first symbol in the message part in which the field is stored. * @param length the number of symbols in the message part in which the field is stored. * @param dataType the data type definition. - * @param comment the field comment. * @param unit the value unit. - * @param replacement the (binary) replacement value to use if the value is not set. + * @param comment the field comment. */ NumericDataField(const std::string name, const PartType partType, const unsigned char offset, const unsigned char length, @@ -217,16 +228,16 @@ protected: * @brief Internal method for reading the raw value from a @a SymbolString. * @param input the unescaped @a SymbolString to read the binary value from. * @param value the variable in which to store the raw value. - * @return true if the value was read successfully. + * @return RESULT_OK on success, or an error code. */ - bool readRawValue(SymbolString& input, unsigned int& value); + result_t readRawValue(SymbolString& input, unsigned int& value); /** * @brief Internal method for writing the raw value to a @a SymbolString. * @param value the raw value to write. * @param output the unescaped @a SymbolString to write the binary value to. - * @return true if the value was written successfully. + * @return RESULT_OK on success, or an error code. */ - bool writeRawValue(unsigned int value, SymbolString& output); + result_t writeRawValue(unsigned int value, SymbolString& output); }; @@ -243,28 +254,27 @@ public: * @param offset the offset to the first symbol in the message part in which the field is stored. * @param length the number of symbols in the message part in which the field is stored. * @param dataType the data type definition. - * @param comment the field comment. * @param unit the value unit. - * @param replacement the (binary) replacement value to use if the value is not set. - * @param factor the factor to apply on the value. + * @param comment the field comment. + * @param divisor the extra divisor to apply on the value, or 1 for none. */ NumberDataField(const std::string name, const PartType partType, const unsigned char offset, const unsigned char length, const dataType_t dataType, const std::string unit, - const std::string comment, const float factor) + const std::string comment, const unsigned int divisor) : NumericDataField(name, partType, offset, length, dataType, unit, comment), - m_factor(factor / dataType.divisor) {} + m_divisor(divisor * dataType.divisor) {} /** * @brief Destructor. */ virtual ~NumberDataField() {} protected: - virtual bool readSymbols(SymbolString& input, std::ostringstream& output); - virtual bool writeSymbols(std::istringstream& input, SymbolString& output); + virtual result_t readSymbols(SymbolString& input, std::ostringstream& output); + virtual result_t writeSymbols(std::istringstream& input, SymbolString& output); - /** the factor to apply on the value. */ - const float m_factor; + /** the combined divisor to apply on the value, or 1 for none. */ + const unsigned int m_divisor; }; @@ -281,10 +291,8 @@ public: * @param offset the offset to the first symbol in the message part in which the field is stored. * @param length the number of symbols in the message part in which the field is stored. * @param dataType the data type definition. - * @param comment the field comment. * @param unit the value unit. - * @param factor the factor to apply on the value. - * @param replacement the (binary) replacement value to use if the value is not set. + * @param comment the field comment. * @param values the value=text assignments. */ ValueListDataField(const std::string name, const PartType partType, @@ -299,8 +307,8 @@ public: virtual ~ValueListDataField() {} protected: - virtual bool readSymbols(SymbolString& input, std::ostringstream& output); - virtual bool writeSymbols(std::istringstream& input, SymbolString& output); + virtual result_t readSymbols(SymbolString& input, std::ostringstream& output); + virtual result_t writeSymbols(std::istringstream& input, SymbolString& output); /** the value=text assignments. */ std::map m_values; diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index 19310cc2..c2ff3e70 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -30,29 +30,32 @@ int main () // {"temp;1;d2b;;°C;Aussentemperatur","temp=18.004 °C [Aussentemperatur]","10fe070009019258042126100714cc", "00"}, // {"zeit;1;ttm;2;Uhr;","zeit=22:40 Uhr","10feffff0188", "00"}, {"x;1-10;hex","53 70 65 69 63 68 65 72 20 20", "10fe07000a53706569636865722020", "00"}, - {"x;1;bti","21:04:58","10fe070009580421", "00"}, - {"x;1;bda","26.10.2014","10fe07000926100714", "00"}, + {"x;;bti","21:04:58","10fe070009580421", "00"}, + {"x;;bda","26.10.2014","10fe07000926100714", "00"}, {"x;1-3;bda","26.10.2014","10fe070003261014", "00"}, - {"x;1;hdy","Sun","10fe07000307", "00"}, - {"x;1;bdy","Sun","10fe07000306", "00"}, - {"x;1;d2b","18.004","10fe0700090112", "00"}, - {"x;1;d2c","288.062","10fe0700090112", "00"}, - {"x;1;ttm","22:40","10feffff0188", "00"}, - {"x;1;bcd","26","10feffff0126", "00"}, - {"x;1;bcd","-","10feffff01ff", "00"}, - {"x;1;uch","38","10feffff0126", "00"}, - {"x;1;sch","-90","10feffff01a6", "00"}, - {"x;1;d1b","-90","10feffff01a6", "00"}, - {"x;1;d1c","19.500","10feffff0127", "00"}, - {"x;1;uin","38","10feffff022600", "00"}, - {"x;1;sin","-90","10feffff02a6ff", "00"}, - {"x;1;ulg","38","10feffff0426000000", "00"}, - {"x;1;slg","-90","10feffff04a6ffffff", "00"}, - {"x;1;flt","-0.090","10feffff02a6ff", "00"}, + {"x;;hdy","Sun","10fe07000307", "00"}, + {"x;;bdy","Sun","10fe07000306", "00"}, + {"x;;d2b","18.004","10fe0700090112", "00"}, + {"x;;d2c","288.062","10fe0700090112", "00"}, + {"x;;ttm","22:40","10feffff0188", "00"}, + {"x;;bcd","26","10feffff0126", "00"}, + {"x;;bcd","-","10feffff01ff", "00"}, + {"x;;uch","38","10feffff0126", "00"}, + {"x;;sch","-90","10feffff01a6", "00"}, + {"x;;d1b","-90","10feffff01a6", "00"}, + {"x;;d1c","19.500","10feffff0127", "00"}, + {"x;;uin","38","10feffff022600", "00"}, + {"x;;sin","-90","10feffff02a6ff", "00"}, + {"x;;ulg","38","10feffff0426000000", "00"}, + {"x;;slg","-90","10feffff04a6ffffff", "00"}, + {"x;;flt","-0.090","10feffff02a6ff", "00"}, {"x;1-9;str","hallo Du!","10feffff0868616c6c6f20447521", "00"}, {"x;1-9;str","hallo Du ","10feffff0868616c6c6f20447520", "00"}, {"new;1;uch;1=test,2=high,3=off,4=on","on","10feffff0104", "00"}, }; + std::map predefined; + std::vector fields; + unsigned char nextPos; for (size_t i = 0; i < sizeof(checks)/sizeof(checks[0]); i++) { std::istringstream isstr(checks[i][0]); std::string expectStr = checks[i][1]; @@ -65,45 +68,70 @@ int main () entries.push_back(item); std::vector::iterator it = entries.begin(); - DataField* field = DataField::create(mstr[1], false, it, entries.end()); + nextPos = 0; + result_t result = DataField::create(mstr[1], false, it, entries.end(), predefined, fields, nextPos); - if (field == NULL) { - std::cout << "create \"" << checks[i][0] << "\" invalid: null" << std::endl; - return 1; + if (result != RESULT_OK) { + std::cout << "create \"" << checks[i][0] << "\" failed: " << getResultCodeCStr(result) << std::endl; + continue; + } + if (fields.empty() == true) { + std::cout << "create \"" << checks[i][0] << "\" failed: empty" << std::endl; + continue; } std::cout << "create \"" << checks[i][0] << "\" successful" << std::endl; - - std::string gotStr = field->read(mstr, sstr); - - if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) - std::cout << "read successful: " << gotStr << std::endl; - else - std::cout << "read invalid: got " << gotStr - << ", expected " << expectStr << std::endl; - - SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); - SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); - if (field->write(gotStr, writeMstr, writeSstr) == false) - std::cout << "write failed" << std::endl; + DataField* field = fields.back(); + fields.pop_back(); + std::ostringstream output; + result = field->read(mstr, sstr, output); + if (result != RESULT_OK) { + std::cout << "read \"" << checks[i][0] << "\" failed: " << getResultCodeCStr(result) << std::endl; + } else { - if (mstr == writeMstr && sstr == writeSstr) - std::cout << "write successful" << std::endl; - else { - std::cout << "write invalid: "; - if (mstr == writeMstr) - std::cout << "master OK"; - else - std::cout << "master got " << writeMstr.getDataStr() << ", expected " << mstr.getDataStr(); + std::string gotStr = output.str(); - if (sstr == writeSstr) - std::cout << ", slave OK"; - else - std::cout << ", slave got " << writeSstr.getDataStr() << ", expected " << sstr.getDataStr(); - std::cout << std::endl; + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) + std::cout << "read successful: " << gotStr << std::endl; + else + std::cout << "read invalid: got " << gotStr + << ", expected " << expectStr << std::endl; + + SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); + SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); + + if (result != RESULT_OK) { + std::cout << "create \"" << checks[i][0] << "\" failed: " << getResultCodeCStr(result) << std::endl; + return 1; + } + result = field->write(gotStr, writeMstr, writeSstr); + if (result != RESULT_OK) { + std::cout << "write \"" << checks[i][0] << "\" failed: " << getResultCodeCStr(result) << std::endl; + } + else { + if (mstr == writeMstr && sstr == writeSstr) + std::cout << "write successful" << std::endl; + else { + std::cout << "write invalid: "; + if (mstr == writeMstr) + std::cout << "master OK"; + else + std::cout << "master got " << writeMstr.getDataStr() << ", expected " << mstr.getDataStr(); + + if (sstr == writeSstr) + std::cout << ", slave OK"; + else + std::cout << ", slave got " << writeSstr.getDataStr() << ", expected " << sstr.getDataStr(); + std::cout << std::endl; + } } } delete field; + while (fields.empty() == false) { + field = fields.back(); + fields.pop_back(); + delete field; // TODO use me + } } return 0; From 72fcd8623dc6961eebd67f409e3e7b661e2bc726 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 9 Nov 2014 14:20:58 +0100 Subject: [PATCH 5/5] introduced NULL_VALUE define --- src/lib/ebus/data.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 91fcaf7e..ac409c23 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -61,6 +61,7 @@ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" #define FIELD_SEPARATOR ';' #define VALUE_SEPARATOR ',' +#define NULL_VALUE "-" result_t DataField::create(const unsigned char dstAddress, const bool isSetMessage, std::vector::iterator& it, const std::vector::iterator end, @@ -535,7 +536,7 @@ result_t NumberDataField::readSymbols(SymbolString& input, std::ostringstream& o return result; if (value == m_dataType.replacement) { - output << "-"; + output << NULL_VALUE; return RESULT_OK; } bool negative = (m_dataType.flags&SIG) != 0 && (value & (1 << (m_dataType.numBytes*8 - 1))) != 0; @@ -568,7 +569,7 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, SymbolString& unsigned int value; const char* str = input.str().c_str(); - if (strcasecmp(str, "-") == 0) + if (strcasecmp(str, NULL_VALUE) == 0) // replacement value value = m_dataType.replacement; else { @@ -633,7 +634,7 @@ result_t ValueListDataField::readSymbols(SymbolString& input, std::ostringstream return result; if (value == m_dataType.replacement) { - output << "-"; + output << NULL_VALUE; return RESULT_OK; }