diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 3490c9e4..6fe7f592 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -42,7 +42,7 @@ static const dataType_t dataTypes[] = { {"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, 0, 5, 5, 0, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) + {"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" {"BCD", 8, bt_num, BCD|LST, 0xff, 0, 0x99, 1, 0}, // unsigned decimal in BCD, 0 - 99 @@ -104,214 +104,264 @@ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" result_t DataField::create(std::vector::iterator& it, const std::vector::iterator end, - const std::map templates, - std::vector& fields, const bool isSetMessage, + const std::map< std::string, DataField*> templates, + DataField*& returnField, const bool isSetMessage, const unsigned char dstAddress) { - std::string unit, comment; - PartType partType; - unsigned int divisor = 0; - unsigned char offset, length, maxPos = 16, offsetCnt = 0; - const bool isTemplate = dstAddress == SYN; - std::string token; - if (it == end) - return RESULT_ERR_EOF; + std::vector fields; + std::string firstName, firstComment; + result_t result = RESULT_OK; + while (it != end && result == RESULT_OK) { + std::string unit, comment; + PartType partType; + unsigned int divisor = 0; + unsigned char offset, length, maxPos = 16, offsetCnt = 0; + const bool isTemplate = dstAddress == SYN; + std::string token; + if (it == end) + break; - // name;[pos];type[;[divisor|values][;[unit][;[comment]]]] - const std::string name = *it++; - if (it == end || name.empty() == true) - return RESULT_ERR_EOF; + // name;[pos];type[;[divisor|values][;[unit][;[comment]]]] + const std::string name = *it++; + if (it == end) + break; - const char* posStr = (*it++).c_str(); - if (it == end) - return RESULT_ERR_EOF; + const char* posStr = (*it++).c_str(); + if (it == end) + break; - 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++; - } - else if ((isTemplate == false && isSetMessage == false && posStr[0] != 0 && posStr[0] <= '9') - || posStr[0] == 's') { // slave data - partType = pt_slaveData; - if (posStr[0] == 's') - posStr++; - } - else if (isTemplate) { - partType = pt_template; - } - else - return RESULT_ERR_INVALID_ARG; - - bool hasPrev = fields.empty() == false; - if (hasPrev == true) { - DataField* previous = fields.back(); - offset = previous->m_offset + previous->m_length; - if ((previous->m_dataType.numBits % 8) != 0 - && previous->m_dataType.precisionOrFirstBit + (previous->m_dataType.numBits % 8) < 8) - offset--; // previous bits not yet fully consumed - } - else - offset = 0; - - if (posStr[0] == 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 - - const char* start = token.c_str(); - char* end = NULL; - unsigned int pos = strtoul(start, &end, 10) - 1; // 1-based - if (end != start + strlen(start)) - return RESULT_ERR_INVALID_ARG; // invalid pos definition - - if (pos > maxPos) // TODO check this in real offset as well - return RESULT_ERR_INVALID_ARG; // invalid pos definition - - 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 (fields.empty() == true) { + 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++; + } + else if ((isTemplate == false && isSetMessage == false && posStr[0] != 0 && posStr[0] <= '9') + || posStr[0] == 's') { // slave data + partType = pt_slaveData; + if (posStr[0] == 's') + posStr++; + } + else if (isTemplate) { + partType = pt_template; + } + else { + result = RESULT_ERR_INVALID_ARG; + break; } - } - const char* typeStr = (*it++).c_str(); - if (typeStr[0] == 0) - return RESULT_ERR_EOF; + 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 - std::map values; - if (it != end) { - std::string divisorStr = *it++; - if (divisorStr.empty() == false) { - if (divisorStr.find_first_not_of("0123456789") == std::string::npos) { - const char* start = divisorStr.c_str(); + const char* start = token.c_str(); char* end = NULL; - divisor = strtoul(start, &end, 10); - if (end != start + strlen(start)) - return RESULT_ERR_INVALID_ARG; + unsigned int pos = strtoul(start, &end, 10) - 1; // 1-based + if (end != start + strlen(start)) { + result = RESULT_ERR_INVALID_ARG; // invalid pos definition + break; + } + + if (pos > maxPos) { // TODO check this in real offset as well + result = RESULT_ERR_INVALID_ARG; // invalid pos definition + 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; + } } - else { - std::istringstream stream(divisorStr); - while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { - const char* start = token.c_str(); + if (result != RESULT_OK) + break; + } + + const char* typeStr = (*it++).c_str(); + if (typeStr[0] == 0) { + break; + } + + std::map values; + if (it != end) { + std::string divisorStr = *it++; + if (divisorStr.empty() == false) { + if (divisorStr.find_first_not_of("0123456789") == std::string::npos) { + const char* start = divisorStr.c_str(); char* end = NULL; - unsigned int id = strtoul(start, &end, 10); - if (end == NULL || end == start || *end != '=') - return RESULT_ERR_INVALID_ARG; - - values[id] = std::string(end + 1); - } - } - } - } - - if (it == end) - unit = ""; - else { - unit = *it++; - - if (unit.length() == 1 && unit[0] == '-') - unit.clear(); - } - - if (it == end) - comment = ""; - else { - comment = *it++; - if (comment.length() == 1 && comment[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::const_iterator ref = templates.find(token); - if (ref == templates.end()) { - if (found == false) - break; // fallback to direct definition - return RESULT_ERR_INVALID_ARG; // cannot mix reference and direct definition - } - found = true; - DataField* templ = ref->second; - if (length != 0 && length != templ->m_length) - return RESULT_ERR_INVALID_ARG; // different length not possible for derivation - unsigned char deriveOffset = offset == 0 ? templ->m_offset : offset; - if (templ->m_partType != pt_template && partType == pt_template) - return RESULT_ERR_INVALID_ARG; - DataField* field = templ->derive(name, partType, deriveOffset, unit, comment, divisor, values); - if (field == NULL) - return RESULT_ERR_INVALID_ARG; // non-supported type for derivation or invalid argument - fields.push_back(field); - } - if (found == true) - return RESULT_OK; - } - - for (size_t i = 0; 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) - return RESULT_ERR_INVALID_ARG; // invalid length - } - else if (useLength == 0) - useLength = numBytes; - else if (useLength != numBytes) - continue; // check for another one with same name but different length - - switch (dataType.type) - { - case bt_str: - case bt_hexstr: - case bt_dat: - case bt_tim: - fields.push_back(new StringDataField(name, partType, offset, useLength,dataType, unit, comment)); - return RESULT_OK; - case bt_num: - if (values.empty() == true && (dataType.flags & DAY) != 0) { - for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++) - values[dataType.minValueOrLength + i] = dayNames[i]; - } - if (values.empty() == true || (dataType.flags & LST) == 0) { - if (divisor == 0) { - divisor = dataType.divisor; + divisor = strtoul(start, &end, 10); + if (end != start + strlen(start)) { + result = RESULT_ERR_INVALID_ARG; + break; } - else - divisor *= dataType.divisor; - fields.push_back(new NumberDataField(name, partType, offset, useLength, dataType, unit, comment, divisor)); - return RESULT_OK; } - if (values.begin()->first < dataType.minValueOrLength - || values.rbegin()->first > dataType.maxValueOrLength) - return RESULT_ERR_INVALID_ARG; - fields.push_back(new ValueListDataField(name, partType, offset, useLength, dataType, unit, comment, values)); - return RESULT_OK; + else { + std::istringstream stream(divisorStr); + while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { + const char* start = token.c_str(); + char* end = NULL; + unsigned int id = strtoul(start, &end, 10); + if (end == NULL || end == start || *end != '=') { + result = RESULT_ERR_INVALID_ARG; + break; + } + + values[id] = std::string(end + 1); + } + if (result != RESULT_OK) + break; + } } } + + if (it == end) + unit = ""; + else { + unit = *it++; + + if (unit.length() == 1 && unit[0] == '-') + unit.clear(); + } + + if (it == end) + comment = ""; + else { + comment = *it++; + if (comment.length() == 1 && comment[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; + } + 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 || result != RESULT_OK) + break; + } + SingleDataField* add = NULL; + 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) { + result = RESULT_ERR_INVALID_ARG; // invalid length + break; + } + } + else if (useLength == 0) + useLength = numBytes; + else if (useLength != numBytes) + continue; // check for another one with same name but different length + + switch (dataType.type) + { + case bt_str: + case bt_hexstr: + case bt_dat: + case bt_tim: + add = new StringDataField(name, comment, unit, dataType, partType, offset, useLength); + break; + case bt_num: + if (values.empty() == true && (dataType.flags & DAY) != 0) { + for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++) + values[dataType.minValueOrLength + i] = dayNames[i]; + } + if (values.empty() == true || (dataType.flags & LST) == 0) { + if (divisor == 0) { + divisor = dataType.divisor; + } + else + divisor *= dataType.divisor; + + add = new NumberDataField(name, comment, unit, dataType, partType, offset, useLength, divisor); + break; + } + if (values.begin()->first < dataType.minValueOrLength + || values.rbegin()->first > dataType.maxValueOrLength) { + result = RESULT_ERR_INVALID_ARG; + break; + } + + add = new ValueListDataField(name, comment, unit, dataType, partType, offset, useLength, values); + break; + } + } + } + if (add != NULL) + fields.push_back(add); + else if (result == RESULT_OK) + result = RESULT_ERR_INVALID_ARG; // type not found } - return RESULT_ERR_INVALID_ARG; + if (fields.empty() == true || result != RESULT_OK) { + while (fields.empty() == false) { + delete fields.back(); + fields.pop_back(); + } + return result == RESULT_OK ? RESULT_ERR_INVALID_ARG :result; + } + + if (fields.size() == 1) + returnField = fields[0]; + else { + returnField = new DataFieldSet(firstName, firstComment, fields); + } + return RESULT_OK; } -result_t DataField::read(SymbolString& masterData, SymbolString& slaveData, - std::ostringstream& output, bool verbose) + +unsigned char SingleDataField::getNextOffset() +{ + 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; @@ -342,8 +392,8 @@ result_t DataField::read(SymbolString& masterData, SymbolString& slaveData, return RESULT_OK; } -result_t DataField::write(std::istringstream& input, SymbolString& masterData, - SymbolString& slaveData) +result_t SingleDataField::write(std::istringstream& input, SymbolString& masterData, + SymbolString& slaveData, char separator) { SymbolString& output = m_partType == pt_masterData ? masterData : slaveData; unsigned char baseOffset; @@ -361,17 +411,27 @@ result_t DataField::write(std::istringstream& input, SymbolString& masterData, return writeSymbols(input, baseOffset, output); } -DataField* StringDataField::derive(std::string name, PartType partType, - unsigned char offset, std::string unit, std::string comment, - unsigned int divisor, std::map values) + +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) { - if (unit.empty() == true) - unit = m_unit; + if (m_partType != pt_template && partType == pt_template) + 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 (name.empty() == true) + name = m_name; if (comment.empty() == true) comment = m_comment; + if (unit.empty() == true) + unit = m_unit; + offset += m_offset; - return new StringDataField(name, partType, offset, m_length, m_dataType, - unit, comment); + fields.push_back(new StringDataField(name, comment, unit, m_dataType, partType, offset, m_length)); + + return RESULT_OK; } result_t StringDataField::readSymbols(SymbolString& input, @@ -379,11 +439,11 @@ result_t StringDataField::readSymbols(SymbolString& input, { size_t start = m_offset, end = m_offset + m_length; int incr = 1; - unsigned char ch; + unsigned char ch, last = 0; - if (baseOffset + end > input.size()) + if (baseOffset + end > 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; @@ -424,7 +484,7 @@ result_t StringDataField::readSymbols(SymbolString& input, else ch = (ch % 6) * 10; // minutes } - if ((i == 0 && ch > 23) || (i > 0 && ch > 59)) + if ((i == 0 && ch > 24) || (i > 0 && (ch > 59 || ( last == 24 && ch > 0) ))) return RESULT_ERR_INVALID_ARG; // invalid time if (i > 0) output << ":"; @@ -436,6 +496,7 @@ result_t StringDataField::readSymbols(SymbolString& input, output << static_cast(ch); break; } + last = ch; } return RESULT_OK; @@ -448,7 +509,7 @@ result_t StringDataField::writeSymbols(std::istringstream& input, int incr = 1; const char* str; char* strEnd; - unsigned long int value = 0, hours = 0; + unsigned long int value = 0, last = 0; std::string token; if ((m_dataType.flags & REV) != 0) { // reverted binary representation (most significant byte first) @@ -505,17 +566,17 @@ result_t StringDataField::writeSymbols(std::istringstream& input, value = strtoul(str, &strEnd, 10); if (strEnd != str + strlen(str)) return RESULT_ERR_INVALID_ARG; // invalid time part - if ((i == 0 && value > 23) || (i > 0 && value > 59)) + if ((i == 0 && value > 24) || (i > 0 && (value > 59 || ( 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 - hours = value; + last = value; continue; } if ((value % 10) != 0) return RESULT_ERR_INVALID_ARG; // invalid truncated time minutes - value = hours * 6 + (value / 10); + value = last * 6 + (value / 10); if (value > 24 * 6) return RESULT_ERR_INVALID_ARG; // invalid time } @@ -533,7 +594,8 @@ result_t StringDataField::writeSymbols(std::istringstream& input, } if (value > 0xff) return RESULT_ERR_INVALID_ARG; // value out of range - output[baseOffset + offset] = (unsigned char) value; + output[baseOffset + offset] = (unsigned char)value; + last = value; } if (i < m_length) @@ -542,6 +604,7 @@ result_t StringDataField::writeSymbols(std::istringstream& input, return RESULT_OK; } + result_t NumericDataField::readRawValue(SymbolString& input, unsigned char baseOffset, unsigned int& value) { @@ -628,20 +691,35 @@ result_t NumericDataField::writeRawValue(unsigned int value, return RESULT_OK; } -DataField* NumberDataField::derive(std::string name, PartType partType, - unsigned char offset, std::string unit, std::string comment, - unsigned int divisor, std::map values) + +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) { - if (unit.empty() == true) - unit = m_unit; + if (m_partType != pt_template && partType == pt_template) + return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance + if (name.empty() == true) + name = m_name; if (comment.empty() == true) comment = m_comment; + if (unit.empty() == true) + unit = m_unit; + offset += m_offset; if (divisor == 0) divisor = m_divisor; else divisor *= m_dataType.divisor; + if (values.empty() == false) { + if (divisor != 1) + return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field - return new NumberDataField(name, partType, offset, m_length, m_dataType, unit, comment, divisor); + fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, offset, m_length, values)); + } + else + fields.push_back(new NumberDataField(name, comment, unit, m_dataType, partType, offset, m_length, divisor)); + + return RESULT_OK; } result_t NumberDataField::readSymbols(SymbolString& input, @@ -658,6 +736,7 @@ result_t NumberDataField::readSymbols(SymbolString& input, output << NULL_VALUE; return RESULT_OK; } + bool negative = (m_dataType.flags & SIG) != 0 && (value & (1 << (m_dataType.numBits - 1))) != 0; if (m_dataType.numBits == 32) { if (negative == false) { @@ -747,23 +826,35 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, return writeRawValue(value, baseOffset, output); } -DataField* ValueListDataField::derive(std::string name, PartType partType, - unsigned char offset, std::string unit, std::string comment, - unsigned int divisor, std::map values) + +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) { - if (unit.empty() == true) - unit = m_unit; + if (m_partType != pt_template && partType == pt_template) + return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance + if (name.empty() == true) + name = m_name; if (comment.empty() == true) 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 - if (values.empty() == true) { - values = m_values; - } - else if (values.begin()->first < m_dataType.minValueOrLength + if (values.empty() == false) { + if (values.begin()->first < m_dataType.minValueOrLength || values.rbegin()->first > m_dataType.maxValueOrLength) - return NULL; + return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field + } + else + values = m_values; - return new ValueListDataField(name, partType, offset, m_length, m_dataType, unit, comment, values); + fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, offset, m_length, values)); + + return RESULT_OK; } result_t ValueListDataField::readSymbols(SymbolString& input, @@ -804,4 +895,87 @@ result_t ValueListDataField::writeSymbols(std::istringstream& input, return RESULT_ERR_INVALID_ARG; // value assignment not found } + +DataFieldSet::~DataFieldSet() +{ + while (m_fields.empty() == false) { + delete m_fields.back(); + m_fields.pop_back(); + } +} + +unsigned char DataFieldSet::getNextOffset() +{ + return m_fields.back()->getNextOffset(); +} + +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) +{ + 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); + if (result != RESULT_OK) + return result; + } + + return RESULT_OK; +} + +result_t DataFieldSet::read(SymbolString& masterData, SymbolString& slaveData, std::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; + + result_t result = (*it)->read(masterData, slaveData, output, verbose); + + if (result != RESULT_OK) + return result; + } + + if (verbose) { + if (m_comment.length() > 0) + output << " [" << m_comment << "]"; + output << "}"; + } + + return RESULT_OK; +} + +result_t DataFieldSet::write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, + char separator) +{ + std::string token; + + 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) + return RESULT_ERR_INVALID_ARG; // incomplete + std::istringstream single(token); + result = (*it)->write(single, masterData, slaveData); + } + else + result = (*it)->write(input, masterData, slaveData); + + if (result != RESULT_OK) + return result; + } + + return RESULT_OK; +} + + } //namespace diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index a5c690bd..c5635022 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -68,34 +68,26 @@ typedef struct { } dataType_t; +class SingleDataField; + /** * @brief Base class for all kinds of data fields. */ class DataField { public: + /** * @brief Constructs a new instance. * @param name the field name. - * @param partType the message part in which the field is stored. - * @param offset the relatvie offset to the first symbol in the message part in which the field is stored. - * @param length the number of symbols in the message part in which the field is stored. - * @param dataType the data type definition. - * @param unit the value unit. * @param comment the field comment. */ - DataField(const std::string name, const PartType partType, - const unsigned char offset, const unsigned char length, - const dataType_t dataType, const std::string unit, - const std::string comment) - : m_name(name), m_partType(partType), m_offset(offset), - m_length(length), m_dataType(dataType), m_unit(unit), - m_comment(comment) {} + DataField(const std::string name, const std::string comment) + : m_name(name), m_comment(comment) {} /** * @brief Destructor. */ virtual ~DataField() {} - /** * @brief Factory method for creating new instances. * @param it the iterator to traverse for the definition parts. @@ -108,9 +100,103 @@ public: * Note: the caller needs to cleanup created instances. */ static result_t create(std::vector::iterator& it, const std::vector::iterator end, - const std::map templates, std::vector& fields, + const std::map< std::string, DataField*> templates, DataField*& fields, 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. + */ + virtual unsigned char getNextOffset() = 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). + */ + 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; + /** + * @brief Get the field name. + * @return the field name. + */ + const std::string getName() { return m_name; } + /** + * @brief Get the field comment. + * @return the field comment. + */ + const std::string getComment() { 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 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 RESULT_OK on success, or an error code. + */ + virtual result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, + bool verbose=false, char separator=';') = 0; + /** + * @brief Writes the value to the master or slave @a SymbolString. + * @param input the 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 RESULT_OK on success, or an error code. + */ + virtual result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, + char separator=';') = 0; +protected: + + /** the field name. */ + const std::string m_name; + /** the field comment. */ + const std::string m_comment; + +}; + + +/** + * @brief A single DataField. + */ +class SingleDataField : public DataField +{ +public: + + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + * @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) + : DataField(name, comment), + m_unit(unit), m_dataType(dataType), m_partType(partType), + m_offset(offset), m_length(length) {} + /** + * @brief Destructor. + */ + virtual ~SingleDataField() {} + /** + * @brief Get the value unit. + * @return the value unit. + */ + const std::string getUnit() { return m_unit; } + virtual unsigned char getNextOffset(); /** * @brief Reads the value from the master or slave @a SymbolString. * @param masterData the unescaped master data @a SymbolString for reading binary data. @@ -120,8 +206,8 @@ public: * the comment in square brackets (if present). * @return RESULT_OK on success, or an error code. */ - result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, - bool verbose=false); + virtual result_t read(SymbolString& masterData, SymbolString& slaveData, std::ostringstream& output, + bool verbose=false, char separator=';'); /** * @brief Writes the value to the master or slave @a SymbolString. * @param input the istringstream to parse the formatted value from. @@ -129,29 +215,11 @@ public: * @param slaveData the unescaped slave data @a SymbolString for writing binary data. * @return RESULT_OK on success, or an error code. */ - result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData); - - /** - * @brief Get the field name. - * @return the field name. - */ - const std::string getName() { return m_name; } - - /** - * @brief Get the value unit. - * @return the value unit. - */ - const std::string getUnit() { return m_unit; } - - /** - * @brief Get the field comment. - * @return the field comment. - */ - const std::string getComment() { return m_comment; } + virtual result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, + char separator=';'); protected: - virtual DataField* derive(std::string name, PartType partType, unsigned char offset, std::string unit, - std::string comment, unsigned int divisor, std::map values) = 0; + /** * @brief Internal method for reading the field from a @a SymbolString. * @param input the unescaped @a SymbolString to read the binary value from. @@ -167,52 +235,52 @@ protected: */ virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output) = 0; - /** the field name. */ - const std::string m_name; + /** the value unit. */ + const std::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; - /** the data type definition. */ - const dataType_t m_dataType; - /** the value unit. */ - const std::string m_unit; - /** the field comment. */ - const std::string m_comment; + }; /** * @brief Base class for all string based data fields. */ -class StringDataField : public DataField +class StringDataField : public SingleDataField { public: + /** * @brief Constructs a new instance. * @param name the field name. + * @param comment the field comment. + * @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 dataType the data type definition. - * @param unit the value unit. - * @param comment the field comment. */ - StringDataField(const std::string name, const PartType partType, - const unsigned char offset, const unsigned char length, - const dataType_t dataType, const std::string unit, - const std::string comment) - : DataField(name, partType, offset, length, dataType, unit, comment) {} + 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) {} /** * @brief Destructor. */ virtual ~StringDataField() {} + 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); protected: - virtual DataField* derive(std::string name, PartType partType, unsigned char offset, std::string unit, - std::string comment, unsigned int divisor, std::map values); + virtual result_t readSymbols(SymbolString& input, unsigned char baseOffset, std::ostringstream& output); virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output); @@ -222,32 +290,34 @@ protected: /** * @brief Base class for all numeric data fields. */ -class NumericDataField : public DataField +class NumericDataField : public SingleDataField { public: + /** * @brief Constructs a new instance. * @param name the field name. + * @param comment the field comment. + * @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 bitOffset the offset to the first bit in the binary value. - * @param dataType the data type definition. - * @param unit the value unit. - * @param comment the field comment. */ - NumericDataField(const std::string name, const PartType partType, + 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 dataType_t dataType, const std::string unit, - const std::string comment, const unsigned char bitOffset) - : DataField(name, partType, offset, length, dataType, unit, comment), - m_bitOffset(bitOffset) {} + const unsigned char bitOffset) + : SingleDataField(name, comment, unit, dataType, partType, offset, length), + m_bitOffset(bitOffset) {} /** * @brief Destructor. */ virtual ~NumericDataField() {} 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. @@ -268,38 +338,43 @@ protected: }; + /** * @brief Base class for all numeric data fields with a number representation. */ class NumberDataField : public NumericDataField { public: + /** * @brief Constructs a new instance. * @param name the field name. + * @param comment the field comment. + * @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 dataType the data type definition. - * @param unit the value unit. - * @param comment the field comment. * @param divisor the extra divisor to apply on the value, or 1 for none. */ - NumberDataField(const std::string name, const PartType partType, + 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, - const dataType_t dataType, const std::string unit, - const std::string comment, const unsigned int divisor) - : NumericDataField(name, partType, offset, length, dataType, unit, comment, + const unsigned int divisor) + : NumericDataField(name, comment, unit, dataType, partType, offset, length, (dataType.numBits%8) != 0 ? dataType.precisionOrFirstBit : 0), - m_divisor(divisor) {} + m_divisor(divisor) {} /** * @brief Destructor. */ virtual ~NumberDataField() {} protected: - virtual DataField* derive(std::string name, PartType partType, unsigned char offset, std::string unit, - std::string comment, unsigned int divisor, std::map values); + + 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, unsigned char baseOffset, std::ostringstream& output); virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output); @@ -308,38 +383,43 @@ protected: }; + /** * @brief A numeric data field with a list of value=text assignments and a string representation. */ class ValueListDataField : public NumericDataField { public: + /** * @brief Constructs a new instance. * @param name the field name. + * @param comment the field comment. + * @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 dataType the data type definition. - * @param unit the value unit. - * @param comment the field comment. * @param values the value=text assignments. */ - ValueListDataField(const std::string name, const PartType partType, + 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 dataType_t dataType, const std::string unit, - const std::string comment, const std::map values) - :NumericDataField(name, partType, offset, length, dataType, unit, comment, + const std::map values) + : NumericDataField(name, comment, unit, dataType, partType, offset, length, (dataType.numBits%8) != 0 ? dataType.precisionOrFirstBit : 0), - m_values(values) {} + m_values(values) {} /** * @brief Destructor. */ virtual ~ValueListDataField() {} protected: - virtual DataField* derive(std::string name, PartType partType, unsigned char offset, std::string unit, - std::string comment, unsigned int divisor, std::map values); + + 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, unsigned char baseOffset, std::ostringstream& output); virtual result_t writeSymbols(std::istringstream& input, unsigned char baseOffset, SymbolString& output); @@ -349,6 +429,78 @@ protected: }; +/** + * @brief A set of DataFields. + */ +class DataFieldSet : public DataField +{ +public: + + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + * @param fields the list of SingleDataFields part of this set. + */ + DataFieldSet(const std::string name, const std::string comment, + const std::vector fields) + : DataField(name, comment), + m_fields(fields) {} + /** + * @brief Destructor. + */ + virtual ~DataFieldSet(); + virtual unsigned char getNextOffset(); + 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); + /** + * @brief Returns the SingleDataField at the specified index. + * @param index the index of the SingleDataField to return. + * @return the SingleDataField at the specified index, or NULL. + */ + SingleDataField* operator[](const size_t index) { if (index >= m_fields.size()) return NULL; return m_fields[index]; } + /** + * @brief Returns the SingleDataField at the specified index. + * @param index the index of the SingleDataField to return. + * @return the SingleDataField at the specified index, or NULL. + */ + const SingleDataField* operator[](const size_t index) const { if (index >= m_fields.size()) return NULL; return m_fields[index]; } + /** + * @brief Returns the number of SingleDataFields in this set. + * @return the number of available SingleDataField. + */ + 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 ostringstream to append the formatted value to. + * @param vervose whether to prepend the name, append the unit (if present), and append + * the comment in square brackets (if present). + * @return RESULT_OK on success, or an error code. + */ + 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 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 RESULT_OK on success, or an error code. + */ + virtual result_t write(std::istringstream& input, SymbolString& masterData, SymbolString& slaveData, + char separator=';'); + +protected: + + /** the list of SingleDataFields part of this set. */ + std::vector m_fields; + +}; + + } //namespace #endif // LIBEBUS_DATA_H_ diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index d96a9562..2a3c0a96 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -67,13 +67,14 @@ int main() {"x;;htm", "21:04", "10fe0700021504", "00", ""}, {"x;;htm", "00:00", "10fe0700020000", "00", ""}, {"x;;htm", "23:59", "10fe070002173b", "00", ""}, + {"x;;htm", "24:00", "10fe0700021800", "00", ""}, {"x;;htm", "", "10fe070002183b", "00", "rw"}, - {"x;;htm", "24:00", "10fe070002173b", "00", "Rw"}, + {"x;;htm", "24:01", "10fe0700021801", "00", "rw"}, {"x;;ttm", "22:40", "10fe07000188", "00", ""}, {"x;;ttm", "00:00", "10fe07000100", "00", ""}, {"x;;ttm", "23:50", "10fe0700018f", "00", ""}, - {"x;;ttm", "24:00", "10fe07000190", "00", "rw"}, // TODO check range - {"x;;ttm", "", "10fe07000191", "00", "rw"}, // TODO check range + {"x;;ttm", "24:00", "10fe07000190", "00", ""}, + {"x;;ttm", "", "10fe07000191", "00", "rw"}, {"x;;bdy", "Mon", "10fe07000300", "00", ""}, {"x;;bdy", "Sun", "10fe07000306", "00", ""}, {"x;;bdy", "", "10fe07000308", "00", "rw"}, @@ -156,22 +157,37 @@ int main() {"x;s3;uch","3","1050ffff00", "03000003", ""}, {"x;s3;uch","3","1050ffff00", "020000", "rW"}, {"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", "c"}, - //TODO test bit combinations - {"temprel;;d2b;;°C;Aussentemperatur","","", "", "p"}, // predefined type with relative pos - {"tempabs;1;d2b;;°C;Aussentemperatur","","", "", "p"},// predefined type with absolute pos - {"strucrelrel;;d2b;;;;y;;d1c","","", "", "p"}, // predefined combined type with relative pos - {"strucrelabs;;d2b;;;;y;1;d1c","","", "", "p"}, // predefined combined type with relative pos - {"strucabsrel;1;d2b;;;;y;;d1c","","", "", "p"}, // predefined combined type with relative pos - {"strucabsabs;2;d2b;;;;y;1;d1c","","", "", "p"}, // predefined combined type with absolute pos - {"x;;temprel","18.004","10fe0700090112", "00", ""}, // reference predefined type - {"strucrelrel;;temprel,temprel","","", "", "p"}, // predefined combined type with relative pos - {"strucrelabs;;temprel,tempabs","","", "", "p"}, // predefined combined type with relative pos - {"strucabsrel;1;tempabs,temprel","","", "", "p"}, // predefined combined type with relative pos - {"strucabs;2;tempabs","","", "", "p"}, // predefined combined type with absolute pos + {"x;;bti;;;;y;;bda;;;;z;6;bdy", "21:04:58;26.10.2014;Sun","10fe07000758042126100614", "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 + {"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 + {"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 }; std::map templates; - std::vector fields; + 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]); @@ -185,116 +201,92 @@ int main() 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 combinedValue = flags.find('c') != std::string::npos; - bool isPredefine = flags.find('p') != std::string::npos; + bool isTemplate = flags.find('t') != std::string::npos; std::string item; std::vector entries; - while (fields.empty() == false) { - delete fields.back(); - fields.pop_back(); - } while (std::getline(isstr, item, ';') != 0) entries.push_back(item); + if (fields != NULL) { + delete fields; + fields = NULL; + } std::vector::iterator it = entries.begin(); - result_t result; - do { - result = DataField::create(it, entries.end(), templates, fields, isSet, isPredefine ? SYN : mstr[1]); - } while (result == RESULT_OK && it != entries.end()); + result_t result = DataField::create(it, entries.end(), templates, fields, isSet, isTemplate ? SYN : mstr[1]); if (result != RESULT_OK) { std::cout << "\"" << check[0] << "\": create error: " << getResultCodeCStr(result) << std::endl; continue; } - if (fields.empty() == true) { + if (fields == NULL) { std::cout << "\"" << check[0] << "\": create error: empty" << std::endl; continue; } + if (it != entries.end()) { + std::cout << "\"" << check[0] << "\": create error: non-empty" << std::endl; + continue; + } std::cout << "\"" << check[0] << "\": create OK" << std::endl; - if (isPredefine) { + if (isTemplate) { // store new template - while (fields.empty() == false) { - DataField* field = fields.front(); - fields.erase(fields.begin()); - std::map::iterator current = templates.find(field->getName()); - if (current == templates.end()) - templates[field->getName()] = field; - else { - delete current->second; - current->second = field; - } + std::string name = fields->getName(); + std::map::iterator current = templates.find(name); + if (current == templates.end()) { + templates[name] = fields; + } else { + delete current->second; + current->second = fields; } + fields = NULL; continue; } std::ostringstream output; - std::istringstream input(expectStr); SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); - bool first = true, failed = false; - while (fields.empty() == false) { - DataField* field = fields.front(); - fields.erase(fields.begin()); - if (first == false) - output << ";"; - - result = field->read(mstr, sstr, output, verbose); - if (failedRead == true) - if (result == RESULT_OK) - std::cout << " failed read " << field->getName() << " >" - << check[2] << "< error: unexpectedly succeeded" << std::endl; - else - std::cout << " failed read " << field->getName() << " >" - << check[2] << "< OK" << std::endl; - else if (result != RESULT_OK) { - std::cout << " read " << field->getName() << " >" << check[2] << "< error: " - << getResultCodeCStr(result) << std::endl; - failed = true; - } - else if (combinedValue == false) { - bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; - verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); - } - - if (verbose == false) { - std::string token; - std::getline(input, token, ';'); - std::istringstream tokeninput(token); - - result = field->write(tokeninput, writeMstr, writeSstr); - if (failedWrite == true) { - if (result == RESULT_OK) - std::cout << " failed write " << field->getName() << " >" - << expectStr << "< error: unexpectedly succeeded" << std::endl; - else - std::cout << " failed write " << field->getName() << " >" - << expectStr << "< OK" << std::endl; - } - else if (result != RESULT_OK) { - std::cout << " write " << field->getName() << " >" - << expectStr << "< error: " << getResultCodeCStr(result) << std::endl; - failed = true; - } - } - first = false; + result = fields->read(mstr, sstr, output, verbose); + if (failedRead == true) + if (result == RESULT_OK) + std::cout << " failed read " << fields->getName() << " >" + << check[2] << "< error: unexpectedly succeeded" << std::endl; + else + std::cout << " failed read " << fields->getName() << " >" + << check[2] << "< OK" << std::endl; + else if (result != RESULT_OK) { + std::cout << " read " << fields->getName() << " >" << check[2] << "< error: " + << getResultCodeCStr(result) << std::endl; } - - if (combinedValue == true && failedRead == false && failed == false) { + else { bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); } - if (verbose == false && failedWrite == false && failed == false) { - bool match = mstr == writeMstr && sstr == writeSstr; - verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr() + " " + sstr.getDataStr(), writeMstr.getDataStr() + " " + writeSstr.getDataStr()); + + if (verbose == false) { + std::istringstream input(expectStr); + result = fields->write(input, writeMstr, writeSstr); + if (failedWrite == true) { + if (result == RESULT_OK) + std::cout << " failed write " << fields->getName() << " >" + << expectStr << "< error: unexpectedly succeeded" << std::endl; + else + std::cout << " failed write " << fields->getName() << " >" + << expectStr << "< OK" << std::endl; + } + else if (result != RESULT_OK) { + std::cout << " write " << fields->getName() << " >" + << expectStr << "< error: " << getResultCodeCStr(result) << std::endl; + } + else { + bool match = mstr == writeMstr && sstr == writeSstr; + verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr() + " " + sstr.getDataStr(), writeMstr.getDataStr() + " " + writeSstr.getDataStr()); + } } + delete fields; + fields = NULL; } - while (fields.empty() == false) { - delete fields.back(); - fields.pop_back(); - } for (std::map::iterator it = templates.begin(); it != templates.end(); it++) delete it->second;