diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 411dd540..7a845899 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -18,30 +18,30 @@ */ #include "data.h" -#include "decode.h" -#include "encode.h" #include #include #include #include #include #include -#include #include +using namespace std; + /** the known data field types. */ static const dataType_t dataTypes[] = { + {"IGN",16*8,bt_str, IGN|ADJ, 0, 1, 16, 0, 0}, // >= 1 byte ignored data {"STR",16*8,bt_str, ADJ, ' ', 1, 16, 0, 0}, // >= 1 byte character string filled up with space {"HEX",16*8,bt_hexstr, ADJ, 0, 2, 47, 0, 0}, // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d - {"BDA", 32, bt_dat, BCD, 0, 10, 10, 0, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) + {"BDA", 32, bt_dat, BCD, 0, 10, 10, 0, 0}, // date with weekday in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is weekday Mon=0x00 - Sun=0x06) {"BDA", 24, bt_dat, BCD, 0, 10, 10, 0, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) - {"HDA", 32, bt_dat, 0, 0, 10, 10, 0, 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", 32, bt_dat, 0, 0, 10, 10, 0, 0}, // date with weekday, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is weekday Mon=0x01 - Sun=0x07)) {"HDA", 24, bt_dat, 0, 0, 10, 10, 0, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) // TODO remove duplicate of BDA {"BTI", 24, bt_tim, BCD|REV, 0, 8, 8, 0, 0}, // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) {"HTM", 16, bt_tim, 0, 0, 5, 5, 0, 0}, // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x17,0x3b) {"TTM", 8, bt_tim, 0, 0x90, 5, 5, 0, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) - {"BDY", 8, bt_num, DAY|LST, 0x07, 0, 6, 1, 0}, // weekday, "Mon" - "Sun" - {"HDY", 8, bt_num, DAY|LST, 0x00, 1, 7, 1, 0}, // weekday, "Mon" - "Sun" + {"BDY", 8, bt_num, DAY|LST, 0x07, 0, 6, 1, 0}, // weekday, "Mon" - "Sun" (0x00 - 0x06) [ebus type] + {"HDY", 8, bt_num, DAY|LST, 0x00, 1, 7, 1, 0}, // weekday, "Mon" - "Sun" (0x01 - 0x07) [Vaillant type] {"BCD", 8, bt_num, BCD|LST, 0xff, 0, 0x99, 1, 0}, // unsigned decimal in BCD, 0 - 99 {"UCH", 8, bt_num, LST, 0xff, 0, 0xfe, 1, 0}, // unsigned integer, 0 - 254 {"SCH", 8, bt_num, SIG, 0x80, 0x81, 0x7f, 1, 0}, // signed integer, -127 - +127 @@ -54,41 +54,14 @@ static const dataType_t dataTypes[] = { {"D2C", 16, bt_num, SIG, 0x8000, 0x8001, 0x7fff, 16, 2}, // signed number (fraction 1/16), -2047.9 - +2047.9 {"ULG", 32, bt_num, LST, 0xffffffff, 0, 0xfffffffe, 1, 0}, // unsigned integer, 0 - 4294967294 {"SLG", 32, bt_num, SIG, 0x80000000, 0x80000001, 0xffffffff, 1, 0}, // signed integer, -2147483647 - +2147483647 - {"BI0", 1, bt_num, LST, 0, 0, 0x1, 1, 0}, // single bit 0 - {"BI1", 1, bt_num, LST, 0, 0, 0x1, 1, 1}, // single bit 1 - {"BI2", 1, bt_num, LST, 0, 0, 0x1, 1, 2}, // single bit 2 - {"BI3", 1, bt_num, LST, 0, 0, 0x1, 1, 3}, // single bit 3 - {"BI4", 1, bt_num, LST, 0, 0, 0x1, 1, 4}, // single bit 4 - {"BI5", 1, bt_num, LST, 0, 0, 0x1, 1, 5}, // single bit 5 - {"BI6", 1, bt_num, LST, 0, 0, 0x1, 1, 6}, // single bit 6 - {"BI7", 1, bt_num, LST, 0, 0, 0x1, 1, 7}, // single bit 7 - {"B01", 2, bt_num, LST, 0, 0, 0x3, 1, 0}, // two bits 0-1 - {"B12", 2, bt_num, LST, 0, 0, 0x3, 1, 1}, // two bits 1-2 - {"B23", 2, bt_num, LST, 0, 0, 0x3, 1, 2}, // two bits 2-3 - {"B34", 2, bt_num, LST, 0, 0, 0x3, 1, 3}, // two bits 3-4 - {"B45", 2, bt_num, LST, 0, 0, 0x3, 1, 4}, // two bits 4-5 - {"B56", 2, bt_num, LST, 0, 0, 0x3, 1, 5}, // two bits 5-6 - {"B67", 2, bt_num, LST, 0, 0, 0x3, 1, 6}, // two bits 6-7 - {"B02", 3, bt_num, LST, 0, 0, 0x7, 1, 0}, // three bits 0-2 - {"B13", 3, bt_num, LST, 0, 0, 0x7, 1, 1}, // three bits 1-3 - {"B24", 3, bt_num, LST, 0, 0, 0x7, 1, 2}, // three bits 2-4 - {"B35", 3, bt_num, LST, 0, 0, 0x7, 1, 3}, // three bits 3-5 - {"B46", 3, bt_num, LST, 0, 0, 0x7, 1, 4}, // three bits 4-6 - {"B57", 3, bt_num, LST, 0, 0, 0x7, 1, 5}, // three bits 5-7 - {"B03", 4, bt_num, LST, 0, 0, 0xf, 1, 0}, // four bits 0-3 - {"B14", 4, bt_num, LST, 0, 0, 0xf, 1, 1}, // four bits 1-4 - {"B25", 4, bt_num, LST, 0, 0, 0xf, 1, 2}, // four bits 2-5 - {"B36", 4, bt_num, LST, 0, 0, 0xf, 1, 3}, // four bits 3-6 - {"B47", 4, bt_num, LST, 0, 0, 0xf, 1, 4}, // four bits 4-7 - {"B04", 5, bt_num, LST, 0, 0, 0x1f, 1, 0}, // five bits 0-4 - {"B15", 5, bt_num, LST, 0, 0, 0x1f, 1, 1}, // five bits 1-5 - {"B26", 5, bt_num, LST, 0, 0, 0x1f, 1, 2}, // five bits 2-6 - {"B37", 5, bt_num, LST, 0, 0, 0x1f, 1, 3}, // five bits 3-7 - {"B05", 6, bt_num, LST, 0, 0, 0x3f, 1, 0}, // six bits 0-5 - {"B16", 6, bt_num, LST, 0, 0, 0x3f, 1, 1}, // six bits 1-6 - {"B27", 6, bt_num, LST, 0, 0, 0x3f, 1, 2}, // six bits 2-7 - {"B06", 7, bt_num, LST, 0, 0, 0x7f, 1, 0}, // seven bits 0-6 - {"B17", 7, bt_num, LST, 0, 0, 0x7f, 1, 1}, // seven bits 1-7 + {"BI0", 7, bt_num, ADJ|LST, 0, 0, 0xef, 1, 0}, // bit 0 (up to 7 bits until bit 6) + {"BI1", 7, bt_num, ADJ|LST, 0, 0, 0x7f, 1, 1}, // bit 1 (up to 7 bits until bit 7) + {"BI2", 6, bt_num, ADJ|LST, 0, 0, 0x3f, 1, 2}, // bit 2 (up to 6 bits until bit 7) + {"BI3", 5, bt_num, ADJ|LST, 0, 0, 0x1f, 1, 3}, // bit 3 (up to 5 bits until bit 7) + {"BI4", 4, bt_num, ADJ|LST, 0, 0, 0x0f, 1, 4}, // bit 4 (up to 4 bits until bit 7) + {"BI5", 3, bt_num, ADJ|LST, 0, 0, 0x07, 1, 5}, // bit 5 (up to 3 bits until bit 7) + {"BI6", 2, bt_num, ADJ|LST, 0, 0, 0x03, 1, 6}, // bit 6 (up to 2 bits until bit 7) + {"BI7", 1, bt_num, ADJ|LST, 0, 0, 0x01, 1, 7}, // bit 7 }; @@ -97,10 +70,11 @@ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" #define FIELD_SEPARATOR ';' #define VALUE_SEPARATOR ',' +#define LENGTH_SEPARATOR ':' #define NULL_VALUE "-" #define MAX_POS 16 -unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result) { +unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length) { char* strEnd = NULL; unsigned int ret = strtoul(str, &strEnd, base); @@ -114,34 +88,37 @@ unsigned int parseInt(const char* str, int base, const unsigned int minValue, co result = RESULT_ERR_INVALID_ARG; // invalid value return 0; } + if (length != NULL) + *length = strEnd - str; + + result = RESULT_OK; return ret; } -result_t DataField::create(std::vector::iterator& it, - const std::vector::iterator end, - const std::map< std::string, DataField*> templates, +result_t DataField::create(vector::iterator& it, + const vector::iterator end, + const map< string, DataField*> templates, DataField*& returnField, const bool isSetMessage, const unsigned char dstAddress) { - std::vector fields; - std::string firstName, firstComment; + vector fields; + string firstName, firstComment; result_t result = RESULT_OK; while (it != end && result == RESULT_OK) { - std::string unit, comment; + string unit, comment; PartType partType; unsigned int divisor = 0; - unsigned char offset, length, offsetCnt = 0; const bool isTemplate = dstAddress == SYN; - std::string token; + string token; if (it == end) break; - // name;[pos];type[;[divisor|values][;[unit][;[comment]]]] - const std::string name = *it++; + // name;part;type[:len][;[divisor|values][;[unit][;[comment]]]] + const string name = *it++; if (it == end) break; - const char* posStr = (*it++).c_str(); + const char* partStr = (*it++).c_str(); if (it == end) break; @@ -149,76 +126,45 @@ result_t DataField::create(std::vector::iterator& it, firstName = name; firstComment = comment; } - if (dstAddress == BROADCAST || isMaster(dstAddress) - || (isTemplate == false && isSetMessage == true && posStr[0] != 0 && posStr[0] <= '9') - || posStr[0] == 'm') { // master data - partType = pt_masterData; - if (posStr[0] == 'm') - posStr++; + if (isTemplate == false && strcasecmp(partStr, "I") == 0) { + partType = pt_masterDataID; } - else if ((isTemplate == false && isSetMessage == false && posStr[0] != 0 && posStr[0] <= '9') - || posStr[0] == 's') { // slave data + else if (dstAddress == BROADCAST || isMaster(dstAddress) + || (isTemplate == false && isSetMessage == true && partStr[0] == 0) + || strcasecmp(partStr, "M") == 0) { // master data + partType = pt_masterData; + } + else if ((isTemplate == false && isSetMessage == false && partStr[0] == 0) + || strcasecmp(partStr, "S") == 0) { // slave data partType = pt_slaveData; - if (posStr[0] == 's') - posStr++; } else if (isTemplate) { - partType = pt_template; + partType = pt_any; } else { result = RESULT_ERR_INVALID_ARG; break; } - if (posStr[0] == 0) { - if (fields.empty() == false) - offset = fields.back()->getNextOffset(); - else - offset = 0; - length = 0; - } - else { - 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 - - unsigned int pos = parseInt(token.c_str(), 10, 1, MAX_POS + 1, result) - 1; // input is 1-based - if (result != RESULT_OK) - break; - - if (offsetCnt == 1) - offset = pos; - else if (pos >= offset) - length = pos + 1 - offset; - else { // wrong order e.g. 4-3 - length = offset - (pos + 1); - offset = pos; - } - } - if (result != RESULT_OK) - break; - } - - const char* typeStr = (*it++).c_str(); - if (typeStr[0] == 0) { + string typeStr = *it++; + if (typeStr.empty() == true) { + if (name.empty() == false || partStr[0] != 0) + result = RESULT_ERR_INVALID_ARG; break; } - std::map values; + map values; if (it != end) { - std::string divisorStr = *it++; + string divisorStr = *it++; if (divisorStr.empty() == false) { - if (divisorStr.find_first_not_of("0123456789") == std::string::npos) { + if (divisorStr.find('=') == string::npos) { divisor = parseInt(divisorStr.c_str(), 10, 1, 10000, result); if (result != RESULT_OK) break; } else { - std::istringstream stream(divisorStr); - while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { + istringstream stream(divisorStr); + while (getline(stream, token, VALUE_SEPARATOR) != 0) { const char* str = token.c_str(); char* strEnd = NULL; unsigned int id = strtoul(str, &strEnd, 10); @@ -227,7 +173,7 @@ result_t DataField::create(std::vector::iterator& it, break; } - values[id] = std::string(strEnd + 1); + values[id] = string(strEnd + 1); } if (result != RESULT_OK) break; @@ -239,8 +185,7 @@ result_t DataField::create(std::vector::iterator& it, unit = ""; else { unit = *it++; - - if (unit.length() == 1 && unit[0] == '-') + if (strcasecmp(unit.c_str(), NULL_VALUE) == 0) unit.clear(); } @@ -248,61 +193,80 @@ result_t DataField::create(std::vector::iterator& it, comment = ""; else { comment = *it++; - if (comment.length() == 1 && comment[0] == '-') + if (strcasecmp(comment.c_str(), NULL_VALUE) == 0) comment.clear(); } - // check for reference(s) to templates - if (templates.empty() == false) { - std::istringstream stream(typeStr); - bool found = false; - while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { - std::map< std::string, DataField*>::const_iterator ref = templates.find(token); - if (ref == templates.end()) { - if (found == false) - break; // fallback to direct definition - result = RESULT_ERR_INVALID_ARG; // cannot mix reference and direct definition - break; + size_t pos = typeStr.find(LENGTH_SEPARATOR); + unsigned char length; + if (pos == string::npos) { + length = 0; + // check for reference(s) to templates + if (templates.empty() == false) { + istringstream stream(typeStr); + bool found = false; + string lengthStr; + while (getline(stream, token, VALUE_SEPARATOR) != 0) { + map::const_iterator ref = templates.find(token); + if (ref == templates.end()) { + if (found == false) + break; // fallback to direct definition + result = RESULT_ERR_INVALID_ARG; // cannot mix reference and direct definition + break; + } + found = true; + result = ref->second->derive(name, comment, unit, partType, divisor, values, fields); + if (result != RESULT_OK) + break; } - if (length > 1) { - result = RESULT_ERR_INVALID_ARG; // different length not possible for derivation - break; - } - found = true; - result = ref->second->derive(name, comment, unit, partType, offset, divisor, values, fields); if (result != RESULT_OK) break; - offset = fields.back()->getNextOffset(); + if (found == true) + continue; // go to next definition } - if (offset > MAX_POS) { - result = RESULT_ERR_INVALID_ARG; // invalid pos definition - break; - } - if (found == true || result != RESULT_OK) - break; } + else { + length = parseInt(typeStr.substr(pos+1).c_str(), 10, 1, MAX_POS, result); + if (result != RESULT_OK) + break; + typeStr = typeStr.substr(0, pos); + } + SingleDataField* add = NULL; + const char* typeName = typeStr.c_str(); for (size_t i = 0; result == RESULT_OK && add == NULL && i < sizeof(dataTypes) / sizeof(dataTypes[0]); i++) { dataType_t dataType = dataTypes[i]; - if (strcasecmp(typeStr, dataType.name) == 0) { - unsigned char numBytes = (dataType.numBits + 7) / 8; - unsigned char useLength = length; - if ((dataType.flags & ADJ) != 0) { - if (useLength == 0) - useLength = 1; // minimum length defaults to 1 - else if (useLength > numBytes) { + if (strcasecmp(typeName, dataType.name) == 0) { + unsigned char bitCount = dataType.maxBits; + unsigned char useLength = (bitCount + 7) / 8; + if ((dataType.flags & ADJ) != 0) { // adjustable length + if ((bitCount % 8) != 0) { + if (length == 0) { + useLength = 1; // default length: 1 byte + bitCount = 1; // default count: 1 bit + } + else if (length > bitCount) { + result = RESULT_ERR_INVALID_ARG; // invalid length + break; + } + else { + bitCount = length; + useLength = (length + 7) / 8; + } + } + else if (length == 0) { + useLength = 1; // default length: 1 byte + } + else if (length <= useLength) { + useLength = length; + } + else { result = RESULT_ERR_INVALID_ARG; // invalid length break; } } - else if (useLength == 0) - useLength = numBytes; - else if (useLength != numBytes) + else if (length > 0 && length != useLength) continue; // check for another one with same name but different length - if (offset + useLength > MAX_POS) { - result = RESULT_ERR_INVALID_ARG; // invalid pos definition - break; - } switch (dataType.type) { @@ -310,7 +274,7 @@ result_t DataField::create(std::vector::iterator& it, case bt_hexstr: case bt_dat: case bt_tim: - add = new StringDataField(name, comment, unit, dataType, partType, offset, useLength); + add = new StringDataField(name, comment, unit, dataType, partType, useLength); break; case bt_num: if (values.empty() == true && (dataType.flags & DAY) != 0) { @@ -324,7 +288,7 @@ result_t DataField::create(std::vector::iterator& it, else divisor *= dataType.divisor; - add = new NumberDataField(name, comment, unit, dataType, partType, offset, useLength, divisor); + add = new NumberDataField(name, comment, unit, dataType, partType, useLength, bitCount, divisor); break; } if (values.begin()->first < dataType.minValueOrLength @@ -333,7 +297,7 @@ result_t DataField::create(std::vector::iterator& it, break; } - add = new ValueListDataField(name, comment, unit, dataType, partType, offset, useLength, values); + add = new ValueListDataField(name, comment, unit, dataType, partType, useLength, bitCount, values); break; } } @@ -360,37 +324,37 @@ result_t DataField::create(std::vector::iterator& it, } -unsigned char SingleDataField::getNextOffset() +result_t SingleDataField::read(SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + ostringstream& output, + bool verbose, char separator) { - unsigned char offset = m_offset + m_length; - if ((m_dataType.numBits % 8) != 0 - && m_dataType.precisionOrFirstBit + (m_dataType.numBits % 8) < 8) - offset--; // not all bits of last offset fully consumed - - return offset; -} - -result_t SingleDataField::read(SymbolString& masterData, SymbolString& slaveData, - std::ostringstream& output, bool verbose, char separator) -{ - SymbolString& input = m_partType == pt_masterData ? masterData : slaveData; - unsigned char baseOffset; + SymbolString& input = m_partType != pt_slaveData ? masterData : slaveData; + unsigned char offset; switch (m_partType) { case pt_masterData: - baseOffset = 5; // skip QQ ZZ PB SB NN + case pt_masterDataID: + offset = 5 + masterOffset; // skip QQ ZZ PB SB NN break; case pt_slaveData: - baseOffset = 1; // skip NN + offset = 1 + slaveOffset; // skip NN break; default: return RESULT_ERR_INVALID_ARG; // invalid part type } - if (verbose) + if (isIgnored() == true) { + if (offset + m_length > input.size()) { + return RESULT_ERR_INVALID_ARG; + } + return RESULT_OK; + } + + if (verbose == true) output << m_name << "="; - result_t result = readSymbols(input, baseOffset, output); + result_t result = readSymbols(input, offset, output); if (result != RESULT_OK) return result; @@ -402,69 +366,71 @@ result_t SingleDataField::read(SymbolString& masterData, SymbolString& slaveData return RESULT_OK; } -result_t SingleDataField::write(std::istringstream& input, SymbolString& masterData, - SymbolString& slaveData, char separator) +result_t SingleDataField::write(istringstream& input, + SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + char separator) { - SymbolString& output = m_partType == pt_masterData ? masterData : slaveData; - unsigned char baseOffset; + SymbolString& output = m_partType != pt_slaveData ? masterData : slaveData; + unsigned char offset; switch (m_partType) { case pt_masterData: - baseOffset = 5; // skip QQ ZZ PB SB NN + case pt_masterDataID: + offset = 5 + masterOffset; // skip QQ ZZ PB SB NN break; case pt_slaveData: - baseOffset = 1; // skip NN + offset = 1 + slaveOffset; // skip NN break; default: return RESULT_ERR_INVALID_ARG; } - return writeSymbols(input, baseOffset, output); + return writeSymbols(input, offset, output); } -result_t StringDataField::derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields) +result_t StringDataField::derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields) { - if (m_partType != pt_template && partType == pt_template) + if (m_partType != pt_any && partType == pt_any) return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance - if (values.empty() == false) - return RESULT_ERR_INVALID_ARG; // cannot set values for string field + if (divisor != 0 || values.empty() == false) + return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for string field if (name.empty() == true) name = m_name; if (comment.empty() == true) comment = m_comment; if (unit.empty() == true) unit = m_unit; - offset += m_offset; - fields.push_back(new StringDataField(name, comment, unit, m_dataType, partType, offset, m_length)); + fields.push_back(new StringDataField(name, comment, unit, m_dataType, partType, m_length)); return RESULT_OK; } result_t StringDataField::readSymbols(SymbolString& input, - unsigned char baseOffset, std::ostringstream& output) + unsigned char baseOffset, ostringstream& output) { - size_t start = m_offset, end = m_offset + m_length; + size_t start = 0, count = m_length; int incr = 1; unsigned char ch, last = 0; - if (baseOffset + end > input.size()) { + if (baseOffset + m_length > input.size()) { return RESULT_ERR_INVALID_ARG; } + if ((m_dataType.flags & REV) != 0) { // reverted binary representation (most significant byte first) - end = start - 1; - start = m_offset + m_length - 1; + start = m_length - 1; incr = -1; } - for (size_t offset = start, i = 0; offset != end; offset += incr, i++) { + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { if (m_length == 4 && i == 2 && m_dataType.type == bt_dat) continue; // skip weekday in between ch = input[baseOffset + offset]; - if ((m_dataType.flags & BCD) != 0) { + if ((m_dataType.flags & BCD) != 0 || m_dataType.type == bt_dat || (m_dataType.type == bt_tim && m_length > 2)) { if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) return RESULT_ERR_INVALID_ARG; // invalid BCD ch = (ch >> 4) * 10 + (ch & 0x0f); @@ -474,8 +440,8 @@ result_t StringDataField::readSymbols(SymbolString& input, case bt_hexstr: if (i > 0) output << ' '; - output << std::nouppercase << std::setw(2) << std::hex - << std::setfill('0') << static_cast(ch); + output << nouppercase << setw(2) << hex << setfill('0') + << static_cast(ch); break; case bt_dat: if (i + 1 == m_length) @@ -483,13 +449,14 @@ result_t StringDataField::readSymbols(SymbolString& input, else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12)) return RESULT_ERR_INVALID_ARG; // invalid date else - output << std::setw(2) << std::setfill('0') << static_cast(ch) << "."; + output << setw(2) << setfill('0') << static_cast(ch) << "."; break; case bt_tim: if (m_length == 1) { // truncated time if (i == 0) { ch /= 6; // hours offset -= incr; // repeat for minutes + count++; } else ch = (ch % 6) * 10; // minutes @@ -498,7 +465,7 @@ result_t StringDataField::readSymbols(SymbolString& input, return RESULT_ERR_INVALID_ARG; // invalid time if (i > 0) output << ":"; - output << std::setw(2) << std::setfill('0') << static_cast(ch); + output << setw(2) << setfill('0') << static_cast(ch); break; default: if (ch < 0x20) @@ -512,29 +479,32 @@ result_t StringDataField::readSymbols(SymbolString& input, return RESULT_OK; } -result_t StringDataField::writeSymbols(std::istringstream& input, +result_t StringDataField::writeSymbols(istringstream& input, unsigned char baseOffset, SymbolString& output) { - size_t start = m_offset, end = m_offset + m_length; + size_t start = 0, count = m_length; int incr = 1; - const char* str; - char* strEnd; - unsigned long int value = 0, last = 0; - std::string token; + unsigned long int value = 0, last = 0, lastLast = 0; + string token; if ((m_dataType.flags & REV) != 0) { // reverted binary representation (most significant byte first) - end = start - 1; - start = m_offset + m_length - 1; + start = m_length - 1; incr = -1; } + if (isIgnored() == true) { + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { + output[baseOffset + offset] = m_dataType.replacement; // fill up with replacement + } + return RESULT_OK; + } result_t result; size_t i = 0; - for (size_t offset = start; offset != end; offset += incr, i++) { + for (size_t offset = start; i < count; offset += incr, i++) { switch (m_dataType.type) { case bt_hexstr: - while (input.peek() == ' ') + while (input.eof() == false && input.peek() == ' ') input.get(); if (input.eof() == true) // no more digits value = m_dataType.replacement; // fill up with replacement @@ -542,12 +512,11 @@ result_t StringDataField::writeSymbols(std::istringstream& input, token.clear(); token.push_back(input.get()); if (input.eof() == true) - return RESULT_ERR_INVALID_ARG; + return RESULT_ERR_INVALID_ARG; // too short hex value token.push_back(input.get()); if (input.eof() == true) - return RESULT_ERR_INVALID_ARG; // invalid hex value + return RESULT_ERR_INVALID_ARG; // too short hex value - result_t result; value = parseInt(token.c_str(), 16, 0, 0xff, result); if (result != RESULT_OK) return result; // invalid hex value @@ -556,28 +525,49 @@ result_t StringDataField::writeSymbols(std::istringstream& input, case bt_dat: if (m_length == 4 && i == 2) continue; // skip weekday in between - if (std::getline(input, token, '.') == 0) + if (input.eof() == true || getline(input, token, '.') == 0) return RESULT_ERR_INVALID_ARG; // incomplete - value = parseInt(token.c_str(), 10, 0, 9999, result); + value = parseInt(token.c_str(), 10, 0, 2099, result); if (result != RESULT_OK) return result; // invalid date part - if (i + 1 == m_length && value >= 2000) - value -= 2000; - else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) + if (i + 1 == m_length) { + if (m_length == 4) { + // calculate local week day + struct tm t; + t.tm_min = t.tm_sec = 0; + t.tm_hour = 12; + t.tm_mday = lastLast; + t.tm_mon = last-1; // January=0 + t.tm_year = (value < 100 ? value + 2000 : value) - 1900; + t.tm_isdst = 0; // automatic + if (mktime(&t) < 0) + return RESULT_ERR_INVALID_ARG; // invalid date + unsigned char daysSinceSunday = (unsigned char)t.tm_wday; // Sun=0 + if ((m_dataType.flags & BCD) != 0) + output[baseOffset + offset - incr] = (6+daysSinceSunday) % 7; // Sun=0x06 + else + output[baseOffset + offset - incr] = (daysSinceSunday==0 ? 7 : daysSinceSunday); // Sun=0x07 + } + if (value >= 2000) + value -= 2000; + else if (value > 99) + return RESULT_ERR_INVALID_ARG; // invalid year + } else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) return RESULT_ERR_INVALID_ARG; // invalid date part break; case bt_tim: - if (std::getline(input, token, ':') == 0) + if (input.eof() == true || getline(input, token, LENGTH_SEPARATOR) == 0) return RESULT_ERR_INVALID_ARG; // incomplete value = parseInt(token.c_str(), 10, 0, 59, result); if (result != RESULT_OK) return result; // invalid time part - if ((i == 0 && value > 24) || (i > 0 && ( last == 24 && value > 0) )) + if ((i == 0 && value > 24) || (i > 0 && (last == 24 && value > 0) )) return RESULT_ERR_INVALID_ARG; // invalid time part if (m_length == 1) { // truncated time if (i == 0) { - offset -= incr; // repeat for minutes last = value; + offset -= incr; // repeat for minutes + count++; continue; } if ((value % 10) != 0) @@ -588,20 +578,25 @@ result_t StringDataField::writeSymbols(std::istringstream& input, } break; default: - value = input.get(); - if (input.eof() == true || value < 0x20) + if (input.eof() == true) value = m_dataType.replacement; + else { + value = input.get(); + if (input.eof() == true || value < 0x20) + value = m_dataType.replacement; + } break; } - if ((m_dataType.flags & BCD) != 0) { + lastLast = last; + last = value; + if ((m_dataType.flags & BCD) != 0 || m_dataType.type == bt_dat || (m_dataType.type == bt_tim && m_length > 2)) { if (value > 99) return RESULT_ERR_INVALID_ARG; // invalid BCD - value = (value / 10) << 4 | (value % 10); + value = ((value / 10) << 4) | (value % 10); } if (value > 0xff) return RESULT_ERR_INVALID_ARG; // value out of range output[baseOffset + offset] = (unsigned char)value; - last = value; } if (i < m_length) @@ -611,24 +606,29 @@ result_t StringDataField::writeSymbols(std::istringstream& input, } +bool NumericDataField::hasFullByteOffset(bool after) +{ + return m_length > 1 || (m_bitCount % 8) == 0 + || (after == true && m_bitOffset + (m_bitCount % 8) >= 8); +} + result_t NumericDataField::readRawValue(SymbolString& input, unsigned char baseOffset, unsigned int& value) { - size_t start = m_offset, end = m_offset + m_length; + size_t start = 0, count = m_length; int incr = 1; unsigned char ch; - if (baseOffset + end > input.size()) + if (baseOffset + m_length > input.size()) 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; - start = m_offset + m_length - 1; + start = m_length - 1; incr = -1; } value = 0; - for (size_t offset = start, exp = 1; offset != end; offset += incr) { + for (size_t offset = start, i = 0, exp = 1; i < count; offset += incr, i++) { ch = input[baseOffset + offset]; if ((m_dataType.flags & BCD) != 0) { if (ch == m_dataType.replacement) { @@ -650,31 +650,32 @@ result_t NumericDataField::readRawValue(SymbolString& input, if ((m_dataType.flags & BCD) == 0) { value >>= m_bitOffset; - if ((m_dataType.numBits % 8) != 0) - value &= (1 << m_dataType.numBits) - 1; + if ((m_bitCount % 8) != 0) + value &= (1 << m_bitCount) - 1; } + return RESULT_OK; } result_t NumericDataField::writeRawValue(unsigned int value, unsigned char baseOffset, SymbolString& output) { - size_t start = m_offset, end = m_offset + m_length; + size_t start = 0, count = m_length; int incr = 1; unsigned char ch; if ((m_dataType.flags & REV) != 0) { // reverted binary representation (most significant byte first) - end = start - 1; - start = m_offset + m_length - 1; + start = m_length - 1; incr = -1; } if ((m_dataType.flags & BCD) == 0) { - if ((m_dataType.numBits % 8) != 0) - value &= (1 << m_dataType.numBits) - 1; + if ((m_bitCount % 8) != 0 && (value & ~((1 << m_bitCount) - 1)) != 0) + return RESULT_ERR_INVALID_ARG; + value <<= m_bitOffset; } - for (size_t offset = start, exp = 1; offset != end; offset += incr) { + for (size_t offset = start, i = 0, exp = 1; i < count; offset += incr, i++) { if ((m_dataType.flags & BCD) != 0) { if (value == m_dataType.replacement) ch = m_dataType.replacement; @@ -688,7 +689,7 @@ result_t NumericDataField::writeRawValue(unsigned int value, ch = (value / exp) & 0xff; exp = exp << 8; } - if (offset == start && (m_dataType.numBits % 8) != 0 && baseOffset + offset < output.size()) + if (offset == start && (m_bitCount % 8) != 0 && baseOffset + offset < output.size()) output[baseOffset + offset] |= ch; else output[baseOffset + offset] = ch; @@ -698,12 +699,12 @@ result_t NumericDataField::writeRawValue(unsigned int value, } -result_t NumberDataField::derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields) +result_t NumberDataField::derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields) { - if (m_partType != pt_template && partType == pt_template) + if (m_partType != pt_any && partType == pt_any) return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance if (name.empty() == true) name = m_name; @@ -711,7 +712,6 @@ result_t NumberDataField::derive(std::string name, std::string comment, comment = m_comment; if (unit.empty() == true) unit = m_unit; - offset += m_offset; if (divisor == 0) divisor = m_divisor; else @@ -720,16 +720,16 @@ result_t NumberDataField::derive(std::string name, std::string comment, if (divisor != 1) return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field - fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, offset, m_length, values)); + fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, m_length, m_bitCount, values)); } else - fields.push_back(new NumberDataField(name, comment, unit, m_dataType, partType, offset, m_length, divisor)); + fields.push_back(new NumberDataField(name, comment, unit, m_dataType, partType, m_length, m_bitCount, divisor)); return RESULT_OK; } result_t NumberDataField::readSymbols(SymbolString& input, - unsigned char baseOffset, std::ostringstream& output) + unsigned char baseOffset, ostringstream& output) { unsigned int value = 0; int signedValue; @@ -743,41 +743,40 @@ result_t NumberDataField::readSymbols(SymbolString& input, return RESULT_OK; } - bool negative = (m_dataType.flags & SIG) != 0 && (value & (1 << (m_dataType.numBits - 1))) != 0; - if (m_dataType.numBits == 32) { + bool negative = (m_dataType.flags & SIG) != 0 && (value & (1 << (m_bitCount - 1))) != 0; + if (m_bitCount == 32) { if (negative == false) { if (m_divisor <= 1) output << static_cast(value); else - output << std::setprecision((m_dataType.numBits % 8) == 0 ? m_dataType.precisionOrFirstBit : 0) - << std::fixed << static_cast(value / (float) m_divisor); + output << setprecision((m_bitCount % 8) == 0 ? m_dataType.precisionOrFirstBit : 0) + << fixed << static_cast(value / (float) m_divisor); return RESULT_OK; } signedValue = (int) value; // negative signed value } else if (negative) // negative signed value - signedValue = (int) value - (1 << m_dataType.numBits); + signedValue = (int) value - (1 << m_bitCount); else signedValue = (int) value; if (m_divisor <= 1) output << static_cast(signedValue); else - output << std::setprecision((m_dataType.numBits % 8) == 0 ? m_dataType.precisionOrFirstBit : 0) - << std::fixed << static_cast(signedValue / (float) m_divisor); + output << setprecision((m_bitCount % 8) == 0 ? m_dataType.precisionOrFirstBit : 0) + << fixed << static_cast(signedValue / (float) m_divisor); return RESULT_OK; } -result_t NumberDataField::writeSymbols(std::istringstream& input, +result_t NumberDataField::writeSymbols(istringstream& input, unsigned char baseOffset, SymbolString& output) { unsigned int value; const char* str = input.str().c_str(); - if (strcasecmp(str, NULL_VALUE) == 0) - // replacement value - value = m_dataType.replacement; + if (isIgnored() == true || strcasecmp(str, NULL_VALUE) == 0) + value = m_dataType.replacement; // replacement value else if (str == NULL || *str == 0) return RESULT_ERR_INVALID_ARG; // input too short else { @@ -785,8 +784,8 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, if (m_divisor <= 1) { if ((m_dataType.flags & SIG) != 0) { int signedValue = strtol(str, &strEnd, 10); - if (signedValue < 0 && m_dataType.numBits != 32) - value = (unsigned int) (signedValue + (1 << m_dataType.numBits)); + if (signedValue < 0 && m_bitCount != 32) + value = (unsigned int) (signedValue + (1 << m_bitCount)); else value = (unsigned int) signedValue; } @@ -798,14 +797,14 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, else { char* strEnd = NULL; double dvalue = strtod(str, &strEnd); - if (strEnd == NULL || strEnd != 0) + if (strEnd == NULL || *strEnd != 0) return RESULT_ERR_INVALID_ARG; // invalid value dvalue = round(dvalue * m_divisor); if ((m_dataType.flags & SIG) != 0) { if (dvalue < -(1LL << (8 * m_length)) || dvalue >= (1LL << (8 * m_length))) return RESULT_ERR_INVALID_ARG; // value out of range - if (dvalue < 0 && m_dataType.numBits != 32) - value = (unsigned int) (dvalue + (1 << m_dataType.numBits)); + if (dvalue < 0 && m_bitCount != 32) + value = (unsigned int) (dvalue + (1 << m_bitCount)); else value = (unsigned int) dvalue; } @@ -817,7 +816,7 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, } if ((m_dataType.flags & SIG) != 0) { // signed value - if ((value & (1 << (m_dataType.numBits - 1))) != 0) { // negative signed value + if ((value & (1 << (m_bitCount - 1))) != 0) { // negative signed value if (value < m_dataType.minValueOrLength) return RESULT_ERR_INVALID_ARG; // value out of range } @@ -832,12 +831,12 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, } -result_t ValueListDataField::derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields) +result_t ValueListDataField::derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields) { - if (m_partType != pt_template && partType == pt_template) + if (m_partType != pt_any && partType == pt_any) return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance if (name.empty() == true) name = m_name; @@ -845,7 +844,6 @@ result_t ValueListDataField::derive(std::string name, std::string comment, comment = m_comment; if (unit.empty() == true) unit = m_unit; - offset += m_offset; if (divisor != 0 && divisor != 1) return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field @@ -857,13 +855,13 @@ result_t ValueListDataField::derive(std::string name, std::string comment, else values = m_values; - fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, offset, m_length, values)); + fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, m_length, m_bitCount, values)); return RESULT_OK; } result_t ValueListDataField::readSymbols(SymbolString& input, - unsigned char baseOffset, std::ostringstream& output) + unsigned char baseOffset, ostringstream& output) { unsigned int value = 0; @@ -871,7 +869,7 @@ result_t ValueListDataField::readSymbols(SymbolString& input, if (result != RESULT_OK) return result; - std::map::iterator it = m_values.find(value); + map::iterator it = m_values.find(value); if (it != m_values.end()) { output << it->second; return RESULT_OK; @@ -885,17 +883,20 @@ result_t ValueListDataField::readSymbols(SymbolString& input, return RESULT_ERR_INVALID_ARG; // value assignment not found } -result_t ValueListDataField::writeSymbols(std::istringstream& input, +result_t ValueListDataField::writeSymbols(istringstream& input, unsigned char baseOffset, SymbolString& output) { + if (isIgnored() == true) + return writeRawValue(m_dataType.replacement, baseOffset, output); // replacement value + const char* str = input.str().c_str(); - for (std::map::iterator it = m_values.begin(); it != m_values.end(); it++) + for (map::iterator it = m_values.begin(); it != m_values.end(); it++) if (it->second.compare(str) == 0) return writeRawValue(it->first, baseOffset, output); - if (strcasecmp(str, NULL_VALUE) == 0) // replacement value - return writeRawValue(m_dataType.replacement, baseOffset, output); + if (strcasecmp(str, NULL_VALUE) == 0) + return writeRawValue(m_dataType.replacement, baseOffset, output); // replacement value return RESULT_ERR_INVALID_ARG; // value assignment not found } @@ -908,21 +909,37 @@ DataFieldSet::~DataFieldSet() } } -unsigned char DataFieldSet::getNextOffset() +unsigned char DataFieldSet::getLength(PartType partType) { - return m_fields.back()->getNextOffset(); + unsigned char length = 0; + + bool previousFullByteOffset[] = { true, true, true, true }; + + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + if (field->getPartType() == partType) { + if (previousFullByteOffset[partType] == false && field->hasFullByteOffset(false) == false) + length--; + + length += field->getLength(partType); + + previousFullByteOffset[partType] = field->hasFullByteOffset(true); + } + } + + return length; } -result_t DataFieldSet::derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields) +result_t DataFieldSet::derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields) { if (values.empty() == false) return RESULT_ERR_INVALID_ARG; // value list not allowed in set derive - for (std::vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - result_t result = (*it)->derive("", "", "", partType, offset, divisor, values, fields); + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + result_t result = (*it)->derive("", "", "", partType, divisor, values, fields); if (result != RESULT_OK) return result; } @@ -930,26 +947,46 @@ result_t DataFieldSet::derive(std::string name, std::string comment, return RESULT_OK; } -result_t DataFieldSet::read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, - bool verbose, char separator) +result_t DataFieldSet::read(SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + ostringstream& output, bool verbose, char separator) { if (verbose) output << m_name << "={ "; bool first = true; - for (std::vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - if (first) - first = false; - else - output << separator; + unsigned char offsets[4]; + memset(offsets, 0, sizeof(offsets)); + offsets[pt_masterData] = masterOffset; + offsets[pt_slaveData] = slaveOffset; + bool previousFullByteOffset[] = { true, true, true, true }; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + bool ignored = field->isIgnored(); + PartType partType = field->getPartType(); - result_t result = (*it)->read(masterData, slaveData, output, verbose); + if (ignored == false) { + if (first) + first = false; + else + output << separator; + } + if (partType == pt_masterDataID) + partType = pt_masterData; + if (previousFullByteOffset[partType] == false && field->hasFullByteOffset(false) == false) + offsets[partType]--; + + result_t result = field->read(masterData, offsets[pt_masterData], slaveData, offsets[pt_slaveData], output, verbose, separator); if (result != RESULT_OK) return result; + + offsets[partType] += field->getLength(partType); + + previousFullByteOffset[partType] = field->hasFullByteOffset(true); } - if (verbose) { + if (verbose == true) { if (m_comment.length() > 0) output << " [" << m_comment << "]"; output << "}"; @@ -958,24 +995,46 @@ result_t DataFieldSet::read(SymbolString& masterData, SymbolString& slaveData, s return RESULT_OK; } -result_t DataFieldSet::write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, +result_t DataFieldSet::write(istringstream& input, + SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, char separator) { - std::string token; + string token; + + unsigned char offsets[4]; + memset(offsets, 0, sizeof(offsets)); + offsets[pt_masterData] = masterOffset; + offsets[pt_slaveData] = slaveOffset; + bool previousFullByteOffset[] = { true, true, true, true }; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + bool ignored = field->isIgnored(); + PartType partType = field->getPartType(); + + if (partType == pt_masterDataID) + partType = pt_masterData; + if (previousFullByteOffset[partType] == false && field->hasFullByteOffset(false) == false) + offsets[partType]--; - for (std::vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { result_t result; if (m_fields.size() > 1) { - if (std::getline(input, token, separator) == 0) + if (ignored == true) + token.clear(); + else if (getline(input, token, separator) == 0) return RESULT_ERR_INVALID_ARG; // incomplete - std::istringstream single(token); - result = (*it)->write(single, masterData, slaveData); + + istringstream single(token); + result = (*it)->write(single, masterData, offsets[pt_masterData], slaveData, offsets[pt_slaveData], separator); } else - result = (*it)->write(input, masterData, slaveData); + result = (*it)->write(input, masterData, offsets[pt_masterData], slaveData, offsets[pt_slaveData], separator); if (result != RESULT_OK) return result; + + offsets[partType] += field->getLength(partType); + previousFullByteOffset[partType] = field->hasFullByteOffset(true); } return RESULT_OK; diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index daa56d65..a9cf9579 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -26,11 +26,14 @@ #include #include +using namespace std; + /** the message part in which a data field is stored. */ enum PartType { - pt_template, // special part type for templates (relative offset) - pt_masterData, // stored in master data - pt_slaveData, // stored in slave data + pt_any, // stored in any data (master or slave) + pt_masterData, // stored in master data + pt_masterDataID, // stored in master data and also used as message ID part + pt_slaveData, // stored in slave data }; /** the available base data types. */ @@ -49,11 +52,12 @@ const unsigned int REV = 0x04; // reverted binary representation (most significa const unsigned int SIG = 0x08; // signed value const unsigned int LST = 0x10; // value list is possible (without applied divisor) const unsigned int DAY = 0x20; // forced value list defaulting to week days +const unsigned int IGN = 0x40; // ignore value during read and write /** the structure for defining field types with their properties. */ typedef struct { const char* name; // field identifier - const unsigned int numBits; // number of bits (maximum length if @a ADJ flag is set, must be multiple of 8 with flag BCD) + const unsigned int maxBits; // number of bits (maximum length if @a ADJ flag is set, must be multiple of 8 with flag @a BCD) const BaseType type; // base data type const unsigned int flags; // flags (e.g. @a BCD) const unsigned int replacement; // replacement value (fill-up value for @a bt_str / @a bt_hexstr, no replacement if equal to @a minValueOrLength for @a bt_num) @@ -71,8 +75,9 @@ typedef struct { * @param minValue the minimum resulting value. * @param maxValue the maximum resulting value. * @param result the variable in which to store an error code when parsing failed or the value is out of bounds. + * @param length the optional variable in which to store the number of read characters. */ -unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result); +unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length=NULL); class SingleDataField; @@ -89,7 +94,7 @@ public: * @param name the field name. * @param comment the field comment. */ - DataField(const std::string name, const std::string comment) + DataField(const string name, const string comment) : m_name(name), m_comment(comment) {} /** * @brief Destructor. @@ -106,68 +111,72 @@ public: * @return @a RESULT_OK on success, or an error code. * Note: the caller needs to free the created instance. */ - static result_t create(std::vector::iterator& it, const std::vector::iterator end, - const std::map templates, DataField*& returnField, + static result_t create(vector::iterator& it, const vector::iterator end, + const map templates, DataField*& returnField, const bool isSetMessage=false, const unsigned char dstAddress=SYN); /** - * @brief Returns the offset to the first symbol in the message part for a field following this field. - * @return the offset to the first symbol in the message part for a field following this field. + * @brief Returns the length of this field (or contained fields) in bytes. + * @param partType the message part of the contained fields to limit the length calculation to. + * @return the length of this field (or contained fields) in bytes. */ - virtual unsigned char getNextOffset() = 0; + virtual unsigned char getLength(PartType partType) = 0; /** * @brief Derives a new DataField from this field. * @param name the field name. * @param comment the field comment, or empty to use this fields comment. * @param unit the value unit, or empty to use this fields unit (if applicable). * @param partType the message part in which the field is stored. - * @param offset the (additional) offset to the first symbol in the message part in which the field is stored. * @param divisor the extra divisor to apply on the value, or 1 for none (if applicable). * @param values the value=text assignments, or empty to use this fields assignments (if applicable). - * @param fields the @a std::vector to which created @a SingleDataField instances shall be added. + * @param fields the @a vector to which created @a SingleDataField instances shall be added. */ - virtual result_t derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields) = 0; + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields) = 0; /** * @brief Get the field name. * @return the field name. */ - const std::string getName() { return m_name; } + string getName() const { return m_name; } /** * @brief Get the field comment. * @return the field comment. */ - const std::string getComment() { return m_comment; } + string getComment() const { return m_comment; } /** * @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. - * @param output the @a std::ostringstream to append the formatted value to. + * @param output the @a ostringstream to append the formatted value to. * @param verbose whether to prepend the name, append the unit (if present), and append * the comment in square brackets (if present). * @param separator the separator character between multiple fields. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, + virtual result_t read(SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + ostringstream& output, bool verbose=false, char separator=';') = 0; /** * @brief Writes the value to the master or slave @a SymbolString. - * @param input the @a std::istringstream to parse the formatted value from. + * @param input the @a istringstream to parse the formatted value from. * @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 separator the separator character between multiple fields. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, + virtual result_t write(istringstream& input, + SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, char separator=';') = 0; protected: /** the field name. */ - const std::string m_name; + const string m_name; /** the field comment. */ - const std::string m_comment; + const string m_comment; }; @@ -186,15 +195,14 @@ public: * @param unit the value unit. * @param dataType the data type definition. * @param partType the message part in which the field is stored. - * @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. */ - SingleDataField(const std::string name, const std::string comment, - const std::string unit, const dataType_t dataType, const PartType partType, - const unsigned char offset, const unsigned char length) + SingleDataField(const string name, const string comment, + const string unit, const dataType_t dataType, const PartType partType, + const unsigned char length) : DataField(name, comment), m_unit(unit), m_dataType(dataType), m_partType(partType), - m_offset(offset), m_length(length) {} + m_length(length) {} /** * @brief Destructor. */ @@ -203,55 +211,81 @@ public: * @brief Get the value unit. * @return the value unit. */ - const std::string getUnit() { return m_unit; } + string getUnit() const { return m_unit; } + /** + * @brief Get whether this field is ignored. + * @return whether this field is ignored. + */ + bool isIgnored() const { return (m_dataType.flags & IGN) != 0; } + /** + * @brief Get the message part in which the field is stored. + * @return the message part in which the field is stored. + */ + PartType getPartType() const { return m_partType; } // @copydoc - virtual unsigned char getNextOffset(); + virtual unsigned char getLength(PartType partType) { return partType == m_partType ? m_length : 0; }; + // re-use same position as previous field as not all bits of fully consumed yet + /** + * @brief Get whether this field uses a full byte offset. + * @param after @p true to check after consuming the bits, false to check before. + * @return true if this field uses a full byte offset, false if this field + * only consumes a part of a byte and a subsequent field may re-use the same offset. + */ + virtual bool hasFullByteOffset(bool after) { return true; } /** * @brief Reads the value from the master or slave @a SymbolString. * @param masterData the unescaped master data @a SymbolString for reading binary data. + * @param masterOffset the extra offset for reading master data. * @param slaveData the unescaped slave data @a SymbolString for reading binary data. + * @param slaveOffset the extra offset for reading slave data. * @param output the ostringstream to append the formatted value to. * @param verbose whether to prepend the name, append the unit (if present), and append * the comment in square brackets (if present). * @return @a RESULT_OK on success, or an error code. */ - virtual result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, - bool verbose=false, char separator=';'); + virtual result_t read(SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + ostringstream& output, + bool verbose, char separator); /** * @brief Writes the value to the master or slave @a SymbolString. - * @param input the @a std::istringstream to parse the formatted value from. + * @param input the @a istringstream to parse the formatted value from. * @param masterData the unescaped master data @a SymbolString for writing binary data. + * @param masterOffset the extra offset for writing master data. * @param slaveData the unescaped slave data @a SymbolString for writing binary data. + * @param slaveOffset the extra offset for writing slave data. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, - char separator=';'); + virtual result_t write(istringstream& input, + SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + char separator); 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 offset the offset in the @a SymbolString. * @param output the ostringstream to append the formatted value to. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t readSymbols(SymbolString& input, unsigned char baseOffset, std::ostringstream& output) = 0; + virtual result_t readSymbols(SymbolString& input, const unsigned char offset, ostringstream& output) = 0; /** * @brief Internal method for writing the field to a @a SymbolString. - * @param input the @a std::istringstream to parse the formatted value from. + * @param input the @a istringstream to parse the formatted value from. + * @param offset the offset in the @a SymbolString. * @param output the unescaped @a SymbolString to write the binary value to. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output) = 0; + virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output) = 0; /** the value unit. */ - const std::string m_unit; + const string m_unit; /** the data type definition. */ const dataType_t m_dataType; /** the message part in which the field is stored. */ const PartType m_partType; - /** the relative offset to the first symbol in the message part in which the field is stored. */ - const unsigned char m_offset; /** the number of symbols in the message part in which the field is stored. */ const unsigned char m_length; @@ -272,29 +306,28 @@ public: * @param unit the value unit. * @param dataType the data type definition. * @param partType the message part in which the field is stored. - * @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. */ - StringDataField(const std::string name, const std::string comment, - const std::string unit, const dataType_t dataType, const PartType partType, - const unsigned char offset, const unsigned char length) - : SingleDataField(name, comment, unit, dataType, partType, offset, length) {} + StringDataField(const string name, const string comment, + const string unit, const dataType_t dataType, const PartType partType, + const unsigned char length) + : SingleDataField(name, comment, unit, dataType, partType, length) {} /** * @brief Destructor. */ virtual ~StringDataField() {} // @copydoc - virtual result_t derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields); + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields); protected: // @copydoc - virtual result_t readSymbols(SymbolString& input, unsigned char baseOffset, std::ostringstream& output); + virtual result_t readSymbols(SymbolString& input, const unsigned char offset, ostringstream& output); // @copydoc - virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output); + virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output); }; @@ -313,41 +346,48 @@ public: * @param unit the value unit. * @param dataType the data type definition. * @param partType the message part in which the field is stored. - * @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 bitCount the number of bits in the binary value. * @param bitOffset the offset to the first bit in the binary value. */ - NumericDataField(const std::string name, const std::string comment, - const std::string unit, const dataType_t dataType, const PartType partType, - const unsigned char offset, const unsigned char length, - const unsigned char bitOffset) - : SingleDataField(name, comment, unit, dataType, partType, offset, length), - m_bitOffset(bitOffset) {} + NumericDataField(const string name, const string comment, + const string unit, const dataType_t dataType, const PartType partType, + const unsigned char length, const unsigned char bitCount, const unsigned char bitOffset) + : SingleDataField(name, comment, unit, dataType, partType, length), + m_bitCount(bitCount), m_bitOffset(bitOffset) {} /** * @brief Destructor. */ virtual ~NumericDataField() {} + // @copydoc + virtual bool hasFullByteOffset(bool after); 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 offset the offset in the @a SymbolString. * @param value the variable in which to store the raw value. * @return @a RESULT_OK on success, or an error code. */ - result_t readRawValue(SymbolString& input, unsigned char baseOffset, unsigned int& value); + result_t readRawValue(SymbolString& input, const unsigned char offset, unsigned int& value); /** * @brief Internal method for writing the raw value to a @a SymbolString. * @param value the raw value to write. + * @param offset the offset in the @a SymbolString. * @param output the unescaped @a SymbolString to write the binary value to. * @return @a RESULT_OK on success, or an error code. */ - result_t writeRawValue(unsigned int value, unsigned char baseOffset, SymbolString& output); + result_t writeRawValue(unsigned int value, const unsigned char offset, SymbolString& output); + + /** the number of bits in the binary value. */ + const unsigned char m_bitCount; /** the offset to the first bit in the binary value. */ const unsigned char m_bitOffset; + }; @@ -365,33 +405,32 @@ public: * @param unit the value unit. * @param dataType the data type definition. * @param partType the message part in which the field is stored. - * @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 divisor the extra divisor to apply on the value, or 1 for none. */ - NumberDataField(const std::string name, const std::string comment, - const std::string unit, const dataType_t dataType, const PartType partType, - const unsigned char offset, const unsigned char length, + NumberDataField(const string name, const string comment, + const string unit, const dataType_t dataType, const PartType partType, + const unsigned char length, const unsigned char bitCount, const unsigned int divisor) - : NumericDataField(name, comment, unit, dataType, partType, offset, length, - (dataType.numBits%8) != 0 ? dataType.precisionOrFirstBit : 0), + : NumericDataField(name, comment, unit, dataType, partType, length, bitCount, + (dataType.maxBits < 8) ? dataType.precisionOrFirstBit : 0), m_divisor(divisor) {} /** * @brief Destructor. */ virtual ~NumberDataField() {} + // @copydoc + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields); protected: // @copydoc - virtual result_t derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields); + virtual result_t readSymbols(SymbolString& input, const unsigned char offset, ostringstream& output); // @copydoc - virtual result_t readSymbols(SymbolString& input, unsigned char baseOffset, std::ostringstream& output); - // @copydoc - virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output); + virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output); /** the combined divisor to apply on the value, or 1 for none. */ const unsigned int m_divisor; @@ -413,36 +452,35 @@ public: * @param unit the value unit. * @param dataType the data type definition. * @param partType the message part in which the field is stored. - * @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 values the value=text assignments. */ - ValueListDataField(const std::string name, const std::string comment, - const std::string unit, const dataType_t dataType, const PartType partType, - const unsigned char offset, const unsigned char length, - const std::map values) - : NumericDataField(name, comment, unit, dataType, partType, offset, length, - (dataType.numBits%8) != 0 ? dataType.precisionOrFirstBit : 0), + ValueListDataField(const string name, const string comment, + const string unit, const dataType_t dataType, const PartType partType, + const unsigned char length, const unsigned char bitCount, + const map values) + : NumericDataField(name, comment, unit, dataType, partType, length, bitCount, + (dataType.maxBits < 8) ? dataType.precisionOrFirstBit : 0), m_values(values) {} /** * @brief Destructor. */ virtual ~ValueListDataField() {} + // @copydoc + virtual result_t derive(string name, string comment, + string unit, const PartType partType, unsigned int divisor, + map values, + vector& fields); protected: // @copydoc - virtual result_t derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields); + virtual result_t readSymbols(SymbolString& input, const unsigned char offset, ostringstream& output); // @copydoc - virtual result_t readSymbols(SymbolString& input, unsigned char baseOffset, std::ostringstream& output); - // @copydoc - virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output); + virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output); /** the value=text assignments. */ - std::map m_values; + map m_values; }; @@ -458,10 +496,10 @@ public: * @brief Constructs a new instance. * @param name the field name. * @param comment the field comment. - * @param fields the @a std::vector of @a SingleDataField instances part of this set. + * @param fields the @a vector of @a SingleDataField instances part of this set. */ - DataFieldSet(const std::string name, const std::string comment, - const std::vector fields) + DataFieldSet(const string name, const string comment, + const vector fields) : DataField(name, comment), m_fields(fields) {} /** @@ -469,12 +507,12 @@ public: */ virtual ~DataFieldSet(); // @copydoc - virtual unsigned char getNextOffset(); + virtual unsigned char getLength(PartType partType); // @copydoc - virtual result_t derive(std::string name, std::string comment, - std::string unit, const PartType partType, unsigned char offset, - unsigned int divisor, std::map values, - std::vector& fields); + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + unsigned int divisor, map values, + vector& fields); /** * @brief Returns the @a SingleDataField at the specified index. * @param index the index of the @a SingleDataField to return. @@ -492,31 +530,21 @@ public: * @return the number of available @a SingleDataField instances. */ size_t size() const { return m_fields.size(); } - /** - * @brief Reads the values from the master and/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. - * @param output the @a std::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 @a RESULT_OK on success, or an error code. - */ - virtual result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, - bool verbose=false, char separator=';'); - /** - * @brief Writes the values to the master and/or slave @a SymbolString. - * @param input the @a std::istringstream to parse the formatted value from. - * @param masterData the unescaped master data @a SymbolString for writing binary data. - * @param slaveData the unescaped slave data @a SymbolString for writing binary data. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, - char separator=';'); + // @copydoc + virtual result_t read(SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + ostringstream& output, + bool verbose, char separator); + // @copydoc + virtual result_t write(istringstream& input, + SymbolString& masterData, unsigned char masterOffset, + SymbolString& slaveData, unsigned char slaveOffset, + char separator); protected: - /** the @a std::vector of @a SingleDataField instances part of this set. */ - std::vector m_fields; + /** the @a vector of @a SingleDataField instances part of this set. */ + vector m_fields; }; diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp new file mode 100644 index 00000000..6bd69d92 --- /dev/null +++ b/src/lib/ebus/message.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) John Baier 2014 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#include "message.h" +#include "data.h" +#include "result.h" +#include "symbol.h" +#include +#include +#include + +using namespace std; + +result_t Message::create(vector::iterator& it, const vector::iterator end, + const map templates, Message*& returnValue) +{ + result_t result; + // [type];class;name;[comment];[QQ];ZZ;id;fields... + if (it == end) + return RESULT_ERR_EOF; + + const char* str = (*it++).c_str(); + if (it == end) + return RESULT_ERR_EOF; + bool isSetMessage, isActiveMessage; + unsigned int pollPriority = 0; + if (strcasecmp(str, "W") == 0) { + isActiveMessage = true; + isSetMessage = true; + } else if (str[0] == 'C' || str[0] == 'c') { + isActiveMessage = false; + isSetMessage = str[1] == 'W' || str[1] == 'w'; + } else if (str[0] == 'P' || str[0] == 'p') { + isActiveMessage = true; + isSetMessage = false; + if (str[1] == 0) + pollPriority = 1; + else { + result_t result; + pollPriority = parseInt(str+1, 10, 1, 9, result); + if (result != RESULT_OK) + return result; + } + } else { + isActiveMessage = true; + isSetMessage = false; + } + + string clazz = *it++; + if (it == end) + return RESULT_ERR_EOF; + + string name = *it++; + if (it == end) + return RESULT_ERR_EOF; + if (name.length() == 0) + return RESULT_ERR_INVALID_ARG; // empty name + + string comment = *it++; + if (it == end) + return RESULT_ERR_EOF; + + str = (*it++).c_str(); + if (it == end) + return RESULT_ERR_EOF; + unsigned char srcAddress; + if (*str == 0 || isActiveMessage == true) + srcAddress = SYN; // no specific source defined, or ignore for active message + else { + srcAddress = parseInt(str, 16, 0, 0xff, result); + if (result != RESULT_OK) + return result; + if (isMaster(srcAddress) == false) + return RESULT_ERR_INVALID_ARG; + } + + str = (*it++).c_str(); + if (it == end) + return RESULT_ERR_EOF; + + unsigned char dstAddress = parseInt(str, 16, 0, 0xff, result); + if (result != RESULT_OK) + return result; + if (isValidAddress(dstAddress) == false) + return RESULT_ERR_INVALID_ARG; + + istringstream input(*it++); // message id (PBSB + optional master data) + vector id; + string token; + if (it == end) + return RESULT_ERR_EOF; + while (input.eof() == false) { + while (input.peek() == ' ') + input.get(); + if (input.eof() == true) // no more digits + break; + token.clear(); + token.push_back(input.get()); + if (input.eof() == true) + return RESULT_ERR_INVALID_ARG; // too short hex + token.push_back(input.get()); + + unsigned char value = parseInt(token.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) + return result; // invalid hex value + id.push_back(value); + } + if (id.size() < 2 || id.size() > 6) + return RESULT_ERR_INVALID_ARG; // missing/too short/too long ID + + DataField* data = NULL; + result = DataField::create(it, end, templates, data, isSetMessage, dstAddress); + if (result != RESULT_OK) + return result; + + returnValue = new Message(clazz, name, isSetMessage, isActiveMessage, comment, srcAddress, dstAddress, id, data, pollPriority); + return RESULT_OK; +} + +result_t Message::prepare(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator) +{ + if (m_isActiveMessage == true) { + masterData.clear(); + masterData.push_back(srcAddress, false); + masterData.push_back(m_dstAddress, false); + masterData.push_back(m_id[0], false); + masterData.push_back(m_id[1], false); + unsigned char addData = m_data->getLength(pt_masterData); + masterData.push_back(m_id.size() - 2 + addData, false); + for (size_t i=2; iwrite(input, masterData, m_id.size() - 2, slaveData, 0, separator); + if (result != RESULT_OK) + return result; + masterData.push_back(masterData.getCRC(), false, false); + } + return RESULT_OK; +} + +result_t Message::handle(SymbolString& masterData, SymbolString& slaveData, + ostringstream& output, char separator, bool answer) +{ + if (m_isActiveMessage == true) { + result_t result = m_data->read(masterData, m_id.size() - 2, slaveData, 0, output, false, separator); + if (result != RESULT_OK) + return result; + } + else if (answer == true) { + istringstream input; // TODO create input from database of internal variables + result_t result = m_data->write(input, masterData, m_id.size() - 2, slaveData, 0, separator); + if (result != RESULT_OK) + return result; + } + return RESULT_OK; +} diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h new file mode 100644 index 00000000..4856f9bb --- /dev/null +++ b/src/lib/ebus/message.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) John Baier 2014 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef LIBEBUS_MESSAGE_H_ +#define LIBEBUS_MESSAGE_H_ + +#include "data.h" +#include "result.h" +#include "symbol.h" +#include +#include + +using namespace std; + +/** + * @brief Base class for all kinds of bus messages. + */ +class Message +{ +public: + + /** + * @brief Constructs a new instance. + * @param class the optional device class. + * @param name the message name (unique within the same class and type). + * @param isSetMessage whether this is a set message. + * @param isActiveMessage true if message can be initiated by the daemon + * itself any any other participant, false if message can only be initiated + * by a participant other than the daemon. + * @param comment the comment. + * @param srcAddress the source address (optional if passive), or @a SYN for any. + * @param dstAddress the destination address. + * @param id the primary, secondary, and optional further ID bytes. + * @param data the @a DataField for encoding/decoding the message. + * @param pollPriority the priority for polling, or 0 for no polling at all. + */ + Message(const string clazz, const string name, const bool isSetMessage, + const bool isActiveMessage, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, DataField* data, + const unsigned int pollPriority) + : m_class(clazz), m_name(name), m_isSetMessage(isSetMessage), + m_isActiveMessage(isActiveMessage), m_comment(comment), + m_srcAddress(srcAddress), m_dstAddress(dstAddress), + m_id(id), m_data(data), m_pollPriority(pollPriority) {} + /** + * @brief Destructor. + */ + virtual ~Message() { delete m_data; } + /** + * @brief Factory method for creating a new instance. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + * @param templates a map of @a DataField templates to be referenced by name. + * @param returnValue the variable in which to store the created instance. + * @return @a RESULT_OK on success, or an error code. + * Note: the caller needs to free the created instance. + */ + static result_t create(vector::iterator& it, const vector::iterator end, + const map templates, Message*& returnValue); + /** + * @brief Get the optional device class. + * @return the optional device class. + */ + string getClass() const { return m_class; } + /** + * @brief Get the message name (unique within the same class and type). + * @return the message name (unique within the same class and type). + */ + string getName() const { return m_name; } + /** + * @brief Get whether this is a set message. + * @return whether this is a set message. + */ + bool isSetMessage() const { return m_isSetMessage; } + /** + * @brief Get whether message can be initiated by the daemon itself and any other + * participant. + * @return true if message can be initiated by the daemon itself and any other + * participant, false if message can only be initiated by a participant + * other than the daemon. + */ + bool isActiveMessage() const { return m_isActiveMessage; } + /** + * @brief Get the comment. + * @return the comment. + */ + string getComment() const { return m_comment; } + /** + * @brief Get the source address. + * @return the source address, or @a SYN for any. + */ + unsigned char getSrcAddress() const { return m_srcAddress; } + /** + * @brief Get the destination address. + * @return the destination address. + */ + unsigned char getDstAddress() const { return m_dstAddress; } + /** + * @brief Get the command ID bytes. + * @return the primary, secondary, and optionally further command ID bytes. + */ + vector getId() const { return m_id; } + /** + * @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. + * @param output the @a ostringstream to append the formatted value to. + * @param verbose whether to prepend the name, append the unit (if present), and append + * the comment in square brackets (if present). + * @param separator the separator character between multiple fields. + * @return @a RESULT_OK on success, or an error code. + */ + //result_t read(SymbolString& masterData, SymbolString& slaveData, ostringstream& output, + // bool verbose=false, char separator=';') = 0; + /** + * @brief Writes the value to the master or slave @a SymbolString. + * @param input the @a istringstream to parse the formatted value from. + * @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 separator the separator character between multiple fields. + * @return @a RESULT_OK on success, or an error code. + */ + result_t prepare(const unsigned char srcAddress, SymbolString& masterData, + istringstream& input, char separator=';'); + result_t handle(SymbolString& masterData, SymbolString& slaveData, + ostringstream& output, char separator=';', bool answer=false); + + +private: + + /** the optional device class. */ + const string m_class; + /** the message name (unique within the same class and type). */ + const string m_name; + /** whether this is a set message. */ + const bool m_isSetMessage; + /** true if message can be initiated by the daemon itself and any other + * participant, false if message can only be initiated by a participant + * other than the daemon. */ + const bool m_isActiveMessage; + /** the comment. */ + const string m_comment; + /** the source address (optional if passive), or @a SYN for any. */ + const unsigned char m_srcAddress; + /** the destination address. */ + const unsigned char m_dstAddress; + /** the primary, secondary, and optionally further command ID bytes. */ + const vector m_id; + /** the @a DataField for encoding/decoding the message. */ + DataField* m_data; + /** the priority for polling, or 0 for no polling at all. */ + const unsigned char m_pollPriority; + +}; + +#endif // LIBEBUS_MESSAGE_H_ diff --git a/src/lib/ebus/result.cpp b/src/lib/ebus/result.cpp index eeb2787e..ac44d46d 100644 --- a/src/lib/ebus/result.cpp +++ b/src/lib/ebus/result.cpp @@ -18,8 +18,12 @@ */ #include "result.h" +#include + +using namespace std; const char* getResultCode(result_t resultCode) { +cout << "DEBUG error code: " << static_cast(resultCode) << endl; switch (resultCode) { case RESULT_ERR_SEND: return "ERR_SEND: send error"; case RESULT_ERR_EXTRA_DATA: return "ERR_EXTRA_DATA: received bytes > sent bytes"; diff --git a/src/lib/ebus/symbol.cpp b/src/lib/ebus/symbol.cpp index 9a979958..730003f6 100644 --- a/src/lib/ebus/symbol.cpp +++ b/src/lib/ebus/symbol.cpp @@ -22,6 +22,8 @@ #include #include +using namespace std; + /** * @brief CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1. */ @@ -46,7 +48,7 @@ static const unsigned char CRC_LOOKUP_TABLE[] = }; -SymbolString::SymbolString(const std::string str) +SymbolString::SymbolString(const string str) : m_unescapeState(0), m_crc(0) { // parse + escape @@ -58,7 +60,7 @@ SymbolString::SymbolString(const std::string str) push_back(m_crc, false, false); } -SymbolString::SymbolString(const std::string str, bool isEscaped) +SymbolString::SymbolString(const string str, bool isEscaped) : m_unescapeState(1), m_crc(0) { // parse + optionally unescape @@ -68,9 +70,9 @@ SymbolString::SymbolString(const std::string str, bool isEscaped) } } -const std::string SymbolString::getDataStr(const bool unescape) +const string SymbolString::getDataStr(const bool unescape) { - std::stringstream sstr; + stringstream sstr; bool previousEscape = false; for (size_t i = 0; i < m_data.size(); i++) { @@ -89,8 +91,8 @@ const std::string SymbolString::getDataStr(const bool unescape) previousEscape = true; // escape sequence not yet finished } else { - sstr << std::nouppercase << std::setw(2) << std::hex - << std::setfill('0') << static_cast(value); + sstr << nouppercase << setw(2) << hex + << setfill('0') << static_cast(value); } } diff --git a/src/lib/ebus/symbol.h b/src/lib/ebus/symbol.h index db48095f..e8b3b136 100644 --- a/src/lib/ebus/symbol.h +++ b/src/lib/ebus/symbol.h @@ -25,6 +25,8 @@ #include #include +using namespace std; + static const unsigned char ESC = 0xA9; // escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA static const unsigned char SYN = 0xAA; // synchronization symbol static const unsigned char ACK = 0x00; // positive acknowledge @@ -48,19 +50,19 @@ public: * @brief Creates a new escaped instance from an unescaped hex string and adds the calculated CRC. * @param str the unescaped hex string. */ - SymbolString(const std::string str); + SymbolString(const string str); /** * @brief Creates a new unescaped instance from a hex string. * @param isEscaped whether the hex string is escaped and shall be unescaped. * @param str the hex string. */ - SymbolString(const std::string str, const bool isEscaped); + SymbolString(const string str, const bool isEscaped); /** * @brief Returns the symbols as hex string. * @param unescape whether to unescape an escaped instance. * @return the symbols as hex string. */ - const std::string getDataStr(const bool unescape=true); + const string getDataStr(const bool unescape=true); /** * @brief Returns a reference to the symbol at the specified index. * @param index the index of the symbol to return. @@ -114,7 +116,7 @@ private: /** * @brief the string of bus symbols. */ - std::vector m_data; + vector m_data; /** * @brief 0 if the symbols in @a m_data are escaped, * 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol, diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index 62058b68..0591bf6b 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -21,43 +21,49 @@ #include #include -void verify(bool expectFailMatch, std::string type, std::string input, - bool match, std::string expectStr, std::string gotStr) +using namespace std; + +void verify(bool expectFailMatch, string type, string input, + bool match, string expectStr, string gotStr) { if (expectFailMatch == true) { if (match == true) - std::cout << " failed " << type << " match >" << input - << "< error: unexpectedly succeeded" << std::endl; + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; else - std::cout << " failed " << type << " match >" << input << "< OK" - << std::endl; + cout << " failed " << type << " match >" << input << "< OK" << endl; } else if (match == true) - std::cout << " " << type << " >" << input << "< OK" << std::endl; + cout << " " << type << " match >" << input << "< OK" << endl; else - std::cout << " " << type << " >" << input << "< error: got >" << gotStr - << "<, expected >" << expectStr << "<" << std::endl; + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; } int main() { - std::string checks[][5] = { - //name;[pos];type[;[divisor|values][;[unit][;[comment]]]], decoded value, master, slave, flags - {"x;1-10;str", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x;1-10;str", "Hallo, Du ", "10fe07000a48616c6c6f2c20447520", "00", ""}, - {"x;1-10;str", " ", "10fe07000a20202020202020202020", "00", ""}, - {"x;1-11;str", "", "10fe07000a20202020202020202020", "00", "rW"}, + string checks[][5] = { + //name;[len];type[;[divisor|values][;[unit][;[comment]]]], decoded value, master, slave, flags + {"x;;ign:10", "", "10fe07000a00000000000000000000", "00", ""}, + {"x;;str:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x;;str:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x;;str:10", "Hallo, Du ", "10fe07000a48616c6c6f2c20447520", "00", ""}, + {"x;;str:10", " ", "10fe07000a20202020202020202020", "00", ""}, + {"x;;str:11", "", "10fe07000a20202020202020202020", "00", "rW"}, {"x;;hex", "20", "10fe07000120", "00", ""}, - {"x;1-10;hex", "48 61 6c 6c 6f 2c 20 44 75 21", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x;1-11;hex", "", "10fe07000a48616c6c6f2c20447521", "00", "rW"}, - {"x;;bda", "26.10.2014","10fe07000426100014", "00", ""}, - {"x;;bda", "01.01.2000","10fe07000401010000", "00", ""}, - {"x;;bda", "31.12.2099","10fe07000431120099", "00", ""}, + {"x;;hex:10", "48 61 6c 6c 6f 2c 20 44 75 21", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x;;hex:11", "", "10fe07000a48616c6c6f2c20447521", "00", "rW"}, + {"x;;bda", "26.10.2014","10fe07000426100614", "00", ""}, // Sunday + {"x;;hda", "26.10.2014","10fe07000426100714", "00", ""}, // Sunday + {"x;;bda", "01.01.2000","10fe07000401010500", "00", ""}, // Saturday + {"x;;hda", "01.01.2000","10fe07000401010600", "00", ""}, // Saturday + {"x;;bda", "31.12.2099","10fe07000431120399", "00", ""}, // Thursday + {"x;;hda", "31.12.2099","10fe07000431120499", "00", ""}, // Thursday {"x;;bda", "", "10fe07000432100014", "00", "rw"}, - {"x;1-3;bda","26.10.2014","10fe070003261014", "00", ""}, - {"x;1-3;bda","01.01.2000","10fe070003010100", "00", ""}, - {"x;1-3;bda","31.12.2099","10fe070003311299", "00", ""}, - {"x;1-3;bda","", "10fe070003321299", "00", "rw"}, + {"x;;bda:3", "26.10.2014","10fe070003261014", "00", ""}, + {"x;;bda:3", "01.01.2000","10fe070003010100", "00", ""}, + {"x;;bda:3", "31.12.2099","10fe070003311299", "00", ""}, + {"x;;bda:3", "", "10fe070003321299", "00", "rw"}, {"x;;bti", "21:04:58", "10fe070003580421", "00", ""}, {"x;;bti", "00:00:00", "10fe070003000000", "00", ""}, {"x;;bti", "23:59:59", "10fe070003595923", "00", ""}, @@ -84,11 +90,11 @@ int main() {"x;;bcd", "99", "10feffff0199", "00", ""}, {"x;;bcd", "-", "10feffff01ff", "00", ""}, {"x;;bcd", "", "10feffff019a", "00", "rw"}, - {"x;16;uch", "15", "10feffff11000102030405060708090a0b0c0d0e0f10", "00", "W"}, - {"x;17;uch", "", "10feffff00", "00", "c"}, - {"x;s3;uch", "2", "1025ffff0310111213", "0300010203", "W"}, - {"x;s3;uch", "2", "1025ffff00", "00000002", ""}, - {"x;s3;uch;;;;y;m2;uch", "2;3","1025ffff020003", "00000002", ""}, + {"x;;str:16", "0123456789ABCDEF", "10feffff1130313233343536373839414243444546", "00", ""}, + {"x;;uch:17", "", "10feffff00", "00", "c"}, + {"x;s;uch", "0", "1025ffff0310111213", "0300010203", "W"}, + {"x;s;uch", "0", "1025ffff00", "0100", ""}, + {"x;s;uch;;;;y;m;uch", "2;3","1025ffff0103", "0102", ""}, {"x;;uch", "38", "10feffff0126", "00", ""}, {"x;;uch", "0", "10feffff0100", "00", ""}, {"x;;uch", "254", "10feffff01fe", "00", ""}, @@ -148,101 +154,86 @@ int main() {"x;;bi3", "-", "10feffff0100", "00", ""}, {"x;;bi3;0=off,1=on","on", "10feffff0108", "00", ""}, {"x;;bi3;0=off,1=on","off","10feffff0100", "00", ""}, - {"x;;b34", "1", "10feffff0108", "00", ""}, - {"x;;b34", "-", "10feffff0100", "00", ""}, - {"x;;b34", "3", "10feffff0118", "00", ""}, - {"x;;b34;1=on","on", "10feffff0108", "00", ""}, - {"x;;b34;1=on","-", "10feffff0100", "00", ""}, - {"x;;b34;0=off,1=on,2=auto,3=eco","auto", "10feffff0110", "00", ""}, - {"x;;b34;0=off,1=on","on", "10feffff0108", "00", ""}, - {"x;;b34;0=off,1=on","off","10feffff0100", "00", ""}, + {"x;;bi3:2", "1", "10feffff0108", "00", ""}, + {"x;;bi3:2", "1", "10feffff01ef", "00", "W"}, + {"x;;bi3:2", "-", "10feffff0100", "00", ""}, + {"x;;bi3:2", "3", "10feffff0118", "00", ""}, + {"x;;bi3:2;1=on","on", "10feffff0108", "00", ""}, + {"x;;bi3:2;1=on","-", "10feffff0100", "00", ""}, + {"x;;bi3:2;0=off,1=on,2=auto,3=eco","auto", "10feffff0110", "00", ""}, + {"x;;bi3:2;0=off,1=on","on", "10feffff0108", "00", ""}, + {"x;;bi3:2;0=off,1=on","off","10feffff0100", "00", ""}, {"x;;uch;1=test,2=high,3=off,4=on","on","10feffff0104", "00", ""}, - {"x;s3;uch","3","1050ffff00", "03000003", ""}, - {"x;s3;uch","3","1050ffff00", "020000", "rW"}, + {"x;s;uch","3","1050ffff00", "0103", ""}, {"x;;d2b;;°C;Aussentemperatur","x=18.004 °C [Aussentemperatur]","10fe0700090112", "00", "v"}, - {"x;;bti;;;;y;;bda;;;;z;6;bdy", "21:04:58;26.10.2014;Sun","10fe07000758042126100614", "00", ""}, // combination + {"x;;bti;;;;y;;bda;;;;z;;bdy", "21:04:58;26.10.2014;Sun","10fe0700085804212610061406", "00", ""}, // combination {"x;;bi3;;;;y;;bi5", "1;-", "10feffff0108", "00", ""}, // bit combination {"x;;bi3;;;;y;;bi5", "1;1", "10feffff0128", "00", ""}, // bit combination {"x;;bi3;;;;y;;bi5", "-;1", "10feffff0120", "00", ""}, // bit combination {"x;;bi3;;;;y;;bi5", "-;-", "10feffff0100", "00", ""}, // bit combination - {"x;;bi3;;;;y;;bi7;;;;t;;uch", "-;-;9","10feffff020009", "00", ""}, // bit combination, auto pos incr - {"x;;bi3;;;;y;;bi5;;;;t;;uch", "-;-;9","10feffff020009", "00", "RW"}, // bit combination + {"x;;bi3;;;;y;;bi7;;;;t;;uch", "-;-;9","10feffff020009", "00", ""}, // bit combination + {"x;;bi6:2;;;;y;;bi0:2;;;;t;;uch", "2;1;9","10feffff03800109", "00", ""}, // bit combination {"temp;;d2b;;°C;Aussentemperatur","","", "", "t"}, // template with relative pos {"x;;temp","18.004","10fe0700020112", "00", ""}, // reference to template - {"tempoff;2;d2b;;°C;Aussentemperatur","","", "", "t"},// template with offset pos - {"x;;tempoff","18.004","10fe070002ff0112", "00", "W"}, // reference to template {"relrel;;d2b;;;;y;;d1c","","", "", "t"}, // template struct with relative pos - {"x;2;relrel","18.004;9.5","10fe070004ff011213", "00", "W"}, // reference to template struct - {"reloff;;d2b;;;;y;1;d1c","","", "", "t"}, // template struct with relative+offset pos - {"x;2;reloff","18.004;0.5","10fe070003130112", "00", "W"}, // reference to template struct - {"offrel;2;d2b;;;;y;;d1c","","", "", "t"}, // template struct with offset+relative pos - {"x;2;offrel","18.004;9.5","10fe070005fffe011213", "00", "W"}, // reference to template struct - {"offoff;2;d2b;;;;y;1;d1c","","", "", "t"}, // template struct with offset pos - {"x;2;offoff","18.004;9.5","10fe070004ff130112", "00", "W"}, // reference to template struct + {"x;;relrel","18.004;9.5","10fe070003011213", "00", ""}, // reference to template struct {"trelrel;;temp,temp","","", "", "t"}, // template struct with relative pos and ref to templates {"x;;trelrel","18.004;19.008","10fe07000401120213", "00", ""}, // reference to template struct - {"x;2;trelrel","18.004;19.008","10fe070005ff01120213", "00", "W"}, // reference to template struct - {"treloff;;temp,tempoff","","", "", "t"}, // template struct with relative+offset pos - {"x;2;treloff","18.004;19.008","10fe070006ff0112fe0213", "00", "W"}, // reference to template struct - {"toffrel;1;tempoff,temp","","", "", "t"}, // template struct with offset+relative pos - {"x;2;toffrel","18.004;19.008","10fe070003fffe01120213", "00", "W"}, // reference to template struct - {"toffoff;1;tempoff,tempoff","","", "", "t"}, // template struct with offset pos - {"x;2;toffoff","18.004;19.008","10fe070003fffe0112fd0213", "00", "W"}, // reference to template struct + {"x;;temp;;;;y;;d1c","18.004;9.5","10fe070003011213", "00", ""}, // reference to template, normal def }; - std::map templates; + map templates; DataField* fields = NULL; for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { - std::string check[5] = checks[i]; - std::istringstream isstr(check[0]); - std::string expectStr = check[1]; + string check[5] = checks[i]; + istringstream isstr(check[0]); + string expectStr = check[1]; SymbolString mstr = SymbolString(check[2], false); SymbolString sstr = SymbolString(check[3], false); - std::string flags = check[4]; - bool isSet = flags.find('s') != std::string::npos; - bool failedCreate = flags.find('c') != std::string::npos; - bool failedRead = flags.find('r') != std::string::npos; - bool failedReadMatch = flags.find('R') != std::string::npos; - bool failedWrite = flags.find('w') != std::string::npos; - bool failedWriteMatch = flags.find('W') != std::string::npos; - bool verbose = flags.find('v') != std::string::npos; - bool isTemplate = flags.find('t') != std::string::npos; - std::string item; - std::vector entries; + string flags = check[4]; + bool isSet = flags.find('s') != string::npos; + bool failedCreate = flags.find('c') != string::npos; + bool failedRead = flags.find('r') != string::npos; + bool failedReadMatch = flags.find('R') != string::npos; + bool failedWrite = flags.find('w') != string::npos; + bool failedWriteMatch = flags.find('W') != string::npos; + bool verbose = flags.find('v') != string::npos; + bool isTemplate = flags.find('t') != string::npos; + string item; + vector entries; - while (std::getline(isstr, item, ';') != 0) + while (getline(isstr, item, ';') != 0) entries.push_back(item); if (fields != NULL) { delete fields; fields = NULL; } - std::vector::iterator it = entries.begin(); + vector::iterator it = entries.begin(); result_t result = DataField::create(it, entries.end(), templates, fields, isSet, isTemplate ? SYN : mstr[1]); - if (failedCreate == true) { if (result == RESULT_OK) - std::cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << std::endl; + cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; else - std::cout << "\"" << check[0] << "\": failed create OK" << std::endl; + cout << "\"" << check[0] << "\": failed create OK" << endl; continue; - } else if (result != RESULT_OK) { - std::cout << "\"" << check[0] << "\": create error: " - << getResultCode(result) << std::endl; + } + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl; continue; } if (fields == NULL) { - std::cout << "\"" << check[0] << "\": create error: empty" << std::endl; + cout << "\"" << check[0] << "\": create error: NULL" << endl; continue; } if (it != entries.end()) { - std::cout << "\"" << check[0] << "\": create error: non-empty" << std::endl; + cout << "\"" << check[0] << "\": create error: trailing input" << endl; continue; } - std::cout << "\"" << check[0] << "\": create OK" << std::endl; + cout << "\"" << check[0] << "\": create OK" << endl; if (isTemplate) { // store new template - std::string name = fields->getName(); - std::map::iterator current = templates.find(name); + string name = fields->getName(); + map::iterator current = templates.find(name); if (current == templates.end()) { templates[name] = fields; } else { @@ -253,20 +244,20 @@ int main() continue; } - std::ostringstream output; + ostringstream output; SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); - result = fields->read(mstr, sstr, output, verbose); + result = fields->read(mstr, 0, sstr, 0, output, verbose); if (failedRead == true) if (result == RESULT_OK) - std::cout << " failed read " << fields->getName() << " >" - << check[2] << "< error: unexpectedly succeeded" << std::endl; + cout << " failed read " << fields->getName() << " >" + << check[2] << "< error: unexpectedly succeeded" << endl; else - std::cout << " failed read " << fields->getName() << " >" - << check[2] << "< OK" << std::endl; + cout << " failed read " << fields->getName() << " >" + << check[2] << "< OK" << endl; else if (result != RESULT_OK) { - std::cout << " read " << fields->getName() << " >" << check[2] << "< error: " - << getResultCode(result) << std::endl; + cout << " read " << fields->getName() << " >" << check[2] + << "< error: " << getResultCode(result) << endl; } else { bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; @@ -274,19 +265,19 @@ int main() } if (verbose == false) { - std::istringstream input(expectStr); - result = fields->write(input, writeMstr, writeSstr); + istringstream input(expectStr); + result = fields->write(input, writeMstr, 0, writeSstr, 0); if (failedWrite == true) { if (result == RESULT_OK) - std::cout << " failed write " << fields->getName() << " >" - << expectStr << "< error: unexpectedly succeeded" << std::endl; + cout << " failed write " << fields->getName() << " >" + << expectStr << "< error: unexpectedly succeeded" << endl; else - std::cout << " failed write " << fields->getName() << " >" - << expectStr << "< OK" << std::endl; + cout << " failed write " << fields->getName() << " >" + << expectStr << "< OK" << endl; } else if (result != RESULT_OK) { - std::cout << " write " << fields->getName() << " >" - << expectStr << "< error: " << getResultCode(result) << std::endl; + cout << " write " << fields->getName() << " >" << expectStr + << "< error: " << getResultCode(result) << endl; } else { bool match = mstr == writeMstr && sstr == writeSstr; @@ -297,7 +288,7 @@ int main() fields = NULL; } - for (std::map::iterator it = templates.begin(); it != templates.end(); it++) + for (map::iterator it = templates.begin(); it != templates.end(); it++) delete it->second; return 0; diff --git a/src/lib/ebus/test/test_message.cpp b/src/lib/ebus/test/test_message.cpp new file mode 100644 index 00000000..58000e5b --- /dev/null +++ b/src/lib/ebus/test/test_message.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) John Baier 2014 + * + * This file is part of libebus. + * + * libebus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libebus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libebus. If not, see http://www.gnu.org/licenses/. + */ + +#include "message.h" +#include +#include + +using namespace std; + +void verify(bool expectFailMatch, string type, string input, + bool match, string expectStr, string gotStr) +{ + if (expectFailMatch == true) { + if (match == true) + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; + else + cout << " failed " << type << " match >" << input << "< OK" << endl; + } + else if (match == true) + cout << " " << type << " match >" << input << "< OK" << endl; + else + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; +} + +void printErrorPos(vector::iterator it, const vector::iterator end, vector::iterator pos) +{ + cout << "Errroneous item is here:" << endl; + bool first = true; + int cnt = 0; + if (pos > it) + pos--; + while (it != end) { + if (first == true) + first = false; + else { + cout << ';'; + if (it <= pos) { + cnt++; + } + } + if (it < pos) { + cnt += (*it).length(); + } + cout << (*it++); + } + cout << endl; + cout << setw(cnt) << " " << setw(0) << "^" << endl; +} + +int main() +{ + // message= [type];class;name;[comment];[QQ];ZZ;PBSB;fields... + // field= name;[pos];type[;[divisor|values][;[unit][;[comment]]]] + string checks[][5] = { + // "message", "flags" + {";;first;;;fe;0700;x;;bda", "26.10.2014", "fffe0700042610061451", "00", ""}, + {"w;;first;;;15;b5090400;date;;bda", "26.10.2014", "ff15b5090604002610061445", "00", ""}, + }; + map templates; + Message* message = NULL; + for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { + string check[5] = checks[i]; + istringstream isstr(check[0]); + string inputStr = check[1]; + SymbolString mstr = SymbolString(check[2], false); + SymbolString sstr = SymbolString(check[3], false); + string flags = check[4]; + bool failedCreate = flags.find('c') != string::npos; + bool failedPrepare = flags.find('p') != string::npos; + bool failedPrepareMatch = flags.find('P') != string::npos; + string item; + vector entries; + + while (getline(isstr, item, ';') != 0) + entries.push_back(item); + + if (message != NULL) { + delete message; + message = NULL; + } + vector::iterator it = entries.begin(); + result_t result = Message::create(it, entries.end(), templates, message); + + if (failedCreate == true) { + if (result == RESULT_OK) + cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; + else + cout << "\"" << check[0] << "\": failed create OK" << endl; + continue; + } + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": create error: " + << getResultCode(result) << endl; + printErrorPos(entries.begin(), entries.end(), it); + continue; + } + if (message == NULL) { + cout << "\"" << check[0] << "\": create error: NULL" << endl; + continue; + } + if (it != entries.end()) { + cout << "\"" << check[0] << "\": create error: trailing input" << endl; + continue; + } + cout << "\"" << check[0] << "\": create OK" << endl; + + istringstream input(inputStr); + SymbolString writeMstr = SymbolString(); + result = message->prepare(0xff, writeMstr, input); + if (failedPrepare == true) { + if (result == RESULT_OK) + cout << "\"" << check[0] << "\": failed prepare error: unexpectedly succeeded" << endl; + else + cout << "\"" << check[0] << "\": failed prepare OK" << endl; + continue; + } + + if (result != RESULT_OK) { + cout << " prepare >" << inputStr << "< error: " + << getResultCode(result) << endl; + continue; + } + cout << " prepare >" << inputStr << "< OK" << endl; + + bool match = writeMstr==mstr; + verify(failedPrepareMatch, "prepare", inputStr, match, mstr.getDataStr(), writeMstr.getDataStr()); + + delete message; + message = NULL; + } + + for (map::iterator it = templates.begin(); it != templates.end(); it++) + delete it->second; + + return 0; + +} diff --git a/src/lib/ebus/test/test_symbol.cpp b/src/lib/ebus/test/test_symbol.cpp index 0ca984a7..b91c3f6e 100644 --- a/src/lib/ebus/test/test_symbol.cpp +++ b/src/lib/ebus/test/test_symbol.cpp @@ -21,6 +21,8 @@ #include #include +using namespace std; + int main () { SymbolString sstr = SymbolString("10feb5050427a915aa"); @@ -30,29 +32,28 @@ int main () if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "ctor escaped successful." << std::endl; else - std::cout << "ctor escaped invalid: got " << gotStr - << ", expected " << expectStr << std::endl; + std::cout << "ctor escaped invalid: got " << gotStr << ", expected " + << expectStr << std::endl; unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77; if (gotCrc == expectCrc) std::cout << "CRC successful." << std::endl; else - std::cout << "CRC invalid: got 0x" - << std::nouppercase << std::setw(2) << std::hex - << std::setfill('0') << static_cast(gotCrc) - << ", expected 0x" - << std::nouppercase << std::setw(2) << std::hex - << std::setfill('0') << static_cast(expectCrc) - << std::endl; + std::cout << "CRC invalid: got 0x" << std::nouppercase << std::setw(2) + << std::hex << std::setfill('0') + << static_cast(gotCrc) << ", expected 0x" + << std::nouppercase << std::setw(2) << std::hex + << std::setfill('0') << static_cast(expectCrc) + << std::endl; gotStr = sstr.getDataStr(), expectStr = "10feb5050427a915aa77"; if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "unescape successful." << std::endl; else - std::cout << "unescape invalid: got " << gotStr - << ", expected " << expectStr << std::endl; + std::cout << "unescape invalid: got " << gotStr << ", expected " + << expectStr << std::endl; sstr = SymbolString("10feb5050427a90015a90177", true); @@ -61,8 +62,8 @@ int main () if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "ctor unescaped successful." << std::endl; else - std::cout << "ctor unescaped invalid: got " << gotStr - << ", expected " << expectStr << std::endl; + std::cout << "ctor unescaped invalid: got " << gotStr << ", expected " + << expectStr << std::endl; return 0;