diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 048384ff..0ad1ad7e 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -211,7 +211,7 @@ void GrabbedMessage::setLastData(MasterSymbolString& master, SlaveSymbolString& * @param firstOnly whether to read only the first non-erroneous offset. * @return @a RESULT_OK on success, or an error code. */ -bool decodeType(DataType* type, SymbolString *input, size_t length, +bool decodeType(const DataType* type, SymbolString *input, size_t length, size_t offsets, ostringstream& output, bool firstOnly = false) { bool first = true; string in = input->getStr(input->getDataOffset()); @@ -225,7 +225,7 @@ bool decodeType(DataType* type, SymbolString *input, size_t length, unsigned int value = 0; if (type->readRawValue(*input, offset, length, value) == RESULT_OK) { out.str(""); - out << DataField::getDayName(reinterpret_cast(type)->getMinValue()+value); + out << DataField::getDayName(reinterpret_cast(type)->getMinValue()+value); } } if (first) { @@ -288,8 +288,8 @@ bool GrabbedMessage::dump(const bool unknown, MessageMap* messages, bool first, if (remain == 0) { return true; } - for (map::const_iterator it = types->begin(); it != types->end(); it++) { - DataType* baseType = it->second; + for (auto it : *types) { + const DataType* baseType = it.second; if ((baseType->getBitCount() % 8) != 0 || baseType->isIgnored()) { // skip bit and ignored types continue; } @@ -300,7 +300,7 @@ bool GrabbedMessage::dump(const bool unknown, MessageMap* messages, bool first, } if (baseType->isAdjustableLength()) { for (size_t length = maxLength; length >= 1; length--) { - DataType* type = types->get(baseType->getId(), length); + const DataType* type = types->get(baseType->getId(), length); if (decodeType(type, input, length, remain-length, output, firstOnly)) { if (firstOnly) { break; // only a single offset with maximum length when adjustable maximum size is at least 8 bytes @@ -1293,7 +1293,7 @@ void BusHandler::formatSeenInfo(ostringstream& output) { } } } - vector& loadedFiles = m_messages->getLoadedFiles(address); + const vector& loadedFiles = m_messages->getLoadedFiles(address); if (!loadedFiles.empty()) { bool first = true; for (auto& loadedFile : loadedFiles) { @@ -1358,7 +1358,7 @@ void BusHandler::formatUpdateInfo(ostringstream& output) { message->decodeLastData(output, OF_NAMES|OF_NUMERIC|OF_JSON|OF_SHORT, true); } } - vector& loadedFiles = m_messages->getLoadedFiles(address); + const vector& loadedFiles = m_messages->getLoadedFiles(address); if (!loadedFiles.empty()) { output << ",\"f\":["; bool first = true; diff --git a/src/ebusd/datahandler.h b/src/ebusd/datahandler.h index 9d474882..bee9b353 100644 --- a/src/ebusd/datahandler.h +++ b/src/ebusd/datahandler.h @@ -73,7 +73,7 @@ class UserInfo { * @param user the user name. * @return whether the user exists. */ - virtual bool hasUser(const string user) = 0; // abstract + virtual bool hasUser(const string user) const = 0; // abstract /** * Check whether the secret string matches the one of the specified user. @@ -81,14 +81,14 @@ class UserInfo { * @param secret the secret to check. * @return whether the secret string is valid. */ - virtual bool checkSecret(const string user, const string secret) = 0; // abstract + virtual bool checkSecret(const string user, const string secret) const = 0; // abstract /** * Get the access levels associated with the specified user. * @param user the user name, or empty for default levels. * @return the access levels separated by semicolon. */ - virtual string getLevels(const string user) = 0; // abstract + virtual string getLevels(const string user) const = 0; // abstract }; @@ -116,13 +116,13 @@ class DataHandler { * Return whether this is a @a DataSink instance. * @return whether this is a @a DataSink instance. */ - virtual bool isDataSink() { return false; } + virtual bool isDataSink() const { return false; } /** * Return whether this is a @a DataSource instance. * @return whether this is a @a DataSource instance. */ - virtual bool isDataSource() { return false; } + virtual bool isDataSource() const { return false; } }; @@ -146,7 +146,7 @@ class DataSink : virtual public DataHandler { virtual ~DataSink() {} // @copydoc - bool isDataSink() override { return true; } + bool isDataSink() const override { return true; } /** * Notify the sink of an updated @a Message. @@ -187,7 +187,7 @@ class DataSource : virtual public DataHandler { virtual ~DataSource() {} // @copydoc - bool isDataSource() override { return true; } + bool isDataSource() const override { return true; } protected: diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 4fe066c9..75f85001 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -884,9 +884,9 @@ result_t loadScanConfigFile(MessageMap* messages, symbol_t address, string& rela if (!message) { return RESULT_ERR_NOTFOUND; } - SlaveSymbolString& data = message->getLastSlaveData(); + const SlaveSymbolString& data = message->getLastSlaveData(); if (data.getDataSize() < 1+5+2+2) { - logError(lf_main, "unable to load scan config %2.2x: slave part too short", address); + logError(lf_main, "unable to load scan config %2.2x: slave part too short (%d)", address, data.getDataSize()); return RESULT_EMPTY; } DataFieldSet* identFields = DataFieldSet::getIdentFields(); diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index ee1c8949..6f3348bd 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -41,7 +41,7 @@ using std::ifstream; #define RECONNECT_MISSING_SIGNAL 60 -result_t UserList::getFieldMap(vector& row, string& errorDescription) { +result_t UserList::getFieldMap(vector& row, string& errorDescription) const { // name,secret,level[,level]* if (row.empty()) { row.push_back("name"); @@ -735,7 +735,7 @@ string MainLoop::executeRead(vector &args, const string levels) { if (srcAddress == SYN && (message->getLastUpdateTime() + maxAge > now || (message->isPassive() && message->getLastUpdateTime() != 0))) { - SlaveSymbolString& slave = message->getLastSlaveData(); + const SlaveSymbolString& slave = message->getLastSlaveData(); logNotice(lf_main, "hex read %s %s from cache", message->getCircuit().c_str(), message->getName().c_str()); return slave.getStr(); } @@ -998,7 +998,7 @@ string MainLoop::executeWrite(vector &args, const string levels) { getResultCode(ret)); return getResultCode(ret); } - dstAddress = message->getLastMasterData()[1]; + dstAddress = message->getLastMasterData().dataAt(1); ostringstream result; if (dstAddress == BROADCAST || isMaster(dstAddress)) { logNotice(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), @@ -1105,8 +1105,10 @@ string MainLoop::executeFind(vector &args, string levels) { } } else if (args[argPos] == "-vv") { verbosity |= OF_NAMES|OF_UNITS; - } else if (args[argPos] == "-vvv" || args[argPos] == "-V") { + } else if (args[argPos] == "-vvv") { verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; + } else if (args[argPos] == "-V") { + verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS|OF_ALL_ATTRS; } else if (args[argPos] == "-f") { configFormat = true; if (hexFormat) { @@ -1251,12 +1253,12 @@ string MainLoop::executeFind(vector &args, string levels) { << " / " << message->getLastSlaveData().getStr() << ")"; } } - if (verbosity == (OF_NAMES|OF_UNITS|OF_COMMENTS)) { + if ((verbosity & (OF_NAMES|OF_UNITS|OF_COMMENTS)) == (OF_NAMES|OF_UNITS|OF_COMMENTS)) { symbol_t dstAddress = message->getDstAddress(); if (dstAddress != SYN) { snprintf(str, sizeof(str), "%02x", dstAddress); } else if (lastup != 0 && message->getLastMasterData().size() > 1) { - snprintf(str, sizeof(str), "%02x", message->getLastMasterData()[1]); + snprintf(str, sizeof(str), "%02x", message->getLastMasterData().dataAt(1)); } else { snprintf(str, sizeof(str), "any"); } @@ -1533,7 +1535,7 @@ string MainLoop::executeHelp() { string MainLoop::executeGet(vector &args, bool& connected) { result_t ret = RESULT_OK; - bool numeric = false, required = false; + bool numeric = false, required = false, full = false; OutputFormat verbosity = OF_NAMES; size_t argPos = 1; string uri = args[argPos++]; @@ -1583,6 +1585,8 @@ string MainLoop::executeGet(vector &args, bool& connected) { } } else if (qname == "numeric") { numeric = value.length() == 0 || value == "1"; + } else if (qname == "full") { + full = value.length() == 0 || value == "1"; } else if (qname == "required") { required = value.length() == 0 || value == "1"; } else if (qname == "user") { @@ -1639,6 +1643,7 @@ string MainLoop::executeGet(vector &args, bool& connected) { } lastCircuit = message->getCircuit(); result << "\n \"" << lastCircuit << "\": {"; + // TODO add circuit specific values first = true; } if (first) { @@ -1652,7 +1657,8 @@ string MainLoop::executeGet(vector &args, bool& connected) { result << ",\n \"zz\": \"" << setfill('0') << setw(2) << hex << static_cast(dstAddress) << "\""; size_t pos = (size_t) result.tellp(); result << ",\n \"fields\": {"; - result_t dret = message->decodeLastData(result, verbosity | (numeric ? OF_NUMERIC : 0) | OF_JSON); + result_t dret = message->decodeLastData( + result, verbosity | (numeric ? OF_NUMERIC : 0) | OF_JSON | (full ? OF_ALL_ATTRS : 0)); if (dret == RESULT_OK) { result << "\n }"; } else { @@ -1671,7 +1677,25 @@ string MainLoop::executeGet(vector &args, bool& connected) { result << "\n },"; } result << "\n \"global\": {"; - result << "\n \"signal\": " << (m_busHandler->hasSignal() ? "1" : "0"); + result << "\n \"version\": \"" << PACKAGE_VERSION "." REVISION "\""; + if (!m_updateCheck.empty()) { + result << ",\n \"updatecheck\": \"" << m_updateCheck << "\""; + } + if (!user.empty()) { + result << ",\n \"user\": \"" << user << "\""; + } + string levels = getUserLevels(user); + if (!user.empty() || !levels.empty()) { + result << ",\n \"access\": \"" << levels << "\""; + } + result << ",\n \"signal\": " << (m_busHandler->hasSignal() ? "1" : "0"); + if (m_busHandler->hasSignal()) { + result << ",\n \"symbolrate\": " << m_busHandler->getSymbolRate(); + result << ",\n \"maxsymbolrate\": " << m_busHandler->getMaxSymbolRate(); + } + result << ",\n \"reconnects\": " << m_reconnectCount; + result << ",\n \"masters\": " << m_busHandler->getMasterCount(); + result << ",\n \"messages\": " << m_messages->size(); result << ",\n \"lastup\": " << setw(0) << dec << static_cast(maxLastUp); result << "\n }"; result << "\n}"; diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 0ffd1ad2..a82be03f 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -62,24 +62,28 @@ class UserList : public UserInfo, public MappedFileReader { virtual ~UserList() {} // @copydoc - result_t getFieldMap(vector& row, string& errorDescription) override; + result_t getFieldMap(vector& row, string& errorDescription) const override; // @copydoc result_t addFromFile(map& row, vector< map >& subRows, string& errorDescription, const string filename, unsigned int lineNo) override; // @copydoc - bool hasUser(const string user) override { + bool hasUser(const string user) const override { return m_userLevels.find(user) != m_userLevels.end(); } // @copydoc - bool checkSecret(const string user, const string secret) override { - return m_userSecrets.find(user) != m_userSecrets.end() && m_userSecrets[user] == secret; + bool checkSecret(const string user, const string secret) const override { + auto it = m_userSecrets.find(user); + return it != m_userSecrets.end() && it->second == secret; } // @copydoc - string getLevels(const string user) override { return m_userLevels[user]; } + string getLevels(const string user) const override { + auto it = m_userLevels.find(user); + return it == m_userLevels.end() ? "" : it->second; + } private: /** the secret string by user name. */ diff --git a/src/lib/ebus/contrib/tem.cpp b/src/lib/ebus/contrib/tem.cpp index cde1f788..59c0e04f 100644 --- a/src/lib/ebus/contrib/tem.cpp +++ b/src/lib/ebus/contrib/tem.cpp @@ -39,7 +39,7 @@ void contrib_tem_register() { DataTypeList::getInstance()->add(new TemParamDataType("TEM_P")); } -result_t TemParamDataType::derive(int divisor, size_t bitCount, NumberDataType* &derived) { +result_t TemParamDataType::derive(int divisor, size_t bitCount, const NumberDataType* &derived) const { if (divisor == 0) { divisor = 1; } @@ -53,9 +53,9 @@ result_t TemParamDataType::derive(int divisor, size_t bitCount, NumberDataType* return RESULT_ERR_INVALID_ARG; } -result_t TemParamDataType::readSymbols(SymbolString& input, +result_t TemParamDataType::readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { unsigned int value = 0; result_t result = readRawValue(input, offset, length, value); @@ -92,7 +92,7 @@ result_t TemParamDataType::readSymbols(SymbolString& input, result_t TemParamDataType::writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) { + SymbolString& output, size_t* usedLength) const { unsigned int value; int grp, num; string token; diff --git a/src/lib/ebus/contrib/tem.h b/src/lib/ebus/contrib/tem.h index af6fee56..07901541 100644 --- a/src/lib/ebus/contrib/tem.h +++ b/src/lib/ebus/contrib/tem.h @@ -47,20 +47,20 @@ class TemParamDataType : public NumberDataType { * @param id the type identifier. */ explicit TemParamDataType(const string id) - : NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {} + : NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0, NULL) {} // @copydoc - result_t derive(int divisor, size_t bitCount, NumberDataType* &derived) override; + result_t derive(int divisor, size_t bitCount, const NumberDataType* &derived) const override; // @copydoc - result_t readSymbols(SymbolString& input, + result_t readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) override; + ostringstream& output, OutputFormat outputFormat) const override; // @copydoc result_t writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) override; + SymbolString& output, size_t* usedLength) const override; }; /** diff --git a/src/lib/ebus/contrib/test/test_tem.cpp b/src/lib/ebus/contrib/test/test_tem.cpp index 7b27ae87..0b255883 100644 --- a/src/lib/ebus/contrib/test/test_tem.cpp +++ b/src/lib/ebus/contrib/test/test_tem.cpp @@ -54,7 +54,7 @@ class TestReader : public MappedFileReader { TestReader(DataFieldTemplates* templates, bool isSet, bool isMasterDest) : MappedFileReader::MappedFileReader(true), m_templates(templates), m_isSet(isSet), m_isMasterDest(isMasterDest), m_fields(NULL) {} - result_t getFieldMap(vector& row, string& errorDescription) override { + result_t getFieldMap(vector& row, string& errorDescription) const override { if (row.empty()) { row.push_back("*name"); row.push_back("part"); @@ -85,11 +85,11 @@ class TestReader : public MappedFileReader { const bool m_isSet; const bool m_isMasterDest; public: - DataField* m_fields; + const DataField* m_fields; }; int main() { - DataType* type = DataTypeList::getInstance()->get("TEM_P"); + const DataType* type = DataTypeList::getInstance()->get("TEM_P"); if (type == NULL) { cout << "datatype not registered" << endl; return 1; @@ -119,7 +119,7 @@ int main() { string errorDescription; vector row; templates->readLineFromStream(dummystr, errorDescription, "inline", lineNo, row); - DataField* fields = NULL; + const DataField* fields = NULL; for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { string check[5] = checks[i]; istringstream isstr(check[0]); diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 7f8ac4ee..1fb0af89 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -79,31 +79,114 @@ string getDataFieldName(const size_t fieldId) { } } + +const string AttributedItem::pluck(map& row, string key) { + map::iterator it = row.find(key); + if (it == row.end()) { + return ""; + } + row.erase(it); + return it->second; +} + +void AttributedItem::dumpString(ostream& output, const string str, const bool prependFieldSeparator) { + if (prependFieldSeparator) { + output << FIELD_SEPARATOR; + } + if (str.find_first_of(FIELD_SEPARATOR) == string::npos) { + output << str; + } else { + output << TEXT_SEPARATOR << str << TEXT_SEPARATOR; + } +} + +void AttributedItem::mergeAttributes(map& attributes) const { + for (auto& entry : m_attributes) { + auto it = attributes.find(entry.first); + if (it == attributes.end() || it->second.empty()) { + attributes[entry.first] = entry.second; + } + } +} + +void AttributedItem::dumpAttribute(ostream& output, const string name, const bool prependFieldSeparator) const { + dumpString(output, getAttribute(name), prependFieldSeparator); +} + +string AttributedItem::getAttribute(const string name) const { + auto it = m_attributes.find(name); + return it == m_attributes.end() ? "" : it->second; +} + +void appendJson(ostringstream& output, const string name, const string value, bool asString = false) { + bool plain; + if (asString) { + plain = false; + } else { + plain = value == "false" || value == "true"; + if (!plain) { + const char* str = value.c_str(); + char* strEnd = NULL; + double dvalue = strtod(str, &strEnd); + plain = strEnd && *strEnd; + } + } + output << ", \"" << name << "\": "; + if (plain) { + output << value; + } else { + output << '"' << value << '"'; + } +} + +void AttributedItem::appendAttribute(ostringstream& output, OutputFormat outputFormat, const string name, + const bool onlyIfNonEmpty, const string prefix, const string suffix) const { + auto it = m_attributes.find(name); + string value = it == m_attributes.end() ? "" : it->second; + if (!onlyIfNonEmpty || !value.empty()) { + if (outputFormat & OF_JSON) { + appendJson(output, name, value, true); + } else { + output << " " << prefix << value << suffix; + } + } +} + + + +string formatInt(size_t value) { + ostringstream stream; + stream << dec << static_cast(value); + return stream.str(); +} + result_t DataField::create(vector< map >& rows, string& errorDescription, - DataFieldTemplates* templates, DataField*& returnField, + DataFieldTemplates* templates, const DataField*& returnField, const bool isWriteMessage, const bool isTemplate, const bool isBroadcastOrMasterDestination, const size_t maxFieldLength) { // template: name,[,part]basetype[:len]|template[:name][,[divisor|values][,[unit][,[comment]]]] // std: name,part,basetype[:len]|template[:name][,[divisor|values][,[unit][,[comment]]]] - vector fields; + vector fields; string firstName; result_t result = RESULT_OK; if (rows.empty()) { + errorDescription = "no fields"; return RESULT_ERR_EOF; } + size_t fieldIndex = -1; for (auto row : rows) { if (result != RESULT_OK) { break; } - const string name = row["name"]; + fieldIndex++; + const string name = pluck(row, "name"); PartType partType; - int divisor = 0; bool hasPart = false; + string part = pluck(row, "part"); if (isTemplate) { partType = pt_any; } else { - string part = row["part"]; hasPart = !part.empty(); if (hasPart) { FileReader::tolower(part); @@ -116,48 +199,57 @@ result_t DataField::create(vector< map >& rows, string& errorDes || part == "s") { // slave data partType = pt_slaveData; } else { - errorDescription = "part "+part+" in "+MappedFileReader::combineRow(row); + errorDescription = "part "+part+" in field "+formatInt(fieldIndex); result = hasPart ? RESULT_ERR_INVALID_ARG : RESULT_ERR_MISSING_ARG; break; } } - string comment = row["comment"]; - if (comment == NULL_VALUE) { - comment = ""; - } if (fields.empty()) { firstName = name; } - const string typeStr = row["type"]; // basetype[:len]|template[:name] + const string typeStr = pluck(row, "type"); // basetype[:len]|template[:name] if (typeStr.empty()) { - errorDescription = "field type in "+MappedFileReader::combineRow(row); + errorDescription = "field type in field "+formatInt(fieldIndex); result = RESULT_ERR_MISSING_ARG; break; } + string divisorStr = pluck(row, "divisor"); + string valuesStr = pluck(row, "values"); + if (divisorStr.empty() && valuesStr.empty()) { + divisorStr = pluck(row, "divisor/values"); // [divisor|values] + if (divisorStr.find('=') != string::npos) { + valuesStr = divisorStr; + divisorStr = ""; + } + } + int divisor = 0; + if (!divisorStr.empty()) { + divisor = parseSignedInt(divisorStr.c_str(), 10, -MAX_DIVISOR, MAX_DIVISOR, result); + if (result != RESULT_OK) { + errorDescription = "divisor "+divisorStr+" in field "+formatInt(fieldIndex); + } + } + bool verifyValue = false; map values; string constantValue; - bool verifyValue = false; - const string divisorStr = row["divisor/values"]; // [divisor|values] - if (!divisorStr.empty()) { - size_t equalPos = divisorStr.find('='); + if (!valuesStr.empty()) { + size_t equalPos = valuesStr.find('='); if (equalPos == string::npos) { - divisor = parseSignedInt(divisorStr.c_str(), 10, -MAX_DIVISOR, MAX_DIVISOR, result); - if (result != RESULT_OK) { - errorDescription = "divisor "+divisorStr+" in "+MappedFileReader::combineRow(row); - } - } else if (equalPos == 0 && divisorStr.length() > 1) { - verifyValue = divisorStr[1] == '='; // == forced verification of constant value - if (verifyValue && divisorStr.length() == 1) { - errorDescription = "divisor "+divisorStr+" in "+MappedFileReader::combineRow(row); + errorDescription = "values "+valuesStr+" in field "+formatInt(fieldIndex); + result = RESULT_ERR_INVALID_LIST; + } else if (equalPos == 0 && valuesStr.length() > 1) { + verifyValue = valuesStr[1] == '='; // == forced verification of constant value + if (verifyValue && valuesStr.length() == 1) { + errorDescription = "values "+valuesStr+" in field "+formatInt(fieldIndex); result = RESULT_ERR_INVALID_LIST; break; } - constantValue = divisorStr.substr(equalPos+(verifyValue?2:1)); + constantValue = valuesStr.substr(equalPos+(verifyValue?2:1)); } else { string token; - istringstream stream(divisorStr); + istringstream stream(valuesStr); while (getline(stream, token, VALUE_SEPARATOR)) { FileReader::trim(token); const char* str = token.c_str(); @@ -170,14 +262,14 @@ result_t DataField::create(vector< map >& rows, string& errorDes id = strtoul(str, &strEnd, 10); // decimal } if (strEnd == NULL || strEnd == str || id > MAX_VALUE) { - errorDescription = "value "+token+" in "+MappedFileReader::combineRow(row); + errorDescription = "value "+token+" in field "+formatInt(fieldIndex); result = RESULT_ERR_INVALID_LIST; break; } // remove blanks around '=' sign while (*strEnd == ' ') strEnd++; if (*strEnd != '=') { - errorDescription = "value "+token+" in "+MappedFileReader::combineRow(row); + errorDescription = "value "+token+" in field "+formatInt(fieldIndex); result = RESULT_ERR_INVALID_LIST; break; } @@ -191,16 +283,13 @@ result_t DataField::create(vector< map >& rows, string& errorDes } } - string unit = row["unit"]; - if (unit == NULL_VALUE) { - unit = ""; - } bool firstType = true; string token; istringstream stream(typeStr); while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR)) { + bool lastType = stream.eof(); FileReader::trim(token); - DataField* templ = templates->get(token); + const DataField* templ = templates->get(token); size_t pos = token.find(LENGTH_SEPARATOR); if (templ == NULL && pos != string::npos) { templ = templates->get(token.substr(0, pos)); @@ -217,43 +306,51 @@ result_t DataField::create(vector< map >& rows, string& errorDes } else { length = (size_t)parseInt(token.substr(pos+1).c_str(), 10, 1, (unsigned int)maxFieldLength, result); if (result != RESULT_OK) { - errorDescription = "field type "+token+" in "+MappedFileReader::combineRow(row); + errorDescription = "field type "+token+" in field "+formatInt(fieldIndex); break; } } typeName = token.substr(0, pos); } transform(typeName.begin(), typeName.end(), typeName.begin(), ::toupper); - SingleDataField* add = NULL; - result = SingleDataField::create(typeName, length, firstType ? name : "", firstType ? comment : "", - firstType ? unit : "", partType, divisor, values, constantValue, verifyValue, add); - if (add != NULL) { - fields.push_back(add); + const DataType* dataType = DataTypeList::getInstance()->get(typeName, length == REMAIN_LEN ? 0 : length); + if (!dataType) { + result = RESULT_ERR_NOTFOUND; + errorDescription = "field type "+typeName+" in field "+formatInt(fieldIndex); } else { - if (result == RESULT_OK) { - errorDescription = "field type " + typeName+" in "+MappedFileReader::combineRow(row); - result = RESULT_ERR_NOTFOUND; // type not found + SingleDataField* add = NULL; + result = SingleDataField::create(firstType ? name : "", row, dataType, partType, length, divisor, values, + constantValue, verifyValue, add); + if (add != NULL) { + fields.push_back(add); } else { - errorDescription = "create field in "+MappedFileReader::combineRow(row); + if (result == RESULT_OK) { + errorDescription = "field type "+typeName+" in field "+formatInt(fieldIndex); + result = RESULT_ERR_NOTFOUND; // type not found + } else { + errorDescription = "create field in field "+formatInt(fieldIndex); + } } } } else if (!constantValue.empty()) { - errorDescription = "constant value "+constantValue+" in "+MappedFileReader::combineRow(row); + errorDescription = "constant value "+constantValue+" in field "+formatInt(fieldIndex); result = RESULT_ERR_INVALID_ARG; // invalid value list } else { // template[:name] string fieldName; - bool lastType = stream.eof(); if (pos != string::npos) { // replacement name specified fieldName = token.substr(pos+1); } else { fieldName = (firstType && lastType) ? name : ""; } - result = templ->derive(fieldName, firstType ? comment : "", firstType ? unit : "", partType, divisor, values, - fields); + result = templ->derive(fieldName, row, partType, divisor, values, fields); if (result != RESULT_OK) { - errorDescription = "derive field "+fieldName+" in "+MappedFileReader::combineRow(row); + errorDescription = "derive field "+fieldName+" in field "+formatInt(fieldIndex); } } + if (firstType && !lastType) { + pluck(row, "comment"); + pluck(row, "unit"); + } firstType = false; } } @@ -269,22 +366,11 @@ result_t DataField::create(vector< map >& rows, string& errorDes if (fields.size() == 1) { returnField = fields[0]; } else { - returnField = new DataFieldSet(firstName, "", fields); + returnField = new DataFieldSet(firstName, fields); } return RESULT_OK; } -void DataField::dumpString(ostream& output, const string str, const bool prependFieldSeparator) { - if (prependFieldSeparator) { - output << FIELD_SEPARATOR; - } - if (str.find_first_of(FIELD_SEPARATOR) == string::npos) { - output << str; - } else { - output << TEXT_SEPARATOR << str << TEXT_SEPARATOR; - } -} - string DataField::getDayName(int day) { if (day < 0 || day > 6) { return ""; @@ -292,14 +378,10 @@ string DataField::getDayName(int day) { return dayNames[day]; } -result_t SingleDataField::create(const string id, const size_t length, - const string name, const string comment, const string unit, - const PartType partType, int divisor, map values, - const string constantValue, const bool verifyValue, SingleDataField* &returnField) { - DataType* dataType = DataTypeList::getInstance()->get(id, length == REMAIN_LEN ? 0 : length); - if (!dataType) { - return RESULT_ERR_NOTFOUND; - } + +result_t SingleDataField::create(const string name, const map& attributes, const DataType* dataType, + const PartType partType, const size_t length, int divisor, map values, + const string constantValue, const bool verifyValue, SingleDataField* &returnField) { size_t bitCount = dataType->getBitCount(); size_t byteCount = (bitCount + 7) / 8; if (dataType->isAdjustableLength()) { @@ -322,11 +404,11 @@ result_t SingleDataField::create(const string id, const size_t length, } } if (!constantValue.empty()) { - returnField = new ConstantDataField(name, comment, unit, dataType, partType, byteCount, constantValue, verifyValue); + returnField = new ConstantDataField(name, attributes, dataType, partType, byteCount, constantValue, verifyValue); return RESULT_OK; } if (dataType->isNumeric()) { - NumberDataType* numType = reinterpret_cast(dataType); + const NumberDataType* numType = reinterpret_cast(dataType); if (values.empty() && numType->hasFlag(DAY)) { for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++) { values[numType->getMinValue() + i] = dayNames[i]; @@ -337,23 +419,23 @@ result_t SingleDataField::create(const string id, const size_t length, return result; } if (values.empty()) { - returnField = new SingleDataField(name, comment, unit, numType, partType, byteCount); + returnField = new SingleDataField(name, attributes, numType, partType, byteCount); return RESULT_OK; } if (values.begin()->first < numType->getMinValue() || values.rbegin()->first > numType->getMaxValue()) { return RESULT_ERR_OUT_OF_RANGE; } - returnField = new ValueListDataField(name, comment, unit, numType, partType, byteCount, values); + returnField = new ValueListDataField(name, attributes, numType, partType, byteCount, values); return RESULT_OK; } if (divisor != 0 || !values.empty()) { return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for string field } - returnField = new SingleDataField(name, comment, unit, dataType, partType, byteCount); + returnField = new SingleDataField(name, attributes, dataType, partType, byteCount); return RESULT_OK; } -void SingleDataField::dump(ostream& output) { +void SingleDataField::dump(ostream& output) const { output << setw(0) << dec; // initialize formatting dumpString(output, m_name, false); output << FIELD_SEPARATOR; @@ -364,13 +446,13 @@ void SingleDataField::dump(ostream& output) { } output << FIELD_SEPARATOR; m_dataType->dump(output, m_length); - dumpString(output, m_unit); - dumpString(output, m_comment); + dumpAttribute(output, "unit"); + dumpAttribute(output, "comment"); } -result_t SingleDataField::read(SymbolString& data, size_t offset, - unsigned int& output, const char* fieldName, ssize_t fieldIndex) { +result_t SingleDataField::read(const SymbolString& data, size_t offset, + unsigned int& output, const char* fieldName, ssize_t fieldIndex) const { if (m_partType == pt_any) { return RESULT_ERR_INVALID_PART; } @@ -384,12 +466,13 @@ result_t SingleDataField::read(SymbolString& data, size_t offset, if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { return RESULT_EMPTY; } - return m_dataType->readRawValue(data, offset, m_length, output); + result_t res = m_dataType->readRawValue(data, offset, m_length, output); + return res; } -result_t SingleDataField::read(SymbolString& data, size_t offset, +result_t SingleDataField::read(const SymbolString& data, size_t offset, ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex, - bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) { + bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) const { if (m_partType == pt_any) { return RESULT_ERR_INVALID_PART; } @@ -435,18 +518,23 @@ result_t SingleDataField::read(SymbolString& data, size_t offset, if (result != RESULT_OK) { return result; } - if (!shortFormat && (outputFormat & OF_UNITS) && m_unit.length() > 0) { - if (outputFormat & OF_JSON) { - output << ", \"unit\": \"" << m_unit << '"'; - } else { - output << " " << m_unit; + if (!shortFormat) { + if ((outputFormat & OF_UNITS)) { + appendAttribute(output, outputFormat, "unit"); } - } - if (!shortFormat && (outputFormat & OF_COMMENTS) && m_comment.length() > 0) { - if (outputFormat & OF_JSON) { - output << ", \"comment\": \"" << m_comment << '"'; - } else { - output << " [" << m_comment << "]"; + if ((outputFormat & OF_COMMENTS)) { + appendAttribute(output, outputFormat, "comment", true, "[", "]"); + } + if (outputFormat & OF_ALL_ATTRS) { + for (auto& entry : m_attributes) { + if (!entry.second.empty() && entry.first != "unit" && entry.first != "comment") { + if (outputFormat & OF_JSON) { + appendJson(output, entry.first, entry.second); + } else { + output << " " << entry.first << "=" << entry.second; + } + } + } } } if (!shortFormat && (outputFormat & OF_JSON)) { @@ -456,7 +544,7 @@ result_t SingleDataField::read(SymbolString& data, size_t offset, } result_t SingleDataField::write(istringstream& input, SymbolString& data, - size_t offset, char separator, size_t* length) { + size_t offset, char separator, size_t* length) const { if (m_partType == pt_any) { return RESULT_ERR_INVALID_PART; } @@ -466,26 +554,24 @@ result_t SingleDataField::write(istringstream& input, SymbolString& data, return writeSymbols(input, (const size_t)offset, data, length); } -result_t SingleDataField::readSymbols(SymbolString& input, +result_t SingleDataField::readSymbols(const SymbolString& input, const size_t offset, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { return m_dataType->readSymbols(input, offset, m_length, output, outputFormat); } result_t SingleDataField::writeSymbols(istringstream& input, - const size_t offset, - SymbolString& output, size_t* usedLength) { + const size_t offset, + SymbolString& output, size_t* usedLength) const { return m_dataType->writeSymbols(input, offset, m_length, output, usedLength); } -SingleDataField* SingleDataField::clone() { +const SingleDataField* SingleDataField::clone() const { return new SingleDataField(*this); } -result_t SingleDataField::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { +result_t SingleDataField::derive(string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const { if (m_partType != pt_any && partType == pt_any) { return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance } @@ -496,15 +582,10 @@ result_t SingleDataField::derive(string name, string comment, if (name.empty()) { name = m_name; } - if (comment.empty()) { - comment = m_comment; - } - if (unit.empty()) { - unit = m_unit; - } - DataType* dataType = m_dataType; + mergeAttributes(attributes); + const DataType* dataType = m_dataType; if (numeric) { - NumberDataType* numType = reinterpret_cast(dataType); + const NumberDataType* numType = reinterpret_cast(dataType); result_t result = numType->derive(divisor, 0, numType); if (result != RESULT_OK) { return result; @@ -512,22 +593,22 @@ result_t SingleDataField::derive(string name, string comment, dataType = numType; } if (values.empty()) { - fields.push_back(new SingleDataField(name, comment, unit, dataType, partType, m_length)); + fields.push_back(new SingleDataField(name, attributes, dataType, partType, m_length)); } else if (numeric) { - fields.push_back(new ValueListDataField(name, comment, unit, reinterpret_cast(dataType), - partType, m_length, values)); + fields.push_back(new ValueListDataField(name, attributes, reinterpret_cast(dataType), + partType, m_length, values)); } else { return RESULT_ERR_INVALID_ARG; } return RESULT_OK; } -bool SingleDataField::hasField(const char* fieldName, bool numeric) { +bool SingleDataField::hasField(const char* fieldName, bool numeric) const { bool numericType = m_dataType->isNumeric(); return numeric == numericType && (fieldName == NULL || fieldName == m_name); } -size_t SingleDataField::getLength(PartType partType, size_t maxLength) { +size_t SingleDataField::getLength(PartType partType, size_t maxLength) const { if (partType != m_partType) { return 0; } @@ -535,13 +616,13 @@ size_t SingleDataField::getLength(PartType partType, size_t maxLength) { return remainder ? maxLength : m_length; } -bool SingleDataField::hasFullByteOffset(bool after) { +bool SingleDataField::hasFullByteOffset(bool after) const { if (m_length > 1) { return true; } int16_t firstBit; if (m_dataType->isNumeric()) { - NumberDataType* num = reinterpret_cast(m_dataType); + const NumberDataType* num = reinterpret_cast(m_dataType); firstBit = num->getFirstBit(); } else { firstBit = 0; @@ -551,26 +632,17 @@ bool SingleDataField::hasFullByteOffset(bool after) { } -ValueListDataField* ValueListDataField::clone() { +const ValueListDataField* ValueListDataField::clone() const { return new ValueListDataField(*this); } -result_t ValueListDataField::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { +result_t ValueListDataField::derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const { if (m_partType != pt_any && partType == pt_any) { return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance } - if (name.empty()) { - name = m_name; - } - if (comment.empty()) { - comment = m_comment; - } - if (unit.empty()) { - unit = m_unit; - } + string useName = name.empty() ? m_name : name; + mergeAttributes(attributes); if (divisor != 0 && divisor != 1) { return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field } @@ -578,19 +650,19 @@ result_t ValueListDataField::derive(string name, string comment, return RESULT_ERR_INVALID_ARG; } if (!values.empty()) { - NumberDataType* num = reinterpret_cast(m_dataType); + const NumberDataType* num = reinterpret_cast(m_dataType); if (values.begin()->first < num->getMinValue() || values.rbegin()->first > num->getMaxValue()) { return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field } } else { values = m_values; } - fields.push_back(new ValueListDataField(name, comment, unit, reinterpret_cast(m_dataType), - partType, m_length, values)); + fields.push_back(new ValueListDataField(useName, attributes, reinterpret_cast(m_dataType), + partType, m_length, values)); return RESULT_OK; } -void ValueListDataField::dump(ostream& output) { +void ValueListDataField::dump(ostream& output) const { output << setw(0) << dec; // initialize formatting dumpString(output, m_name, false); output << FIELD_SEPARATOR; @@ -601,27 +673,30 @@ void ValueListDataField::dump(ostream& output) { } output << FIELD_SEPARATOR; if (!m_dataType->dump(output, m_length)) { // no divisor appended - for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { - if (it != m_values.begin()) { + bool first = true; + for (auto it : m_values) { + if (first) { + first = false; + } else { output << VALUE_SEPARATOR; } - output << static_cast(it->first) << "=" << it->second; + output << static_cast(it.first) << "=" << it.second; } } // else: impossible since divisor is not allowed for ValueListDataField - dumpString(output, m_unit); - dumpString(output, m_comment); + dumpAttribute(output, "unit"); + dumpAttribute(output, "comment"); } -result_t ValueListDataField::readSymbols(SymbolString& input, +result_t ValueListDataField::readSymbols(const SymbolString& input, const size_t offset, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { unsigned int value = 0; result_t result = m_dataType->readRawValue(input, offset, m_length, value); if (result != RESULT_OK) { return result; } - map::iterator it = m_values.find(value); + auto it = m_values.find(value); if (it == m_values.end() && value != m_dataType->getReplacement()) { // fall back to raw value in input output << setw(0) << dec << static_cast(value); @@ -645,17 +720,17 @@ result_t ValueListDataField::readSymbols(SymbolString& input, result_t ValueListDataField::writeSymbols(istringstream& input, const size_t offset, - SymbolString& output, size_t* usedLength) { - NumberDataType* numType = reinterpret_cast(m_dataType); + SymbolString& output, size_t* usedLength) const { + const NumberDataType* numType = reinterpret_cast(m_dataType); if (isIgnored()) { // replacement value return numType->writeRawValue(numType->getReplacement(), offset, m_length, output, usedLength); } const char* str = input.str().c_str(); - for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { - if (it->second.compare(str) == 0) { - return numType->writeRawValue(it->first, offset, m_length, output, usedLength); + for (auto it : m_values) { + if (it.second.compare(str) == 0) { + return numType->writeRawValue(it.first, offset, m_length, output, usedLength); } } if (strcasecmp(str, NULL_VALUE) == 0) { @@ -675,25 +750,20 @@ result_t ValueListDataField::writeSymbols(istringstream& input, } -ConstantDataField* ConstantDataField::clone() { +const ConstantDataField* ConstantDataField::clone() const { return new ConstantDataField(*this); } -result_t ConstantDataField::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { +result_t ConstantDataField::derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const { if (m_partType != pt_any && partType == pt_any) { return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance } - if (name.empty()) { - name = m_name; - } - if (comment.empty()) { - comment = m_comment; - } - if (unit.empty()) { - unit = m_unit; + string useName = name.empty() ? m_name : name; + for (auto entry : m_attributes) { // merge with this attributes + if (attributes[entry.first].empty()) { + attributes[entry.first] = entry.second; + } } if (divisor != 0) { return RESULT_ERR_INVALID_ARG; // cannot use other than current divisor for constant value field @@ -701,11 +771,11 @@ result_t ConstantDataField::derive(string name, string comment, if (!values.empty()) { return RESULT_ERR_INVALID_ARG; // cannot use value list for constant value field } - fields.push_back(new ConstantDataField(name, comment, unit, m_dataType, partType, m_length, m_value, m_verify)); + fields.push_back(new ConstantDataField(useName, attributes, m_dataType, partType, m_length, m_value, m_verify)); return RESULT_OK; } -void ConstantDataField::dump(ostream& output) { +void ConstantDataField::dump(ostream& output) const { output << setw(0) << dec; // initialize formatting dumpString(output, m_name, false); output << FIELD_SEPARATOR; @@ -718,13 +788,13 @@ void ConstantDataField::dump(ostream& output) { if (!m_dataType->dump(output, m_length)) { // no divisor appended output << (m_verify?"==":"=") << m_value; } // else: impossible since divisor is not allowed for ConstantDataField - dumpString(output, m_unit); - dumpString(output, m_comment); + dumpAttribute(output, "unit"); + dumpAttribute(output, "comment"); } -result_t ConstantDataField::readSymbols(SymbolString& input, +result_t ConstantDataField::readSymbols(const SymbolString& input, const size_t offset, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { ostringstream coutput; result_t result = SingleDataField::readSymbols(input, offset, coutput, 0); if (result != RESULT_OK) { @@ -742,7 +812,7 @@ result_t ConstantDataField::readSymbols(SymbolString& input, result_t ConstantDataField::writeSymbols(istringstream& input, const size_t offset, - SymbolString& output, size_t* usedLength) { + SymbolString& output, size_t* usedLength) const { istringstream cinput(m_value); return SingleDataField::writeSymbols(cinput, offset, output, usedLength); } @@ -752,9 +822,12 @@ DataFieldSet* DataFieldSet::s_identFields = NULL; DataFieldSet* DataFieldSet::getIdentFields() { if (s_identFields == NULL) { - NumberDataType* uchDataType = reinterpret_cast(DataTypeList::getInstance()->get("UCH")); - StringDataType* stringDataType = reinterpret_cast(DataTypeList::getInstance()->get("STR")); - NumberDataType* pinDataType = reinterpret_cast(DataTypeList::getInstance()->get("PIN")); + const NumberDataType* uchDataType = reinterpret_cast( + DataTypeList::getInstance()->get("UCH")); + const StringDataType* stringDataType = reinterpret_cast( + DataTypeList::getInstance()->get("STR")); + const NumberDataType* pinDataType = reinterpret_cast( + DataTypeList::getInstance()->get("PIN")); map manufacturers; manufacturers[0x06] = "Dungs"; manufacturers[0x0f] = "FH Ostfalia"; @@ -781,37 +854,36 @@ DataFieldSet* DataFieldSet::getIdentFields() { manufacturers[0xc0] = "Toby"; manufacturers[0xc5] = "Weishaupt"; manufacturers[0xfd] = "ebusd.eu"; - vector fields; - fields.push_back(new ValueListDataField("MF", "", "", uchDataType, pt_slaveData, 1, manufacturers)); - fields.push_back(new SingleDataField("ID", "", "", stringDataType, pt_slaveData, 5)); - fields.push_back(new SingleDataField("SW", "", "", pinDataType, pt_slaveData, 2)); - fields.push_back(new SingleDataField("HW", "", "", pinDataType, pt_slaveData, 2)); - s_identFields = new DataFieldSet("ident", "", fields); + vector fields; + map attributes; + fields.push_back(new ValueListDataField("MF", attributes, uchDataType, pt_slaveData, 1, manufacturers)); + fields.push_back(new SingleDataField("ID", attributes, stringDataType, pt_slaveData, 5)); + fields.push_back(new SingleDataField("SW", attributes, pinDataType, pt_slaveData, 2)); + fields.push_back(new SingleDataField("HW", attributes, pinDataType, pt_slaveData, 2)); + s_identFields = new DataFieldSet("ident", fields); } return s_identFields; } DataFieldSet::~DataFieldSet() { - while (!m_fields.empty()) { - delete m_fields.back(); - m_fields.pop_back(); + for (auto it : m_fields) { + delete it; } } -DataFieldSet* DataFieldSet::clone() { - vector fields; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - fields.push_back((*it)->clone()); +const DataFieldSet* DataFieldSet::clone() const { + vector fields; + for (auto it : m_fields) { + fields.push_back(it->clone()); } - return new DataFieldSet(m_name, m_comment, fields); + return new DataFieldSet(m_name, fields); } -size_t DataFieldSet::getLength(PartType partType, size_t maxLength) { +size_t DataFieldSet::getLength(PartType partType, size_t maxLength) const { size_t length = 0; bool previousFullByteOffset[] = { true, true, true, true }; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; + for (auto field : m_fields) { if (field->getPartType() == partType) { if (!previousFullByteOffset[partType] && !field->hasFullByteOffset(false)) { length--; @@ -831,7 +903,7 @@ size_t DataFieldSet::getLength(PartType partType, size_t maxLength) { return length; } -string DataFieldSet::getName(ssize_t fieldIndex) { +string DataFieldSet::getName(const ssize_t fieldIndex) const { if (fieldIndex < 0) { return m_name; } @@ -846,28 +918,25 @@ string DataFieldSet::getName(ssize_t fieldIndex) { return ostream.str(); } -result_t DataFieldSet::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { +result_t DataFieldSet::derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const { if (!values.empty()) { return RESULT_ERR_INVALID_ARG; // value list not allowed in set derive } - bool first = true; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - result_t result = (*it)->derive("", first?comment:"", first?unit:"", partType, divisor, values, fields); + for (auto it : m_fields) { + result_t result = it->derive("", attributes, partType, divisor, values, fields); if (result != RESULT_OK) { return result; } - first = false; + pluck(attributes, "comment"); + pluck(attributes, "unit"); } return RESULT_OK; } -bool DataFieldSet::hasField(const char* fieldName, bool numeric) { - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; +bool DataFieldSet::hasField(const char* fieldName, bool numeric) const { + for (auto field : m_fields) { if (field->hasField(fieldName, numeric) == 0) { return true; } @@ -875,24 +944,23 @@ bool DataFieldSet::hasField(const char* fieldName, bool numeric) { return false; } -void DataFieldSet::dump(ostream& output) { +void DataFieldSet::dump(ostream& output) const { bool first = true; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + for (auto it : m_fields) { if (first) { first = false; } else { output << FIELD_SEPARATOR; } - (*it)->dump(output); + it->dump(output); } } -result_t DataFieldSet::read(SymbolString& data, size_t offset, - unsigned int& output, const char* fieldName, ssize_t fieldIndex) { +result_t DataFieldSet::read(const SymbolString& data, size_t offset, + unsigned int& output, const char* fieldName, ssize_t fieldIndex) const { bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; PartType partType = data.isMaster() ? pt_masterData : pt_slaveData; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; + for (auto field : m_fields) { if (field->getPartType() != partType) { continue; } @@ -926,16 +994,15 @@ result_t DataFieldSet::read(SymbolString& data, size_t offset, return RESULT_OK; } -result_t DataFieldSet::read(SymbolString& data, size_t offset, +result_t DataFieldSet::read(const SymbolString& data, size_t offset, ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex, - bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) { + bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) const { bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; if (outputIndex < 0 && (!m_uniqueNames || ((outputFormat & OF_JSON) && !(outputFormat & OF_NAMES)))) { outputIndex = 0; } PartType partType = data.isMaster() ? pt_masterData : pt_slaveData; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; + for (auto field : m_fields) { if (field->getPartType() != partType) { if (outputIndex >= 0 && !field->isIgnored()) { outputIndex++; @@ -973,25 +1040,16 @@ result_t DataFieldSet::read(SymbolString& data, size_t offset, if (!found) { return RESULT_EMPTY; } - if (!(outputFormat & OF_SHORT) && (outputFormat & OF_COMMENTS) && m_comment.length() > 0) { - if (outputFormat & OF_JSON) { - output << ",\"comment\": \"" << m_comment << '"'; - } else { - output << " [" << m_comment << "]"; - } - } - return RESULT_OK; } result_t DataFieldSet::write(istringstream& input, SymbolString& data, - size_t offset, char separator, size_t* length) { + size_t offset, char separator, size_t* length) const { string token; PartType partType = data.isMaster() ? pt_masterData : pt_slaveData; bool previousFullByteOffset = true; size_t baseOffset = offset; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; + for (auto field : m_fields) { if (field->getPartType() != partType) { continue; } @@ -1007,9 +1065,9 @@ result_t DataFieldSet::write(istringstream& input, SymbolString& data, token.clear(); } istringstream single(token); - result = (*it)->write(single, data, offset, separator, &fieldLength); + result = field->write(single, data, offset, separator, &fieldLength); } else { - result = (*it)->write(input, data, offset, separator, &fieldLength); + result = field->write(input, data, offset, separator, &fieldLength); } if (result != RESULT_OK) { return result; @@ -1027,24 +1085,24 @@ result_t DataFieldSet::write(istringstream& input, SymbolString& data, DataFieldTemplates::DataFieldTemplates(DataFieldTemplates& other) : MappedFileReader::MappedFileReader(false) { - for (map::iterator it = other.m_fieldsByName.begin(); it != other.m_fieldsByName.end(); it++) { - m_fieldsByName[it->first] = it->second->clone(); + for (auto it : other.m_fieldsByName) { + m_fieldsByName[it.first] = it.second->clone(); } } void DataFieldTemplates::clear() { - for (map::iterator it = m_fieldsByName.begin(); it != m_fieldsByName.end(); it++) { - delete it->second; - it->second = NULL; + for (auto it : m_fieldsByName) { + delete it.second; + it.second = NULL; } m_fieldsByName.clear(); } -result_t DataFieldTemplates::add(DataField* field, string name, bool replace) { +result_t DataFieldTemplates::add(const DataField* field, string name, bool replace) { if (name.length() == 0) { name = field->getName(); } - map::iterator it = m_fieldsByName.find(name); + auto it = m_fieldsByName.find(name); if (it != m_fieldsByName.end()) { if (!replace) { return RESULT_ERR_DUPLICATE_NAME; // duplicate key @@ -1058,7 +1116,7 @@ result_t DataFieldTemplates::add(DataField* field, string name, bool replace) { return RESULT_OK; } -result_t DataFieldTemplates::getFieldMap(vector& row, string& errorDescription) { +result_t DataFieldTemplates::getFieldMap(vector& row, string& errorDescription) const { // name[:usename],basetype[:len]|template[:usename][,[divisor|values][,[unit][,[comment]]]] if (row.empty()) { // default map does not include separate field name @@ -1086,49 +1144,54 @@ result_t DataFieldTemplates::getFieldMap(vector& row, string& errorDescr bool inDataFields = false; map seen; for (auto &name : row) { - tolower(name); - size_t fieldId; + string useName = name; + tolower(useName); if (inDataFields) { - fieldId = getDataFieldId(name); - if (fieldId == UINT_MAX) { - errorDescription = "unknown field " + name; - return RESULT_ERR_INVALID_ARG; - } - if (seen.find(name) != seen.end()) { - if (seen.find("type") == seen.end()) { - return RESULT_ERR_EOF; // require at least type + size_t fieldId = getDataFieldId(useName); + if (fieldId != UINT_MAX) { + useName = getDataFieldName(fieldId); + if (seen.find(useName) != seen.end()) { + if (seen.find("type") == seen.end()) { + errorDescription = "missing type"; + return RESULT_ERR_EOF; // require at least type + } + seen.clear(); } - seen.clear(); - name = "*" + getDataFieldName(fieldId); // data field repetition - } else { - name = getDataFieldName(fieldId); } - } else if (name == "name") { - if (seen.find(name) != seen.end()) { - errorDescription = "duplicate field " + name; - return RESULT_ERR_INVALID_ARG; - } - name = "name"; } else { - fieldId = getDataFieldId(name); - if (fieldId == UINT_MAX) { - errorDescription = "unknown field " + name; + if (useName == "name" && seen.find("name") == seen.end()) { + // keep first name for template + } else { + size_t fieldId = getDataFieldId(useName); + if (fieldId != UINT_MAX) { + useName = getDataFieldName(fieldId); + if (seen.find("name") == seen.end()) { + errorDescription = "missing name"; + return RESULT_ERR_EOF; // require at least name + } + inDataFields = true; + seen.clear(); + } + } + if (!inDataFields && seen.find(useName) != seen.end()) { + errorDescription = "duplicate " + useName; return RESULT_ERR_INVALID_ARG; } - if (seen.find("name") == seen.end()) { - return RESULT_ERR_EOF; // require at least name - } - inDataFields = true; - seen.clear(); - name = "*" + getDataFieldName(fieldId); } - seen[name] = name; + if (seen.empty() && inDataFields) { + name = "*" + useName; // data field repetition + } else { + name = useName; + } + seen[useName] = useName; } if (!inDataFields) { + errorDescription = "missing fields"; return RESULT_ERR_EOF; // require at least one field } - if (seen.find("name") == seen.end() || seen.find("type") == seen.end()) { - return RESULT_ERR_EOF; // require at least name and type + if (seen.find("type") == seen.end()) { + errorDescription = "missing type"; + return RESULT_ERR_EOF; // require at least type } return RESULT_OK; } @@ -1144,7 +1207,7 @@ result_t DataFieldTemplates::addFromFile(map& row, vector< map& row, vector< map::const_iterator ref = m_fieldsByName.find(name); +const DataField* DataFieldTemplates::get(const string name) const { + auto ref = m_fieldsByName.find(name); if (ref == m_fieldsByName.end()) { return NULL; } diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index 4175b6ca..c5346e71 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -91,18 +91,116 @@ string getDataFieldName(const size_t fieldId); class DataFieldTemplates; class SingleDataField; +/** + * Base class for named items with optional named attributes. + */ +class AttributedItem { + protected: + /** + * Constructs a new instance. + * @param name the item name. + * @param attributes the additional named attributes. + */ + AttributedItem(const string name, const map& attributes) + : m_name(name), m_attributes(attributes) {} + + /** + * Constructs a new instance (without additional attributes). + * @param name the field name. + */ + explicit AttributedItem(const string name) + : m_name(name) {} + + /** + * Destructor. + */ + virtual ~AttributedItem() {} + + + public: + /** + * Remove and return a certain value from a map. + * @param row the map to remove the value from. + * @param key the name of the value to remove. + * @return the named value from the map, or empty if not available. + */ + static const string pluck(map& row, const string key); + + /** + * Dump the @a string optionally embedded in @a TEXT_SEPARATOR to the output. + * @param output the @a ostream to dump to. + * @param str the @a string to dump. + * @param prependFieldSeparator whether to start with a @a FIELD_SEPARATOR. + */ + static void dumpString(ostream& output, const string str, const bool prependFieldSeparator = true); + + /** + * Merge this instance's additional named attributes into the specified attributes. + * @param attributes the additional named attributes to merge in this instance's additional named attributes. + */ + void mergeAttributes(map& attributes) const; + + /** + * Dump the attribute optionally embedded in @a TEXT_SEPARATOR to the output. + * @param output the @a ostream to dump to. + * @param name the name of the attribute to dump. + * @param prependFieldSeparator whether to start with a @a FIELD_SEPARATOR. + */ + void dumpAttribute(ostream& output, const string name, const bool prependFieldSeparator = true) const; + + /** + * Append the attribute value to the output. + * @param output the @a ostringstream to append the formatted value to. + * @param outputFormat the @a OutputFormat options to use. + * @param name the name of the attribute to append. + * @param onlyIfNonEmpty true to append only if the value is not empty. + * @param prefix optional prefix to use (only for non-JSON output). + * @param suffix optional suffix to use (only for non-JSON output). + */ + void appendAttribute(ostringstream& output, OutputFormat outputFormat, const string name, + const bool onlyIfNonEmpty = true, const string prefix = "", const string suffix = "") const; + + /** + * Get the item name. + * @return the item name. + */ + string getName() const { return m_name; } + + /** + * Get a named attribute. + * @param name the name of the attribute. + * @return the named attribute value, or empty. + */ + string getAttribute(const string name) const; + + + protected: + /** the field name. */ + const string m_name; + + /** the additional named attributes. */ + const map m_attributes; +}; + /** * Base class for all kinds of data fields. */ -class DataField { +class DataField : public AttributedItem { public: /** * Constructs a new instance. * @param name the field name. - * @param comment the field comment. + * @param attributes the additional named attributes. */ - DataField(const string name, const string comment) - : m_name(name), m_comment(comment) {} + DataField(const string name, const map& attributes) + : AttributedItem(name, attributes) {} + + /** + * Constructs a new instance (without additional attributes). + * @param name the field name. + */ + explicit DataField(const string name) + : AttributedItem(name) {} /** * Destructor. @@ -113,7 +211,7 @@ class DataField { * Clone this instance. * @return a clone of this instance. */ - virtual DataField* clone() = 0; + virtual const DataField* clone() const = 0; /** * Factory method for creating new instances. @@ -129,18 +227,10 @@ class DataField { * Note: the caller needs to free the created instance. */ static result_t create(vector< map >& rows, string& errorDescription, - DataFieldTemplates* templates, DataField*& returnField, - const bool isWriteMessage, - const bool isTemplate, const bool isBroadcastOrMasterDestination, - const size_t maxFieldLength = MAX_POS); - - /** - * Dump the @a string optionally embedded in @a TEXT_SEPARATOR to the output. - * @param output the @a ostream to dump to. - * @param str the @a string to dump. - * @param prependFieldSeparator whether to start with a @a FIELD_SEPARATOR. - */ - static void dumpString(ostream& output, const string str, const bool prependFieldSeparator = true); + DataFieldTemplates* templates, const DataField*& returnField, + const bool isWriteMessage, + const bool isTemplate, const bool isBroadcastOrMasterDestination, + const size_t maxFieldLength = MAX_POS); /** * Return the name of the specified day. @@ -155,42 +245,33 @@ class DataField { * @param maxLength the maximum length for calculating remainder of input. * @return the length of this field (or contained fields) in bytes. */ - virtual size_t getLength(PartType partType, size_t maxLength = MAX_LEN) = 0; + virtual size_t getLength(PartType partType, size_t maxLength = MAX_LEN) const = 0; /** * Derive a new @a DataField from this field. * @param name the field name, or empty to use this fields 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 attributes the additional named attributes to override. * @param partType the message part in which the field is stored. * @param divisor the extra divisor (negative for reciprocal) 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 vector to which created @a SingleDataField instances shall be added. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) = 0; + virtual result_t derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const = 0; /** * Get the specified field name. * @param fieldIndex the index of the field, or -1 for this. * @return the field name, or the index as string if not unique or not available. */ - virtual string getName(ssize_t fieldIndex = -1) { return m_name; } - - /** - * Get the field comment. - * @return the field comment. - */ - string getComment() const { return m_comment; } + virtual string getName(const ssize_t fieldIndex = -1) const { return m_name; } /** * Dump the field settings to the output. * @param output the @a ostream to dump to. */ - virtual void dump(ostream& output) = 0; + virtual void dump(ostream& output) const = 0; /** * Return whether the field is available. @@ -198,7 +279,7 @@ class DataField { * @param numeric true for a numeric field, false for a string field. * @return true if the field is available. */ - virtual bool hasField(const char* fieldName, bool numeric) = 0; + virtual bool hasField(const char* fieldName, bool numeric) const = 0; /** * Reads the numeric value from the @a SymbolString. @@ -212,8 +293,8 @@ class DataField { * not match or ignored, or due to @a fieldName or @a fieldIndex), * or an error code. */ - virtual result_t read(SymbolString& data, size_t offset, - unsigned int& output, const char* fieldName = NULL, ssize_t fieldIndex = -1) = 0; + virtual result_t read(const SymbolString& data, size_t offset, + unsigned int& output, const char* fieldName = NULL, ssize_t fieldIndex = -1) const = 0; /** * Reads the value from the @a SymbolString. @@ -229,9 +310,9 @@ class DataField { * or @a RESULT_EMPTY if the field was skipped (either ignored or due to @a fieldName or @a fieldIndex), * or an error code. */ - virtual result_t read(SymbolString& data, size_t offset, - ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex = -1, - bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) = 0; + virtual result_t read(const SymbolString& data, size_t offset, + ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex = -1, + bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) const = 0; /** * Writes the value to the master or slave @a SymbolString. @@ -243,15 +324,7 @@ class DataField { * @return @a RESULT_OK on success, or an error code. */ virtual result_t write(istringstream& input, SymbolString& data, - size_t offset, char separator = UI_FIELD_SEPARATOR, size_t* length = NULL) = 0; - - - protected: - /** the field name. */ - const string m_name; - - /** the field comment. */ - const string m_comment; + size_t offset, char separator = UI_FIELD_SEPARATOR, size_t* length = NULL) const = 0; }; @@ -263,18 +336,15 @@ class SingleDataField : public DataField { /** * Constructs a new instance. * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. + * @param attributes the additional named attributes. * @param dataType the data type definition. * @param partType 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 string name, const string comment, - const string unit, DataType* dataType, const PartType partType, - const size_t length) - : DataField(name, comment), - m_unit(unit), m_dataType(dataType), m_partType(partType), - m_length(length) {} + SingleDataField(const string name, const map& attributes, const DataType* dataType, + const PartType partType, const size_t length) + : DataField(name, attributes), + m_partType(partType), m_dataType(dataType), m_length(length) {} /** * Destructor. @@ -282,16 +352,15 @@ class SingleDataField : public DataField { virtual ~SingleDataField() {} // @copydoc - SingleDataField* clone() override; + const SingleDataField* clone() const override; /** * Factory method for creating a new @a SingleDataField instance derived from a base type. - * @param id the ID string (excluding optional length suffix). - * @param length the base type length, or 0 for default, or @a REMAIN_LEN for remainder within same message part. * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. + * @param attributes the additional named attributes. * @param partType the message part in which the field is stored. + * @param dataType the @a DataType instance. + * @param length the base type length, or 0 for default, or @a REMAIN_LEN for remainder within same message part. * @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable). * @param values the value=text assignments. * @param constantValue the constant value as string, or empty. @@ -300,16 +369,9 @@ class SingleDataField : public DataField { * @return @a RESULT_OK on success, or an error code. * Note: the caller needs to free the created instance. */ - static result_t create(const string id, const size_t length, - const string name, const string comment, const string unit, - const PartType partType, int divisor, map values, - const string constantValue, const bool verifyValue, SingleDataField* &returnField); - - /** - * Get the value unit. - * @return the value unit. - */ - string getUnit() const { return m_unit; } + static result_t create(const string name, const map& attributes, const DataType* dataType, + const PartType partType, const size_t length, int divisor, map values, + const string constantValue, const bool verifyValue, SingleDataField* &returnField); /** * Get whether this field is ignored. @@ -324,13 +386,11 @@ class SingleDataField : public DataField { PartType getPartType() const { return m_partType; } // @copydoc - size_t getLength(PartType partType, size_t maxLength = MAX_LEN) override; + size_t getLength(PartType partType, size_t maxLength = MAX_LEN) const override; // @copydoc - result_t derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) override; + result_t derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const override; /** * Get whether this field uses a full byte offset. @@ -338,26 +398,26 @@ class SingleDataField : public DataField { * @return @p true if this field uses a full byte offset, @p false if this field * only consumes a part of a byte and a subsequent field may re-use the same offset. */ - bool hasFullByteOffset(bool after); + bool hasFullByteOffset(bool after) const; // @copydoc - void dump(ostream& output) override; + void dump(ostream& output) const override; // @copydoc - bool hasField(const char* fieldName, bool numeric) override; + bool hasField(const char* fieldName, bool numeric) const override; // @copydoc - result_t read(SymbolString& data, size_t offset, - unsigned int& output, const char* fieldName = NULL, ssize_t fieldIndex = -1) override; + result_t read(const SymbolString& data, size_t offset, + unsigned int& output, const char* fieldName = NULL, ssize_t fieldIndex = -1) const override; // @copydoc - result_t read(SymbolString& data, size_t offset, - ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex = -1, - bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) override; + result_t read(const SymbolString& data, size_t offset, + ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex = -1, + bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) const override; // @copydoc result_t write(istringstream& input, SymbolString& data, - size_t offset, char separator = UI_FIELD_SEPARATOR, size_t* length = NULL) override; + size_t offset, char separator = UI_FIELD_SEPARATOR, size_t* length = NULL) const override; protected: @@ -369,9 +429,9 @@ class SingleDataField : public DataField { * @param outputFormat the @a OutputFormat options to use. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t readSymbols(SymbolString& input, - const size_t offset, - ostringstream& output, OutputFormat outputFormat); + virtual result_t readSymbols(const SymbolString& input, + const size_t offset, + ostringstream& output, OutputFormat outputFormat) const; /** * Internal method for writing the field to a @a SymbolString. @@ -382,18 +442,15 @@ class SingleDataField : public DataField { * @return @a RESULT_OK on success, or an error code. */ virtual result_t writeSymbols(istringstream& input, - const size_t offset, - SymbolString& output, size_t* usedLength); - - /** the value unit. */ - const string m_unit; - - /** the data type definition. */ - DataType* m_dataType; + const size_t offset, + SymbolString& output, size_t* usedLength) const; /** the message part in which the field is stored. */ const PartType m_partType; + /** the data type definition. */ + const DataType* m_dataType; + /** the number of symbols in the message part in which the field is stored. */ const size_t m_length; }; @@ -407,17 +464,15 @@ class ValueListDataField : public SingleDataField { /** * Constructs a new instance. * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. + * @param attributes the additional named attributes. * @param dataType the data type definition. * @param partType 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 string name, const string comment, - const string unit, NumberDataType* dataType, const PartType partType, - const size_t length, const map values) - : SingleDataField(name, comment, unit, dataType, partType, length), + ValueListDataField(const string name, const map& attributes, const DataType* dataType, + const PartType partType, const size_t length, const map values) + : SingleDataField(name, attributes, dataType, partType, length), m_values(values) {} /** @@ -426,31 +481,29 @@ class ValueListDataField : public SingleDataField { virtual ~ValueListDataField() {} // @copydoc - ValueListDataField* clone() override; + const ValueListDataField* clone() const override; // @copydoc - result_t derive(string name, string comment, - string unit, const PartType partType, int divisor, - map values, - vector& fields) override; + result_t derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const override; // @copydoc - void dump(ostream& output) override; + void dump(ostream& output) const override; protected: // @copydoc - result_t readSymbols(SymbolString& input, const size_t offset, - ostringstream& output, OutputFormat outputFormat) override; + result_t readSymbols(const SymbolString& input, const size_t offset, + ostringstream& output, OutputFormat outputFormat) const override; // @copydoc result_t writeSymbols(istringstream& input, const size_t offset, - SymbolString& output, size_t* usedLength) override; + SymbolString& output, size_t* usedLength) const override; private: /** the value=text assignments. */ - map m_values; + const map m_values; }; @@ -462,18 +515,16 @@ class ConstantDataField : public SingleDataField { /** * Constructs a new instance. * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. + * @param attributes the additional named attributes. * @param dataType the data type definition. * @param partType 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 value the constant value. * @param verify whether to verify the read value against the constant value. */ - ConstantDataField(const string name, const string comment, - const string unit, DataType* dataType, const PartType partType, - const size_t length, const string value, const bool verify) - : SingleDataField(name, comment, unit, dataType, partType, length), + ConstantDataField(const string name, const map& attributes, const DataType* dataType, + const PartType partType, const size_t length, const string value, const bool verify) + : SingleDataField(name, attributes, dataType, partType, length), m_value(value), m_verify(verify) {} /** @@ -482,26 +533,24 @@ class ConstantDataField : public SingleDataField { virtual ~ConstantDataField() {} // @copydoc - ConstantDataField* clone() override; + const ConstantDataField* clone() const override; // @copydoc - result_t derive(string name, string comment, - string unit, const PartType partType, int divisor, - map values, - vector& fields) override; + result_t derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const override; // @copydoc - void dump(ostream& output) override; + void dump(ostream& output) const override; protected: // @copydoc - result_t readSymbols(SymbolString& input, const size_t offset, - ostringstream& output, OutputFormat outputFormat) override; + result_t readSymbols(const SymbolString& input, const size_t offset, + ostringstream& output, OutputFormat outputFormat) const override; // @copydoc result_t writeSymbols(istringstream& input, const size_t offset, - SymbolString& output, size_t* usedLength) override; + SymbolString& output, size_t* usedLength) const override; private: @@ -533,16 +582,13 @@ class DataFieldSet : public DataField { /** * Constructs a new instance. * @param name the field name. - * @param comment the field comment. * @param fields the @a vector of @a SingleDataField instances part of this set. */ - DataFieldSet(const string name, const string comment, - const vector fields) - : DataField(name, comment), m_fields(fields) { + DataFieldSet(const string name, const vector fields) + : DataField(name), m_fields(fields) { bool uniqueNames = true; map names; - for (vector::const_iterator it = fields.begin(); it != fields.end(); it++) { - SingleDataField* field = *it; + for (auto field : fields) { if (field->isIgnored()) { continue; } @@ -562,31 +608,29 @@ class DataFieldSet : public DataField { virtual ~DataFieldSet(); // @copydoc - DataFieldSet* clone() override; + const DataFieldSet* clone() const override; // @copydoc - size_t getLength(PartType partType, size_t maxLength = MAX_LEN) override; + size_t getLength(PartType partType, size_t maxLength = MAX_LEN) const override; // @copydoc - string getName(ssize_t fieldIndex = -1) override; + string getName(const ssize_t fieldIndex = -1) const override; // @copydoc - result_t derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) override; + result_t derive(const string name, map attributes, const PartType partType, + int divisor, map values, vector& fields) const override; /** * Returns the @a SingleDataField at the specified index. * @param index the index of the @a SingleDataField to return. * @return the @a SingleDataField at the specified index, or NULL. */ - SingleDataField* operator[](const size_t index) { + /*SingleDataField* operator[](const size_t index) { if (index >= m_fields.size()) { return NULL; } return m_fields[index]; - } + }*/ /** * Returns the @a SingleDataField at the specified index. @@ -607,23 +651,23 @@ class DataFieldSet : public DataField { size_t size() const { return m_fields.size(); } // @copydoc - bool hasField(const char* fieldName, bool numeric) override; + bool hasField(const char* fieldName, bool numeric) const override; // @copydoc - void dump(ostream& output) override; + void dump(ostream& output) const override; // @copydoc - result_t read(SymbolString& data, size_t offset, - unsigned int& output, const char* fieldName = NULL, ssize_t fieldIndex = -1) override; + result_t read(const SymbolString& data, size_t offset, + unsigned int& output, const char* fieldName = NULL, ssize_t fieldIndex = -1) const override; // @copydoc - result_t read(SymbolString& data, size_t offset, - ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex = -1, - bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) override; + result_t read(const SymbolString& data, size_t offset, + ostringstream& output, OutputFormat outputFormat, ssize_t outputIndex = -1, + bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) const override; // @copydoc result_t write(istringstream& input, SymbolString& data, - size_t offset, char separator = UI_FIELD_SEPARATOR, size_t* length = NULL) override; + size_t offset, char separator = UI_FIELD_SEPARATOR, size_t* length = NULL) const override; private: @@ -631,7 +675,7 @@ class DataFieldSet : public DataField { static DataFieldSet* s_identFields; /** the @a vector of @a SingleDataField instances part of this set. */ - vector m_fields; + const vector m_fields; /** whether all fields have a unique name. */ bool m_uniqueNames; @@ -674,14 +718,14 @@ class DataFieldTemplates : public MappedFileReader { * @return @a RESULT_OK on success, or an error code. * Note: the caller may not free the added instance on success. */ - result_t add(DataField* field, string name = "", bool replace = false); + result_t add(const DataField* field, string name = "", bool replace = false); // @copydoc - result_t getFieldMap(vector& row, string& errorDescription) override; + result_t getFieldMap(vector& row, string& errorDescription) const override; // @copydoc result_t addFromFile(map& row, vector< map >& subRows, - string& errorDescription, const string filename, unsigned int lineNo) override; + string& errorDescription, const string filename, unsigned int lineNo) override; /** * Gets the template @a DataField instance with the specified name. @@ -689,12 +733,12 @@ class DataFieldTemplates : public MappedFileReader { * @return the template @a DataField instance, or NULL. * Note: the caller may not free the returned instance. */ - DataField* get(string name); + const DataField* get(string name) const; private: /** the known template @a DataField instances by name. */ - map m_fieldsByName; + map m_fieldsByName; }; } // namespace ebusd diff --git a/src/lib/ebus/datatype.cpp b/src/lib/ebus/datatype.cpp index f5891230..63e82967 100644 --- a/src/lib/ebus/datatype.cpp +++ b/src/lib/ebus/datatype.cpp @@ -58,14 +58,14 @@ bool DataType::dump(ostream& output, const size_t length, const bool appendSepar } -result_t StringDataType::readRawValue(SymbolString& input, const size_t offset, - const size_t length, unsigned int& value) { +result_t StringDataType::readRawValue(const SymbolString& input, const size_t offset, + const size_t length, unsigned int& value) const { return RESULT_EMPTY; } -result_t StringDataType::readSymbols(SymbolString& input, +result_t StringDataType::readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { size_t start = 0, count = length; int incr = 1; symbol_t symbol; @@ -116,7 +116,7 @@ result_t StringDataType::readSymbols(SymbolString& input, result_t StringDataType::writeSymbols(istringstream& input, size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) { + SymbolString& output, size_t* usedLength) const { size_t start = 0, count = length; bool remainder = count == REMAIN_LEN && hasFlag(ADJ); int incr = 1; @@ -196,14 +196,14 @@ result_t StringDataType::writeSymbols(istringstream& input, } -result_t DateTimeDataType::readRawValue(SymbolString& input, const size_t offset, - const size_t length, unsigned int& value) { +result_t DateTimeDataType::readRawValue(const SymbolString& input, const size_t offset, + const size_t length, unsigned int& value) const { return RESULT_EMPTY; } -result_t DateTimeDataType::readSymbols(SymbolString& input, +result_t DateTimeDataType::readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { size_t start = 0, count = length; int incr = 1; symbol_t symbol, last = 0, hour = 0; @@ -335,7 +335,7 @@ result_t DateTimeDataType::readSymbols(SymbolString& input, result_t DateTimeDataType::writeSymbols(istringstream& input, size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) { + SymbolString& output, size_t* usedLength) const { size_t start = 0, count = length; bool remainder = count == REMAIN_LEN && hasFlag(ADJ); int incr = 1; @@ -527,7 +527,7 @@ bool NumberDataType::dump(ostream& output, size_t length, const bool appendSepar return false; } -result_t NumberDataType::derive(int divisor, size_t bitCount, NumberDataType* &derived) { +result_t NumberDataType::derive(int divisor, size_t bitCount, const NumberDataType* &derived) const { if (divisor == 0) { divisor = 1; } @@ -570,19 +570,18 @@ result_t NumberDataType::derive(int divisor, size_t bitCount, NumberDataType* &d } if (m_bitCount < 8) { derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement, - m_firstBit, divisor); + m_firstBit, divisor, m_baseType ? m_baseType : this); } else { derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement, - m_minValue, m_maxValue, divisor); + m_minValue, m_maxValue, divisor, m_baseType ? m_baseType : this); } - derived->m_baseType = m_baseType ? m_baseType : this; DataTypeList::getInstance()->addCleanup(derived); return RESULT_OK; } -result_t NumberDataType::readRawValue(SymbolString& input, +result_t NumberDataType::readRawValue(const SymbolString& input, size_t offset, const size_t length, - unsigned int& value) { + unsigned int& value) const { size_t start = 0, count = length; int incr = 1; symbol_t symbol; @@ -629,9 +628,9 @@ result_t NumberDataType::readRawValue(SymbolString& input, return RESULT_OK; } -result_t NumberDataType::readSymbols(SymbolString& input, +result_t NumberDataType::readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) { + ostringstream& output, OutputFormat outputFormat) const { unsigned int value = 0; int signedValue; @@ -743,7 +742,7 @@ result_t NumberDataType::readSymbols(SymbolString& input, result_t NumberDataType::writeRawValue(unsigned int value, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) { + SymbolString& output, size_t* usedLength) const { size_t start = 0, count = length; int incr = 1; symbol_t symbol; @@ -789,7 +788,7 @@ result_t NumberDataType::writeRawValue(unsigned int value, result_t NumberDataType::writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) { + SymbolString& output, size_t* usedLength) const { unsigned int value; const char* str = input.str().c_str(); @@ -906,8 +905,8 @@ bool DataTypeList::s_contrib_initialized = libebus_contrib_register(); DataTypeList::DataTypeList() { add(new StringDataType("STR", MAX_LEN*8, ADJ, ' ')); // >= 1 byte character string filled up with space // unsigned decimal in BCD, 0000 - 9999 (fixed length) - add(new NumberDataType("PIN", 16, FIX|BCD|REV, 0xffff, 0, 0x9999, 1)); - add(new NumberDataType("UCH", 8, 0, 0xff, 0, 0xfe, 1)); // unsigned integer, 0 - 254 + add(new NumberDataType("PIN", 16, FIX|BCD|REV, 0xffff, 0, 0x9999, 1, NULL)); + add(new NumberDataType("UCH", 8, 0, 0xff, 0, 0xfe, 1, NULL)); // unsigned integer, 0 - 254 add(new StringDataType("IGN", MAX_LEN*8, IGN|ADJ, 0)); // >= 1 byte ignored data // >= 1 byte character string filled up with 0x00 (null terminated string) add(new StringDataType("NTS", MAX_LEN*8, ADJ, 0)); @@ -945,56 +944,56 @@ DataTypeList::DataTypeList() { add(new DateTimeDataType("TTH", 6, 0, 0, false, true, 30)); // truncated time (only multiple of 15 minutes), 00:00 - 24:00 (minutes div 15 + hour * 4 as integer) add(new DateTimeDataType("TTQ", 7, 0, 0, false, true, 15)); - add(new NumberDataType("BDY", 8, DAY, 0x07, 0, 6, 1)); // weekday, "Mon" - "Sun" (0x00 - 0x06) [eBUS type] - add(new NumberDataType("HDY", 8, DAY, 0x00, 1, 7, 1)); // weekday, "Mon" - "Sun" (0x01 - 0x07) [Vaillant type] - add(new NumberDataType("BCD", 8, BCD, 0xff, 0, 99, 1)); // unsigned decimal in BCD, 0 - 99 - add(new NumberDataType("BCD", 16, BCD, 0xffff, 0, 9999, 1)); // unsigned decimal in BCD, 0 - 9999 - add(new NumberDataType("BCD", 24, BCD, 0xffffff, 0, 999999, 1)); // unsigned decimal in BCD, 0 - 999999 - add(new NumberDataType("BCD", 32, BCD, 0xffffffff, 0, 99999999, 1)); // unsigned decimal in BCD, 0 - 99999999 - add(new NumberDataType("HCD", 32, HCD|BCD|REQ, 0, 0, 99999999, 1)); // unsigned decimal in HCD, 0 - 99999999 - add(new NumberDataType("HCD", 8, HCD|BCD|REQ, 0, 0, 99, 1)); // unsigned decimal in HCD, 0 - 99 - add(new NumberDataType("HCD", 16, HCD|BCD|REQ, 0, 0, 9999, 1)); // unsigned decimal in HCD, 0 - 9999 - add(new NumberDataType("HCD", 24, HCD|BCD|REQ, 0, 0, 999999, 1)); // unsigned decimal in HCD, 0 - 999999 - add(new NumberDataType("SCH", 8, SIG, 0x80, 0x81, 0x7f, 1)); // signed integer, -127 - +127 - add(new NumberDataType("D1B", 8, SIG, 0x80, 0x81, 0x7f, 1)); // signed integer, -127 - +127 + add(new NumberDataType("BDY", 8, DAY, 0x07, 0, 6, 1, NULL)); // weekday, "Mon" - "Sun" (0x00 - 0x06) [eBUS type] + add(new NumberDataType("HDY", 8, DAY, 0x00, 1, 7, 1, NULL)); // weekday, "Mon" - "Sun" (0x01 - 0x07) [Vaillant type] + add(new NumberDataType("BCD", 8, BCD, 0xff, 0, 99, 1, NULL)); // unsigned decimal in BCD, 0 - 99 + add(new NumberDataType("BCD", 16, BCD, 0xffff, 0, 9999, 1, NULL)); // unsigned decimal in BCD, 0 - 9999 + add(new NumberDataType("BCD", 24, BCD, 0xffffff, 0, 999999, 1, NULL)); // unsigned decimal in BCD, 0 - 999999 + add(new NumberDataType("BCD", 32, BCD, 0xffffffff, 0, 99999999, 1, NULL)); // unsigned decimal in BCD, 0 - 99999999 + add(new NumberDataType("HCD", 32, HCD|BCD|REQ, 0, 0, 99999999, 1, NULL)); // unsigned decimal in HCD, 0 - 99999999 + add(new NumberDataType("HCD", 8, HCD|BCD|REQ, 0, 0, 99, 1, NULL)); // unsigned decimal in HCD, 0 - 99 + add(new NumberDataType("HCD", 16, HCD|BCD|REQ, 0, 0, 9999, 1, NULL)); // unsigned decimal in HCD, 0 - 9999 + add(new NumberDataType("HCD", 24, HCD|BCD|REQ, 0, 0, 999999, 1, NULL)); // unsigned decimal in HCD, 0 - 999999 + add(new NumberDataType("SCH", 8, SIG, 0x80, 0x81, 0x7f, 1, NULL)); // signed integer, -127 - +127 + add(new NumberDataType("D1B", 8, SIG, 0x80, 0x81, 0x7f, 1, NULL)); // signed integer, -127 - +127 // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff) - add(new NumberDataType("D1C", 8, 0, 0xff, 0x00, 0xc8, 2)); + add(new NumberDataType("D1C", 8, 0, 0xff, 0x00, 0xc8, 2, NULL)); // signed number (fraction 1/256), -127.99 - +127.99 - add(new NumberDataType("D2B", 16, SIG, 0x8000, 0x8001, 0x7fff, 256)); + add(new NumberDataType("D2B", 16, SIG, 0x8000, 0x8001, 0x7fff, 256, NULL)); // signed number (fraction 1/16), -2047.9 - +2047.9 - add(new NumberDataType("D2C", 16, SIG, 0x8000, 0x8001, 0x7fff, 16)); + add(new NumberDataType("D2C", 16, SIG, 0x8000, 0x8001, 0x7fff, 16, NULL)); // signed number (fraction 1/1000), -32.767 - +32.767, little endian - add(new NumberDataType("FLT", 16, SIG, 0x8000, 0x8001, 0x7fff, 1000)); + add(new NumberDataType("FLT", 16, SIG, 0x8000, 0x8001, 0x7fff, 1000, NULL)); // signed number (fraction 1/1000), -32.767 - +32.767, big endian - add(new NumberDataType("FLR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1000)); + add(new NumberDataType("FLR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1000, NULL)); // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), little endian - add(new NumberDataType("EXP", 32, SIG|EXP, 0x7f800000, 0x00000000, 0xffffffff, 1)); + add(new NumberDataType("EXP", 32, SIG|EXP, 0x7f800000, 0x00000000, 0xffffffff, 1, NULL)); // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), big endian - add(new NumberDataType("EXR", 32, SIG|EXP|REV, 0x7f800000, 0x00000000, 0xffffffff, 1)); + add(new NumberDataType("EXR", 32, SIG|EXP|REV, 0x7f800000, 0x00000000, 0xffffffff, 1, NULL)); // unsigned integer, 0 - 65534, little endian - add(new NumberDataType("UIN", 16, 0, 0xffff, 0, 0xfffe, 1)); + add(new NumberDataType("UIN", 16, 0, 0xffff, 0, 0xfffe, 1, NULL)); // unsigned integer, 0 - 65534, big endian - add(new NumberDataType("UIR", 16, REV, 0xffff, 0, 0xfffe, 1)); + add(new NumberDataType("UIR", 16, REV, 0xffff, 0, 0xfffe, 1, NULL)); // signed integer, -32767 - +32767, little endian - add(new NumberDataType("SIN", 16, SIG, 0x8000, 0x8001, 0x7fff, 1)); + add(new NumberDataType("SIN", 16, SIG, 0x8000, 0x8001, 0x7fff, 1, NULL)); // signed integer, -32767 - +32767, big endian - add(new NumberDataType("SIR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1)); + add(new NumberDataType("SIR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1, NULL)); // unsigned 3 bytes int, 0 - 16777214, little endian - add(new NumberDataType("U3N", 24, 0, 0xffffff, 0, 0xfffffe, 1)); + add(new NumberDataType("U3N", 24, 0, 0xffffff, 0, 0xfffffe, 1, NULL)); // unsigned 3 bytes int, 0 - 16777214, big endian - add(new NumberDataType("U3R", 24, REV, 0xffffff, 0, 0xfffffe, 1)); + add(new NumberDataType("U3R", 24, REV, 0xffffff, 0, 0xfffffe, 1, NULL)); // signed 3 bytes int, -8388607 - +8388607, little endian - add(new NumberDataType("S3N", 24, SIG, 0x800000, 0x800001, 0xffffff, 1)); + add(new NumberDataType("S3N", 24, SIG, 0x800000, 0x800001, 0xffffff, 1, NULL)); // signed 3 bytes int, -8388607 - +8388607, big endian - add(new NumberDataType("S3R", 24, SIG|REV, 0x800000, 0x800001, 0xffffff, 1)); + add(new NumberDataType("S3R", 24, SIG|REV, 0x800000, 0x800001, 0xffffff, 1, NULL)); // unsigned integer, 0 - 4294967294, little endian - add(new NumberDataType("ULG", 32, 0, 0xffffffff, 0, 0xfffffffe, 1)); + add(new NumberDataType("ULG", 32, 0, 0xffffffff, 0, 0xfffffffe, 1, NULL)); // unsigned integer, 0 - 4294967294, big endian - add(new NumberDataType("ULR", 32, REV, 0xffffffff, 0, 0xfffffffe, 1)); + add(new NumberDataType("ULR", 32, REV, 0xffffffff, 0, 0xfffffffe, 1, NULL)); // signed integer, -2147483647 - +2147483647, little endian - add(new NumberDataType("SLG", 32, SIG, 0x80000000, 0x80000001, 0xffffffff, 1)); + add(new NumberDataType("SLG", 32, SIG, 0x80000000, 0x80000001, 0xffffffff, 1, NULL)); // signed integer, -2147483647 - +2147483647, big endian - add(new NumberDataType("SLR", 32, SIG|REV, 0x80000000, 0x80000001, 0xffffffff, 1)); + add(new NumberDataType("SLR", 32, SIG|REV, 0x80000000, 0x80000001, 0xffffffff, 1, NULL)); add(new NumberDataType("BI0", 7, ADJ|REQ, 0, 0, 1)); // bit 0 (up to 7 bits until bit 6) add(new NumberDataType("BI1", 7, ADJ|REQ, 0, 1, 1)); // bit 1 (up to 7 bits until bit 7) add(new NumberDataType("BI2", 6, ADJ|REQ, 0, 2, 1)); // bit 2 (up to 6 bits until bit 7) @@ -1010,21 +1009,20 @@ DataTypeList* DataTypeList::getInstance() { } void DataTypeList::clear() { - for (list::iterator it = m_cleanupTypes.begin(); it != m_cleanupTypes.end(); it++) { - delete *it; + for (auto& it : m_cleanupTypes) { + delete it; } m_cleanupTypes.clear(); m_typesByIdLength.clear(); m_typesById.clear(); } -result_t DataTypeList::add(DataType* dataType) { +result_t DataTypeList::add(const DataType* dataType) { if (!dataType->isAdjustableLength()) { ostringstream str; size_t bitCount = dataType->getBitCount(); str << dataType->getId() << LENGTH_SEPARATOR << static_cast(bitCount >= 8?bitCount/8:bitCount); - map::iterator it = m_typesByIdLength.find(str.str()); - if (it != m_typesByIdLength.end()) { + if (m_typesByIdLength.find(str.str()) != m_typesByIdLength.end()) { return RESULT_ERR_DUPLICATE_NAME; // duplicate key } m_typesByIdLength[str.str()] = dataType; @@ -1040,20 +1038,23 @@ result_t DataTypeList::add(DataType* dataType) { return RESULT_OK; } -DataType* DataTypeList::get(const string id, const size_t length) { - DataType* dataType = NULL; +const DataType* DataTypeList::get(const string id, const size_t length) const { if (length > 0) { ostringstream str; str << id << LENGTH_SEPARATOR << static_cast(length); - dataType = m_typesByIdLength[str.str()]; - } - if (!dataType) { - dataType = m_typesById[id]; - if (dataType && length > 0 && !dataType->isAdjustableLength()) { - return NULL; + auto it = m_typesByIdLength.find(str.str()); + if (it != m_typesByIdLength.end()) { + return it->second; } } - return dataType; + auto it = m_typesById.find(id); + if (it == m_typesById.end()) { + return NULL; + } + if (length > 0 && !it->second->isAdjustableLength()) { + return NULL; + } + return it->second; } } // namespace ebusd diff --git a/src/lib/ebus/datatype.h b/src/lib/ebus/datatype.h index 12478da5..019a2466 100644 --- a/src/lib/ebus/datatype.h +++ b/src/lib/ebus/datatype.h @@ -100,6 +100,9 @@ typedef int OutputFormat; /** bit flag for @a OutputFormat: short format (only name and value, no indentation). */ #define OF_SHORT 0x20 +/** bit flag for @a OutputFormat: include all attributes. */ +#define OF_ALL_ATTRS 0x40 + /** the message part in which a data field is stored. */ enum PartType { pt_any, //!< stored in any data (master or slave) @@ -225,9 +228,9 @@ class DataType { * @param value the variable in which to store the numeric raw value. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t readRawValue(SymbolString& input, + virtual result_t readRawValue(const SymbolString& input, const size_t offset, const size_t length, - unsigned int& value) = 0; + unsigned int& value) const = 0; /** * Internal method for reading the field from a @a SymbolString. @@ -238,9 +241,9 @@ class DataType { * @param outputFormat the @a OutputFormat options to use. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t readSymbols(SymbolString& input, + virtual result_t readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) = 0; + ostringstream& output, OutputFormat outputFormat) const = 0; /** * Internal method for writing the field to a @a SymbolString. @@ -253,7 +256,7 @@ class DataType { */ virtual result_t writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) = 0; + SymbolString& output, size_t* usedLength) const = 0; protected: @@ -295,19 +298,19 @@ class StringDataType : public DataType { virtual ~StringDataType() {} // @copydoc - result_t readRawValue(SymbolString& input, + result_t readRawValue(const SymbolString& input, const size_t offset, const size_t length, - unsigned int& value) override; + unsigned int& value) const override; // @copydoc - result_t readSymbols(SymbolString& input, + result_t readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) override; + ostringstream& output, OutputFormat outputFormat) const override; // @copydoc result_t writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) override; + SymbolString& output, size_t* usedLength) const override; private: @@ -357,19 +360,19 @@ class DateTimeDataType : public DataType { int16_t getResolution() const { return m_resolution; } // @copydoc - result_t readRawValue(SymbolString& input, + result_t readRawValue(const SymbolString& input, const size_t offset, const size_t length, - unsigned int& value) override; + unsigned int& value) const override; // @copydoc - result_t readSymbols(SymbolString& input, + result_t readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) override; + ostringstream& output, OutputFormat outputFormat) const override; // @copydoc result_t writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) override; + SymbolString& output, size_t* usedLength) const override; private: @@ -398,11 +401,13 @@ class NumberDataType : public DataType { * @param minValue the minimum raw value. * @param maxValue the maximum raw value. * @param divisor the divisor (negative for reciprocal). + * @param baseType the base @a NumberDataType for derived instances, or NULL. */ NumberDataType(const string id, const size_t bitCount, const uint16_t flags, const unsigned int replacement, - const unsigned int minValue, const unsigned int maxValue, const int divisor) + const unsigned int minValue, const unsigned int maxValue, const int divisor, + const NumberDataType* baseType) : DataType(id, bitCount, flags|NUM, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor), - m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(NULL) {} + m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(baseType) {} /** * Constructs a new instance for less than 8 bits. @@ -412,11 +417,12 @@ class NumberDataType : public DataType { * @param replacement the replacement value (no replacement if zero). * @param firstBit the offset to the first bit. * @param divisor the divisor (negative for reciprocal). + * @param baseType the base @a NumberDataType for derived instances, or NULL. */ NumberDataType(const string id, const size_t bitCount, const uint16_t flags, const unsigned int replacement, - const int16_t firstBit, const int divisor) + const int16_t firstBit, const int divisor, const NumberDataType* baseType = NULL) : DataType(id, bitCount, flags|NUM, replacement), m_minValue(0), m_maxValue((1 << bitCount)-1), m_divisor(divisor), - m_precision(0), m_firstBit(firstBit), m_baseType(NULL) {} + m_precision(0), m_firstBit(firstBit), m_baseType(baseType) {} /** * Destructor. @@ -444,7 +450,7 @@ class NumberDataType : public DataType { * not necessary. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t derive(int divisor, size_t bitCount, NumberDataType* &derived); + virtual result_t derive(int divisor, size_t bitCount, const NumberDataType* &derived) const; /** * @return the minimum raw value. @@ -472,14 +478,14 @@ class NumberDataType : public DataType { int16_t getFirstBit() const { return m_firstBit; } // @copydoc - result_t readRawValue(SymbolString& input, + result_t readRawValue(const SymbolString& input, const size_t offset, const size_t length, - unsigned int& value) override; + unsigned int& value) const override; // @copydoc - result_t readSymbols(SymbolString& input, + result_t readSymbols(const SymbolString& input, const size_t offset, const size_t length, - ostringstream& output, OutputFormat outputFormat) override; + ostringstream& output, OutputFormat outputFormat) const override; /** * Internal method for writing the numeric raw value to a @a SymbolString. @@ -493,12 +499,12 @@ class NumberDataType : public DataType { */ result_t writeRawValue(unsigned int value, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength = NULL); + SymbolString& output, size_t* usedLength = NULL) const; // @copydoc result_t writeSymbols(istringstream& input, const size_t offset, const size_t length, - SymbolString& output, size_t* usedLength) override; + SymbolString& output, size_t* usedLength) const override; private: @@ -518,7 +524,7 @@ class NumberDataType : public DataType { const int16_t m_firstBit; /** the base @a NumberDataType for derived instances. */ - NumberDataType* m_baseType; + const NumberDataType* m_baseType; }; @@ -556,13 +562,13 @@ class DataTypeList { * @return @a RESULT_OK on success, or an error code. * Note: the caller may not free the added instance on success. */ - result_t add(DataType* dataType); + result_t add(const DataType* dataType); /** * Adds a @a DataType instance for later cleanup. * @param dataType the @a DataType instance to add. */ - void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); } + void addCleanup(const DataType* dataType) { m_cleanupTypes.push_back(dataType); } /** * Gets the @a DataType instance with the specified ID. @@ -571,30 +577,30 @@ class DataTypeList { * @return the @a DataType instance, or NULL if not available. * Note: the caller may not free the instance. */ - DataType* get(const string id, const size_t length = 0); + const DataType* get(const string id, const size_t length = 0) const; /** * Returns an iterator pointing to the first ID/@a DataType pair. * @return an iterator pointing to the first ID/@a DataType pair. */ - map::const_iterator begin() const { return m_typesById.begin(); } + map::const_iterator begin() const { return m_typesById.begin(); } /** * Returns an iterator pointing one past the last ID/@a DataType pair. * @return an iterator pointing one past the last ID/@a DataType pair. */ - map::const_iterator end() const { return m_typesById.end(); } + map::const_iterator end() const { return m_typesById.end(); } private: /** the known @a DataType instances by ID only. */ - map m_typesById; + map m_typesById; /** the known @a DataType instances by ID and length (i.e. "ID:BITS"). * Note: adjustable length types are stored by ID only. */ - map m_typesByIdLength; + map m_typesByIdLength; /** the @a DataType instances to cleanup. */ - list m_cleanupTypes; + list m_cleanupTypes; /** the singleton instance. */ static DataTypeList s_instance; diff --git a/src/lib/ebus/filereader.cpp b/src/lib/ebus/filereader.cpp index e5e47275..36feb9a3 100644 --- a/src/lib/ebus/filereader.cpp +++ b/src/lib/ebus/filereader.cpp @@ -75,11 +75,14 @@ result_t FileReader::readFromFile(const string filename, string& errorDescriptio result_t FileReader::readLineFromStream(istream& stream, string& errorDescription, const string filename, unsigned int& lineNo, vector& row, bool verbose, size_t* hash, size_t* size) { + result_t result; if (!splitFields(stream, row, lineNo, hash, size)) { - return RESULT_ERR_EOF; + errorDescription = "blank line"; + result = RESULT_ERR_EOF; + } else { + errorDescription = ""; + result = addFromFile(row, errorDescription, filename, lineNo); } - errorDescription = ""; - result_t result = addFromFile(row, errorDescription, filename, lineNo); if (result != RESULT_OK) { if (!verbose) { ostringstream error; @@ -134,6 +137,7 @@ bool FileReader::splitFields(istream& ifs, vector& row, unsigned int& li *size += length + 1; // normalized with trailing endl } if (hash) { + // TODO ensure 32 bit machine produces same result *hash ^= (hashFunction(line) << 1) ^ (length << (7 * (lineNo % 5))); } if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) { diff --git a/src/lib/ebus/filereader.h b/src/lib/ebus/filereader.h index 08e681e3..44bb64d0 100644 --- a/src/lib/ebus/filereader.h +++ b/src/lib/ebus/filereader.h @@ -185,7 +185,7 @@ class MappedFileReader : public FileReader { * @return true if the minimum parts were extracted, false otherwise. */ virtual bool extractDefaultsFromFilename(string filename, map& defaults, - symbol_t* destAddress = NULL, unsigned int* software = NULL, unsigned int* hardware = NULL) { + symbol_t* destAddress = NULL, unsigned int* software = NULL, unsigned int* hardware = NULL) const { return false; } @@ -199,7 +199,7 @@ class MappedFileReader : public FileReader { * @param errorDescription a string in which to store the error description in case of error. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t getFieldMap(vector& row, string& errorDescription) = 0; + virtual result_t getFieldMap(vector& row, string& errorDescription) const = 0; /** * Add a default row that was read from a file. @@ -231,14 +231,14 @@ class MappedFileReader : public FileReader { /** * @return a reference to all previously extracted default values by type and field name. */ - virtual map >& getDefaults() { + map >& getDefaults() { return m_lastDefaults; } /** * @return a reference to all previously extracted sub default values by type and field name. */ - virtual map > >& getSubDefaults() { + map > >& getSubDefaults() { return m_lastSubDefaults; } diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 2ab0da24..724a4849 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -161,20 +161,20 @@ string getMessageFieldName(size_t fieldId, bool withDataFields = true) { } Message::Message(const string circuit, const string level, const string name, - const bool isWrite, const bool isPassive, const string comment, + const bool isWrite, const bool isPassive, const map& attributes, const symbol_t srcAddress, const symbol_t dstAddress, const vector id, - DataField* data, const bool deleteData, + const DataField* data, const bool deleteData, const size_t pollPriority, Condition* condition) - : m_circuit(circuit), m_level(level), m_name(name), m_isWrite(isWrite), - m_isPassive(isPassive), m_comment(comment), + : AttributedItem(name, attributes), m_circuit(circuit), m_level(level), m_isWrite(isWrite), + m_isPassive(isPassive), m_srcAddress(srcAddress), m_dstAddress(dstAddress), m_id(id), m_data(data), m_deleteData(deleteData), m_pollPriority(pollPriority), m_usedByCondition(false), m_isScanMessage(false), m_condition(condition), - m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { - m_key = createKey(id, isWrite, isPassive, srcAddress, dstAddress); + m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0), + m_key(createKey(id, isWrite, isPassive, srcAddress, dstAddress)) { if (circuit == "scan") { setScanMessage(); m_pollPriority = 0; @@ -183,22 +183,15 @@ Message::Message(const string circuit, const string level, const string name, Message::Message(const string circuit, const string level, const string name, const symbol_t pb, const symbol_t sb, - const bool broadcast, DataField* data, const bool deleteData) - : m_circuit(circuit), m_level(level), m_name(name), m_isWrite(broadcast), - m_isPassive(false), m_comment(), + const bool broadcast, const DataField* data, const bool deleteData) + : AttributedItem(name), m_circuit(circuit), m_level(level), m_isWrite(broadcast), + m_isPassive(false), m_srcAddress(SYN), m_dstAddress(broadcast ? BROADCAST : SYN), - m_data(data), m_deleteData(deleteData), + m_id({pb, sb}), m_data(data), m_deleteData(deleteData), m_pollPriority(0), m_usedByCondition(false), m_isScanMessage(true), m_condition(NULL), - m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { - m_id.push_back(pb); - m_id.push_back(sb); - uint64_t key = 0; - key |= (broadcast ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active - key |= (uint64_t)(broadcast ? BROADCAST : SYN) << (8 * 6); - key |= (uint64_t)pb << (8 * 5); - key |= (uint64_t)sb << (8 * 4); - m_key = key; + m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0), + m_key(createKey(pb, sb, broadcast)) { } @@ -214,7 +207,7 @@ Message::Message(const string circuit, const string level, const string name, * empty and @p replaceStar is @p true. * @return the default if available and value is empty, or the value. */ -string getDefault(const string value, map& defaults, const string fieldName, +string getDefault(const string value, const map& defaults, const string fieldName, bool replaceStar = false, bool required = false) { if (defaults.empty()) { return value; @@ -222,7 +215,8 @@ string getDefault(const string value, map& defaults, const strin if (value.length() == 0 && replaceStar && required) { return value; } - string defaultStr = defaults[fieldName]; + auto it = defaults.find(fieldName); + const string defaultStr = it == defaults.end() ? "" : it->second; if (!replaceStar || defaultStr.empty()) { return value.length() > 0 ? value : defaultStr; } @@ -279,6 +273,15 @@ uint64_t Message::createKey(MasterSymbolString& master, size_t maxIdLength, bool return key; } +uint64_t Message::createKey(const symbol_t pb, const symbol_t sb, const bool broadcast) { + uint64_t key = 0; + key |= (broadcast ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active + key |= (uint64_t)(broadcast ? BROADCAST : SYN) << (8 * 6); + key |= (uint64_t)pb << (8 * 5); + key |= (uint64_t)sb << (8 * 4); + return key; +} + result_t Message::parseId(string input, vector& id) { istringstream in(input); while (!in.eof()) { @@ -314,8 +317,9 @@ result_t Message::create(map row, vector< map > bool isWrite = false, isPassive = false; string defaultName; size_t pollPriority = 0; - string typeStr = row["type"]; + string typeStr = pluck(row, "type"); if (typeStr.empty()) { + errorDescription = "empty type"; return RESULT_ERR_EOF; } if (typeStr.empty()) { // default: active read @@ -341,36 +345,39 @@ result_t Message::create(map row, vector< map > } map& defaults = rowDefaults[defaultName]; - string circuit = getDefault(row["circuit"], defaults, "circuit", true); // [circuit[#level]] - string level = getDefault(row["level"], defaults, "level", true); + string circuit = getDefault(pluck(row, "circuit"), defaults, "circuit", true); // [circuit[#level]] + string level = getDefault(pluck(row, "level"), defaults, "level", true); size_t pos = circuit.find('#'); // TODO remove some day if (pos != string::npos) { level = circuit.substr(pos+1); circuit.resize(pos); } - string name = getDefault(row["name"], defaults, "name", true, true); // name + string name = getDefault(pluck(row, "name"), defaults, "name", true, true); // name if (name.empty()) { - errorDescription = "name in "+MappedFileReader::combineRow(row); + errorDescription = "name"; return RESULT_ERR_MISSING_ARG; // empty name } - string comment = getDefault(row["comment"], defaults, "comment", true); // [comment] - string str = getDefault(row["qq"], defaults, "qq"); // [QQ[;QQ]*] + string comment = getDefault(pluck(row, "comment"), defaults, "comment", true); // [comment] + if (!comment.empty()) { + row["comment"] = comment; + } + string str = getDefault(pluck(row, "qq"), defaults, "qq"); // [QQ[;QQ]*] symbol_t srcAddress; if (str.empty()) { srcAddress = SYN; // no specific source } else { srcAddress = (symbol_t)parseInt(str.c_str(), 16, 0, 0xff, result); if (result != RESULT_OK) { - errorDescription = "qq "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "qq "+str; return result; } if (!isMaster(srcAddress)) { - errorDescription = "qq "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "qq "+str; return RESULT_ERR_INVALID_ADDR; } } - str = getDefault(row["zz"], defaults, "zz"); // [ZZ] + str = getDefault(pluck(row, "zz"), defaults, "zz"); // [ZZ] vector dstAddresses; bool isBroadcastOrMasterDestination = false; if (str.empty()) { @@ -383,11 +390,11 @@ result_t Message::create(map row, vector< map > FileReader::trim(token); symbol_t dstAddress = (symbol_t)parseInt(token.c_str(), 16, 0, 0xff, result); if (result != RESULT_OK) { - errorDescription = "zz "+token+" in "+MappedFileReader::combineRow(row); + errorDescription = "zz "+token; return result; } if (!isValidAddress(dstAddress)) { - errorDescription = "zz "+token+" in "+MappedFileReader::combineRow(row); + errorDescription = "zz "+token; return RESULT_ERR_INVALID_ADDR; } bool broadcastOrMaster = (dstAddress == BROADCAST) || isMaster(dstAddress); @@ -395,7 +402,7 @@ result_t Message::create(map row, vector< map > isBroadcastOrMasterDestination = broadcastOrMaster; first = false; } else if (isBroadcastOrMasterDestination != broadcastOrMaster) { - errorDescription = "zz "+token+" in "+MappedFileReader::combineRow(row); + errorDescription = "zz "+token; return RESULT_ERR_INVALID_ADDR; } dstAddresses.push_back(dstAddress); @@ -403,21 +410,21 @@ result_t Message::create(map row, vector< map > } vector id; - str = row["pbsb"]; // [PBSB] + str = pluck(row, "pbsb"); // [PBSB] bool useDefaults = str.empty(); if (useDefaults) { str = getDefault(str, defaults, "pbsb"); } result = parseId(str, id); if (result != RESULT_OK) { - errorDescription = "pbsb "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "pbsb "+str; return result; } if (id.size() != 2) { - errorDescription = "pbsb "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "pbsb "+str; return RESULT_ERR_INVALID_ARG; // missing/to short/to long PBSB } - str = row["id"]; // [ID] (optional master data) + str = pluck(row, "id"); // [ID] (optional master data) string defaultIdPrefix; if (useDefaults) { defaultIdPrefix = getDefault("", defaults, "id"); @@ -437,7 +444,7 @@ result_t Message::create(map row, vector< map > if (lastChainLengthSpecified) { chainLength = parseInt(str.substr(lengthPos+1).c_str(), 10, 0, MAX_POS, result); if (result != RESULT_OK) { - errorDescription = "id "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "id "+str; return result; } str.resize(lengthPos); @@ -445,11 +452,11 @@ result_t Message::create(map row, vector< map > vector chainId = id; result = parseId(str, chainId); if (result != RESULT_OK) { - errorDescription = "id "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "id "+str; return result; } if (!chainIds.empty() && chainId.size() != chainIds.front().size()) { - errorDescription = "id length "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "id length "+str; return RESULT_ERR_INVALID_LIST; } chainIds.push_back(chainId); @@ -467,7 +474,7 @@ result_t Message::create(map row, vector< map > } } if (maxLength+chainLength > 255) { - errorDescription = "id length "+str+" in "+MappedFileReader::combineRow(row); + errorDescription = "id length "+str; return RESULT_ERR_INVALID_POS; } maxLength += chainLength; @@ -476,7 +483,7 @@ result_t Message::create(map row, vector< map > id = chainIds.front(); if (chainIds.size() > 1) { if (isPassive) { - errorDescription = "id (passive) in "+MappedFileReader::combineRow(row); + errorDescription = "id (passive)"; return RESULT_ERR_INVALID_LIST; } if (id.size() > chainPrefixLength) { @@ -493,10 +500,10 @@ result_t Message::create(map row, vector< map > if (!subDefaults.empty()) { subRows.insert(subRows.begin(), subDefaults.begin(), subDefaults.end()); } - DataField* data = NULL; + const DataField* data = NULL; if (subRows.empty()) { - vector fields; - data = new DataFieldSet("", "", fields); + vector fields; + data = new DataFieldSet("", fields); } else { result = DataField::create(subRows, errorDescription, templates, data, isWrite, false, isBroadcastOrMasterDestination, maxLength); @@ -508,7 +515,7 @@ result_t Message::create(map row, vector< map > || data->getLength(pt_slaveData, maxLength) > maxLength) { // max NN exceeded delete data; - errorDescription = "data length in "+MappedFileReader::combineRow(row); + errorDescription = "data length"; return RESULT_ERR_INVALID_POS; } unsigned int index = 0; @@ -523,10 +530,10 @@ result_t Message::create(map row, vector< map > } Message* message; if (chainIds.size() > 1) { - message = new ChainedMessage(useCircuit, level, name, isWrite, comment, srcAddress, dstAddress, id, chainIds, + message = new ChainedMessage(useCircuit, level, name, isWrite, row, srcAddress, dstAddress, id, chainIds, chainLengths, data, index == 0, pollPriority, condition); } else { - message = new Message(useCircuit, level, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data, + message = new Message(useCircuit, level, name, isWrite, isPassive, row, srcAddress, dstAddress, id, data, index == 0, pollPriority, condition); } messages.push_back(message); @@ -561,9 +568,9 @@ bool Message::extractFieldIds(string str, vector& fields, bool checkAbbr return !fields.empty(); } -Message* Message::derive(const symbol_t dstAddress, const symbol_t srcAddress, const string circuit) { +Message* Message::derive(const symbol_t dstAddress, const symbol_t srcAddress, const string circuit) const { Message* result = new Message(circuit.length() == 0 ? m_circuit : circuit, m_level, m_name, - m_isWrite, m_isPassive, m_comment, + m_isWrite, m_isPassive, m_attributes, srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress, m_id, m_data, false, m_pollPriority, m_condition); @@ -573,7 +580,7 @@ Message* Message::derive(const symbol_t dstAddress, const symbol_t srcAddress, c return result; } -Message* Message::derive(const symbol_t dstAddress, const bool extendCircuit) { +Message* Message::derive(const symbol_t dstAddress, const bool extendCircuit) const { if (extendCircuit) { ostringstream out; out << m_circuit << '.' << hex << setw(2) << setfill('0') << static_cast(dstAddress); @@ -604,7 +611,8 @@ bool Message::checkLevel(const string level, const string checkLevels) { } return false; } -bool Message::checkIdPrefix(vector& id) { + +bool Message::checkIdPrefix(const vector& id) const { if (id.size() > m_id.size()) { return false; } @@ -616,7 +624,7 @@ bool Message::checkIdPrefix(vector& id) { return true; } -bool Message::checkId(MasterSymbolString& master, size_t* index) { +bool Message::checkId(const MasterSymbolString& master, size_t* index) const { size_t idLen = getIdLength(); if (master.getDataSize() < idLen) { return false; @@ -632,7 +640,7 @@ bool Message::checkId(MasterSymbolString& master, size_t* index) { return true; } -bool Message::checkId(Message& other) { +bool Message::checkId(Message& other) const { size_t idLen = getIdLength(); if (idLen != other.getIdLength() || getCount() > 1) { // only equal for non-chained messages return false; @@ -640,7 +648,7 @@ bool Message::checkId(Message& other) { return other.checkIdPrefix(m_id); } -uint64_t Message::getDerivedKey(const symbol_t dstAddress) { +uint64_t Message::getDerivedKey(const symbol_t dstAddress) const { return (m_key & ~(0xffLL << (8*6))) | (uint64_t)dstAddress << (8*6); } @@ -670,7 +678,7 @@ bool Message::isAvailable() { return (m_condition == NULL) || m_condition->isTrue(); } -bool Message::hasField(const char* fieldName, bool numeric) { +bool Message::hasField(const char* fieldName, bool numeric) const { return m_data->hasField(fieldName, numeric); } @@ -749,8 +757,8 @@ result_t Message::storeLastData(MasterSymbolString& master, SlaveSymbolString& s } result_t Message::storeLastData(MasterSymbolString& data, size_t index) { - if (data.size() > 0 - && (m_isWrite || this->m_dstAddress == BROADCAST || isMaster(this->m_dstAddress))) { + if (data.size() > 0 && (m_isWrite || this->m_dstAddress == BROADCAST || isMaster(this->m_dstAddress) + || data.getDataSize() + 2 > m_id.size())) { time(&m_lastUpdateTime); } switch (data.compareTo(m_lastMasterData)) { @@ -761,6 +769,7 @@ result_t Message::storeLastData(MasterSymbolString& data, size_t index) { case 2: // only master address is different m_lastMasterData = data; break; + // else: identical } return RESULT_OK; } @@ -772,12 +781,13 @@ result_t Message::storeLastData(SlaveSymbolString& data, size_t index) { if (data != m_lastSlaveData) { m_lastChangeTime = m_lastUpdateTime; m_lastSlaveData = data; + const SlaveSymbolString& chk = getLastSlaveData(); } return RESULT_OK; } result_t Message::decodeLastMasterData(ostringstream& output, OutputFormat outputFormat, - bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) { + bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) const { size_t offset = m_id.size() - 2; result_t result = m_data->read(m_lastMasterData, offset, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); @@ -791,7 +801,7 @@ result_t Message::decodeLastMasterData(ostringstream& output, OutputFormat outpu } result_t Message::decodeLastSlaveData(ostringstream& output, OutputFormat outputFormat, - bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) { + bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) const { result_t result = m_data->read(m_lastSlaveData, 0, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); if (result < RESULT_OK) { @@ -804,7 +814,7 @@ result_t Message::decodeLastSlaveData(ostringstream& output, OutputFormat output } result_t Message::decodeLastData(ostringstream& output, OutputFormat outputFormat, - bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) { + bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) const { size_t startPos = output.str().length(); result_t result = m_data->read(m_lastMasterData, getIdLength(), output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); @@ -825,7 +835,7 @@ result_t Message::decodeLastData(ostringstream& output, OutputFormat outputForma return result; } -result_t Message::decodeLastDataNumField(unsigned int& output, const char* fieldName, ssize_t fieldIndex) { +result_t Message::decodeLastDataNumField(unsigned int& output, const char* fieldName, ssize_t fieldIndex) const { result_t result = m_data->read(m_lastMasterData, getIdLength(), output, fieldName, fieldIndex); if (result < RESULT_OK) { return result; @@ -842,7 +852,7 @@ result_t Message::decodeLastDataNumField(unsigned int& output, const char* field return result; } -bool Message::isLessPollWeight(const Message* other) { +bool Message::isLessPollWeight(const Message* other) const { size_t tprio = m_pollPriority; size_t oprio = other->m_pollPriority; size_t tw = tprio * m_pollCount; @@ -909,7 +919,7 @@ void Message::dumpHeader(ostream& output, vector* fieldIds) { } } -void Message::dump(ostream& output, vector* fieldIds, bool withConditions) { +void Message::dump(ostream& output, vector* fieldIds, bool withConditions) const { bool first = true; if (fieldIds == NULL) { for (size_t fieldId = MESSAGEFIELD_RANGE_MIN; fieldId <= MESSAGEFIELD_RANGE_MAX; fieldId++) { @@ -935,7 +945,7 @@ void Message::dump(ostream& output, vector* fieldIds, bool withCondition } } -void Message::dumpField(ostream& output, size_t fieldId, bool withConditions) { +void Message::dumpField(ostream& output, size_t fieldId, bool withConditions) const { switch (fieldId) { case MESSAGEFIELD_TYPE: if (withConditions && m_condition != NULL) { @@ -956,16 +966,16 @@ void Message::dumpField(ostream& output, size_t fieldId, bool withConditions) { } break; case MESSAGEFIELD_CIRCUIT: - DataField::dumpString(output, m_circuit, false); + dumpString(output, m_circuit, false); break; case MESSAGEFIELD_LEVEL: - DataField::dumpString(output, m_level, false); + dumpString(output, m_level, false); break; case MESSAGEFIELD_NAME: - DataField::dumpString(output, m_name, false); + dumpString(output, m_name, false); break; case MESSAGEFIELD_COMMENT: - DataField::dumpString(output, m_comment, false); + dumpAttribute(output, "comment", false); break; case MESSAGEFIELD_QQ: if (m_srcAddress != SYN) { @@ -997,14 +1007,14 @@ void Message::dumpField(ostream& output, size_t fieldId, bool withConditions) { ChainedMessage::ChainedMessage(const string circuit, const string level, const string name, - const bool isWrite, const string comment, + const bool isWrite, const map& attributes, const symbol_t srcAddress, const symbol_t dstAddress, const vector id, vector< vector > ids, vector lengths, - DataField* data, const bool deleteData, + const DataField* data, const bool deleteData, const size_t pollPriority, Condition* condition) - : Message(circuit, level, name, isWrite, false, comment, + : Message(circuit, level, name, isWrite, false, attributes, srcAddress, dstAddress, id, data, deleteData, pollPriority, condition), m_ids(ids), m_lengths(lengths), @@ -1033,9 +1043,9 @@ ChainedMessage::~ChainedMessage() { free(m_lastSlaveUpdateTimes); } -Message* ChainedMessage::derive(const symbol_t dstAddress, const symbol_t srcAddress, const string circuit) { +Message* ChainedMessage::derive(const symbol_t dstAddress, const symbol_t srcAddress, const string circuit) const { ChainedMessage* result = new ChainedMessage(circuit.length() == 0 ? m_circuit : circuit, m_level, m_name, - m_isWrite, m_comment, + m_isWrite, m_attributes, srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress, m_id, m_ids, m_lengths, m_data, false, m_pollPriority, m_condition); @@ -1045,7 +1055,7 @@ Message* ChainedMessage::derive(const symbol_t dstAddress, const symbol_t srcAdd return result; } -bool ChainedMessage::checkId(MasterSymbolString& master, size_t* index) { +bool ChainedMessage::checkId(const MasterSymbolString& master, size_t* index) const { size_t idLen = getIdLength(); if (master.getDataSize() < idLen) { return false; @@ -1076,7 +1086,7 @@ bool ChainedMessage::checkId(MasterSymbolString& master, size_t* index) { return false; } -bool ChainedMessage::checkId(Message& other) { +bool ChainedMessage::checkId(Message& other) const { size_t idLen = getIdLength(); if (idLen != other.getIdLength() || other.getCount() == 1) { // only equal for chained messages return false; @@ -1242,7 +1252,7 @@ result_t ChainedMessage::combineLastParts() { return result; } -void ChainedMessage::dumpField(ostream& output, size_t fieldId, bool withConditions) { +void ChainedMessage::dumpField(ostream& output, size_t fieldId, bool withConditions) const { if (fieldId != MESSAGEFIELD_ID) { Message::dumpField(output, fieldId, withConditions); return; @@ -1270,10 +1280,9 @@ void ChainedMessage::dumpField(ostream& output, size_t fieldId, bool withConditi * @param onlyAvailable true to include only available messages (default true), false to also include messages that * are currently not available (e.g. due to unresolved or false conditions). */ -Message* getFirstAvailable(vector &messages, MasterSymbolString* sameIdExtAs, +Message* getFirstAvailable(const vector &messages, const MasterSymbolString* sameIdExtAs, const bool onlyAvailable = true) { - for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) { - Message* message = *msgIt; + for (auto message : messages) { if (sameIdExtAs && !message->checkId(*sameIdExtAs)) { continue; } @@ -1291,10 +1300,9 @@ Message* getFirstAvailable(vector &messages, MasterSymbolString* sameI * @param onlyAvailable true to include only available messages (default true), false to also include messages that * are currently not available (e.g. due to unresolved or false conditions). */ -Message* getFirstAvailable(vector &messages, Message* sameIdExtAs = NULL, +Message* getFirstAvailable(const vector &messages, Message* sameIdExtAs = NULL, const bool onlyAvailable = true) { - for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) { - Message* message = *msgIt; + for (auto message : messages) { if (sameIdExtAs && !message->checkId(*sameIdExtAs)) { continue; } @@ -1381,7 +1389,7 @@ result_t splitValues(string valueList, vector& valueRanges) { result_t Condition::create(const string condName, map row, map rowDefaults, SimpleCondition*& returnValue) { - // name,circuit,messagename,[comment],[fieldname],[ZZ],values + // type=name,circuit,name=messagename,[comment],qq=[fieldname],[ZZ],pbsb=values string circuit = row["circuit"]; // circuit[#level] string level; size_t pos = circuit.find('#'); @@ -1414,7 +1422,10 @@ result_t Condition::create(const string condName, map row, map row, mapgetDerivedKey(m_dstAddress); - vector* derived = messages->getByKey(key); + const vector* derived = messages->getByKey(key); if (derived == NULL) { message = message->derive(m_dstAddress, true); messages->add(message); @@ -1600,17 +1611,15 @@ bool SimpleStringCondition::checkValue(Message* message, string field) { } -void CombinedCondition::dump(ostream& output, bool matched) { - for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - Condition* condition = *it; +void CombinedCondition::dump(ostream& output, bool matched) const { + for (auto condition : m_conditions) { condition->dump(output, matched); } } result_t CombinedCondition::resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)) { - for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - Condition* condition = *it; + for (auto condition : m_conditions) { ostringstream dummy; result_t ret = condition->resolve(messages, dummy, readMessageFunc); if (ret != RESULT_OK) { @@ -1662,23 +1671,28 @@ result_t Instruction::create(const string& contextPath, const string type, return RESULT_ERR_INVALID_ARG; } -string Instruction::getDestination() { +string Instruction::getDestination() const { // ZZ.circuit[.suffix] string ret; - if (!m_defaults["zz"].empty()) { - ret = m_defaults["zz"]; + auto it = m_defaults.find("zz"); + if (it != m_defaults.end() && !it->second.empty()) { + ret = it->second; } - if (!m_defaults["circuit"].empty() || !m_defaults["suffix"].empty()) { + it = m_defaults.find("circuit"); + string circuit = it == m_defaults.end() ? "" : it->second; + it = m_defaults.find("suffix"); + string suffix = it == m_defaults.end() ? "" : it->second; + if (!circuit.empty() || !suffix.empty()) { if (!ret.empty()) { ret += "."; } - if (m_defaults["circuit"].empty()) { + if (circuit.empty()) { ret += "*"; } else { - ret += m_defaults["circuit"]; + ret += circuit; } - if (!m_defaults["suffix"].empty()) { - ret += m_defaults["suffix"]; + if (!suffix.empty()) { + ret += suffix; } } return ret; @@ -1725,6 +1739,8 @@ result_t LoadInstruction::execute(MessageMap* messages, ostringstream& log, Cond } +vector MessageMap::s_noFiles; + result_t MessageMap::add(Message* message, bool storeByName) { uint64_t key = message->getKey(); bool conditional = message->isConditional(); @@ -1763,7 +1779,6 @@ result_t MessageMap::add(Message* message, bool storeByName) { } } m_messagesByName[nameKey].push_back(message); - nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit map >::iterator nameIt = m_messagesByName.find(nameKey); if (nameIt == m_messagesByName.end()) { @@ -1800,7 +1815,7 @@ result_t MessageMap::add(Message* message, bool storeByName) { return RESULT_OK; } -result_t MessageMap::getFieldMap(vector& row, string& errorDescription) { +result_t MessageMap::getFieldMap(vector& row, string& errorDescription) const { // type (r[1-9];w;u),circuit,name,[comment],[QQ],ZZ,PBSB,[ID],field1,part (m/s),datatypes/templates,divider/values, // unit,comment // minimum: type,name,PBSB,field,datatype @@ -1826,55 +1841,56 @@ result_t MessageMap::getFieldMap(vector& row, string& errorDescription) bool inDataFields = false; map seen; for (auto &name : row) { - tolower(name); - size_t fieldId; + string useName = name; + tolower(useName); if (inDataFields) { - fieldId = getDataFieldId(name); - if (fieldId == UINT_MAX) { - errorDescription = "unknown field " + name; - return RESULT_ERR_INVALID_ARG; - } - if (seen.find(name) != seen.end()) { - if (seen.find("name") == seen.end() || seen.find("type") == seen.end()) { - return RESULT_ERR_EOF; // require at least name and type + size_t fieldId = getDataFieldId(useName); + if (fieldId != UINT_MAX) { + useName = getDataFieldName(fieldId); + if (seen.find(useName) != seen.end()) { + if (seen.find("name") == seen.end() || seen.find("type") == seen.end()) { + errorDescription = "missing field name/type as of already seen "+useName; + return RESULT_ERR_EOF; // require at least name and type + } + seen.clear(); } - seen.clear(); - name = "*" + getDataFieldName(fieldId); // data field repetition - } else { - name = getDataFieldName(fieldId); } } else { - fieldId = getMessageFieldId(name); - if (fieldId == UINT_MAX) { - fieldId = getDataFieldId(name); - if (fieldId == UINT_MAX) { - errorDescription = "unknown field " + name; - return RESULT_ERR_INVALID_ARG; - } - if (seen.find("type") == seen.end() || seen.find("name") == seen.end() || seen.find("pbsb") == seen.end()) { - return RESULT_ERR_EOF; // require at least type, name, and pbsb - } - inDataFields = true; - seen.clear(); - name = "*" + getDataFieldName(fieldId); + size_t fieldId = getMessageFieldId(useName); + if (fieldId != UINT_MAX && (fieldId != MESSAGEFIELD_NAME || seen.find("name") == seen.end())) { + useName = getMessageFieldName(fieldId); } else { - if (seen.find(name) != seen.end()) { - errorDescription = "duplicate field " + name; - return RESULT_ERR_INVALID_ARG; + fieldId = getDataFieldId(useName); + if (fieldId != UINT_MAX) { + useName = getDataFieldName(fieldId); + if (seen.find("type") == seen.end() || seen.find("name") == seen.end() || seen.find("pbsb") == seen.end()) { + errorDescription = "missing message name/type/pbsb"; + return RESULT_ERR_EOF; // require at least type, name, and pbsb + } + inDataFields = true; + seen.clear(); } - name = getMessageFieldName(fieldId); + } + if (!inDataFields && seen.find(useName) != seen.end()) { + errorDescription = "duplicate message " + useName; + return RESULT_ERR_INVALID_ARG; } } - seen[name] = name; + if (seen.empty() && inDataFields) { + name = "*" + useName; // data field repetition + } else { + name = useName; + } + seen[useName] = useName; } if (inDataFields) { if (seen.find("name") == seen.end() || seen.find("type") == seen.end()) { + errorDescription = "missing field name/type"; return RESULT_ERR_EOF; // require at least name and type } - } else { - if (seen.find("type") == seen.end() || seen.find("name") == seen.end() || seen.find("pbsb") == seen.end()) { - return RESULT_ERR_EOF; // require at least type, name, and pbsb - } + } else if (seen.find("type") == seen.end() || seen.find("name") == seen.end() || seen.find("pbsb") == seen.end()) { + errorDescription = "missing message name/type/pbsb"; + return RESULT_ERR_EOF; // require at least type, name, and pbsb } return RESULT_OK; } @@ -1884,9 +1900,13 @@ result_t MessageMap::addDefaultFromFile(map& row, vector< map defaults; + if (mainDefaults != getDefaults().end()) { + defaults = mainDefaults->second; + } if (!type.empty() && type[0] == '[' && type[type.length()-1] == ']') { // condition - map defaults = getDefaults()[""]; type = type.substr(1, type.length()-2); if (type.find('[') != string::npos || type.find(']') != string::npos) { errorDescription = "invalid condition name "+type; @@ -1907,41 +1927,40 @@ result_t MessageMap::addDefaultFromFile(map& row, vector< map defaults = getDefaults()[""]; - string defaultCircuit = defaults["circuit"]; - string defaultSuffix = defaults["suffix"]; - defaults.erase("suffix"); - for (auto entry : row) { - string value = entry.second; - if (entry.first == "circuit" && !defaultCircuit.empty()) { // TODO remove some day - if (value.empty()) { - value = defaultCircuit+defaultSuffix; // set default circuit and suffix: "circuit[.suffix]" - } else if (value[0] == '#') { - // move access level behind default circuit and suffix: "circuit[.suffix]#level" - value = defaultCircuit+defaultSuffix+value; - } else if (!defaultSuffix.empty() && value.find_last_of('.') == string::npos) { - // circuit suffix not yet present - size_t pos = value.find_first_of('#'); - if (pos == string::npos) { - value += defaultSuffix; // append default suffix: "circuit.suffix" - } else { - // insert default suffix: "circuit.suffix#level" - value = value.substr(0, pos)+defaultSuffix+value.substr(pos); - } + if (type.empty()) { + errorDescription = "invalid default definition"; + return RESULT_ERR_INVALID_ARG; + } + string defaultCircuit = defaults["circuit"]; + string defaultSuffix = defaults["suffix"]; + defaults.erase("suffix"); + for (auto entry : row) { + string value = entry.second; + if (entry.first == "circuit" && !defaultCircuit.empty()) { // TODO remove some day + if (value.empty()) { + value = defaultCircuit+defaultSuffix; // set default circuit and suffix: "circuit[.suffix]" + } else if (value[0] == '#') { + // move access level behind default circuit and suffix: "circuit[.suffix]#level" + value = defaultCircuit+defaultSuffix+value; + } else if (!defaultSuffix.empty() && value.find_last_of('.') == string::npos) { + // circuit suffix not yet present + size_t pos = value.find_first_of('#'); + if (pos == string::npos) { + value += defaultSuffix; // append default suffix: "circuit.suffix" + } else { + // insert default suffix: "circuit.suffix#level" + value = value.substr(0, pos)+defaultSuffix+value.substr(pos); } } - if (!value.empty() || defaults[entry.first].empty()) { - defaults[entry.first] = value; - } } - getDefaults()[type] = defaults; - vector< map > subDefaults = subRows; // ensure to have a copy - getSubDefaults()[type] = subDefaults; - return RESULT_OK; + if (!value.empty() || defaults[entry.first].empty()) { + defaults[entry.first] = value; + } } - errorDescription = "invalid default definition"; - return RESULT_ERR_INVALID_ARG; + getDefaults()[type] = defaults; + vector< map > subDefaults = subRows; // ensure to have a copy + getSubDefaults()[type] = subDefaults; + return RESULT_OK; } result_t MessageMap::readConditions(string& types, const string filename, string& errorDescription, @@ -1950,7 +1969,7 @@ result_t MessageMap::readConditions(string& types, const string filename, string if (types.length() > 0 && types[0] == '[' && (pos=types.find_last_of(']')) != string::npos) { // check if combined or simple condition is already known const string combinedkey = filename+":"+types.substr(1, pos-1); - map::iterator it = m_conditions.find(combinedkey); + auto it = m_conditions.find(combinedkey); if (it != m_conditions.end()) { condition = it->second; types = types.substr(pos+1); @@ -2005,7 +2024,7 @@ result_t MessageMap::readConditions(string& types, const string filename, string } bool MessageMap::extractDefaultsFromFilename(string filename, map& defaults, - symbol_t* destAddress, unsigned int* software, unsigned int* hardware) { + symbol_t* destAddress, unsigned int* software, unsigned int* hardware) const { string ident, circuit, suffix; unsigned int sw = UINT_MAX, hw = UINT_MAX; string remain = filename; @@ -2180,7 +2199,7 @@ Message* MessageMap::getScanMessage(const symbol_t dstAddress) { return NULL; } uint64_t key = m_scanMessage->getDerivedKey(dstAddress); - vector* msgs = getByKey(key); + const vector* msgs = getByKey(key); if (msgs != NULL) { return msgs->front(); } @@ -2220,12 +2239,11 @@ result_t MessageMap::resolveCondition(Condition* condition, string& errorDescrip result_t MessageMap::executeInstructions(ostringstream& log, void (*readMessageFunc)(Message* message)) { result_t overallResult = RESULT_OK; vector remove; - for (map >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) { - vector instructions = it->second; + for (auto& it : m_instructions) { + auto& instructions = it.second; bool removeSingletons = false; vector remain; - for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { - Instruction* instruction = *lit; + for (auto instruction : instructions) { if (removeSingletons && instruction->isSingleton()) { delete instruction; continue; @@ -2238,6 +2256,11 @@ result_t MessageMap::executeInstructions(ostringstream& log, void (*readMessageF instruction->isSingleton()?readMessageFunc:NULL); if (result != RESULT_OK) { overallResult = result; + log << "error resolving condition for \"" << instruction->getDestination() << "\": " + << getResultCode(result); + if (!errorDescription.empty()) { + log << " " << errorDescription; + } } else if (condition->isTrue()) { execute = true; } @@ -2268,13 +2291,13 @@ result_t MessageMap::executeInstructions(ostringstream& log, void (*readMessageF } } if (remain.empty()) { - remove.push_back(it->first); + remove.push_back(it.first); } else { - it->second = remain; + it.second = remain; } } - for (vector::iterator it = remove.begin(); it != remove.end(); it++) { - m_instructions.erase(*it); + for (auto it : remove) { + m_instructions.erase(it); } return overallResult; } @@ -2289,11 +2312,15 @@ void MessageMap::addLoadedFile(symbol_t address, string file, string comment) { } } -vector& MessageMap::getLoadedFiles(symbol_t address) { - return m_loadedFiles[address]; +const vector& MessageMap::getLoadedFiles(symbol_t address) const { + auto files = m_loadedFiles.find(address); + if (files != m_loadedFiles.end()) { + return files->second; + } + return s_noFiles; } -vector MessageMap::getLoadedFiles() { +vector MessageMap::getLoadedFiles() const { vector ret; for (auto& loadedFile : m_loadedFileInfos) { ret.push_back(loadedFile.first); @@ -2301,8 +2328,8 @@ vector MessageMap::getLoadedFiles() { return ret; } -bool MessageMap::getLoadedFileInfo(string filename, string& comment, size_t* hash, size_t* size, time_t* time) { - map::iterator it = m_loadedFileInfos.find(filename); +bool MessageMap::getLoadedFileInfo(string filename, string& comment, size_t* hash, size_t* size, time_t* time) const { + auto it = m_loadedFileInfos.find(filename); if (it == m_loadedFileInfos.end()) { comment = ""; hash = size = 0; @@ -2322,8 +2349,8 @@ bool MessageMap::getLoadedFileInfo(string filename, string& comment, size_t* has return true; } -vector* MessageMap::getByKey(const uint64_t key) { - map >::iterator it = m_messagesByKey.find(key); +const vector* MessageMap::getByKey(const uint64_t key) const { + auto it = m_messagesByKey.find(key); if (it != m_messagesByKey.end()) { return &it->second; } @@ -2331,7 +2358,7 @@ vector* MessageMap::getByKey(const uint64_t key) { } Message* MessageMap::find(const string& circuit, const string& name, const string& levels, const bool isWrite, - const bool isPassive) { + const bool isPassive) const { string lcircuit = circuit; FileReader::tolower(lcircuit); string lname = name; @@ -2345,7 +2372,7 @@ Message* MessageMap::find(const string& circuit, const string& name, const strin } else { continue; // not allowed without circuit } - map >::iterator it = m_messagesByName.find(key); + auto it = m_messagesByName.find(key); if (it != m_messagesByName.end()) { Message* message = getFirstAvailable(it->second); if (message && message->hasLevel(levels)) { @@ -2359,7 +2386,7 @@ Message* MessageMap::find(const string& circuit, const string& name, const strin deque MessageMap::findAll(const string& circuit, const string& name, const string& levels, const bool completeMatch, const bool withRead, const bool withWrite, const bool withPassive, const bool includeEmptyLevel, const bool onlyAvailable, - const time_t since, const time_t until) { + const time_t since, const time_t until) const { deque ret; string lcircuit = circuit; FileReader::tolower(lcircuit); @@ -2368,12 +2395,11 @@ deque MessageMap::findAll(const string& circuit, const string& name, c bool checkCircuit = lcircuit.length() > 0; bool checkLevel = levels != "*"; bool checkName = lname.length() > 0; - for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { - if (it->first[0] == '-') { // avoid duplicates: instances stored multiple times have a key starting with "-" + for (auto it : m_messagesByName) { + if (it.first[0] == '-') { // avoid duplicates: instances stored multiple times have a key starting with "-" continue; } - for (vector::iterator msgIt = it->second.begin(); msgIt != it->second.end(); msgIt++) { - Message* message = *msgIt; + for (auto message : it.second) { if (checkLevel && !message->hasLevel(levels, includeEmptyLevel)) { continue; } @@ -2415,7 +2441,7 @@ deque MessageMap::findAll(const string& circuit, const string& name, c } } if (!onlyAvailable || message->isAvailable()) { - ret.push_back(*msgIt); + ret.push_back(message); } } } @@ -2424,7 +2450,7 @@ deque MessageMap::findAll(const string& circuit, const string& name, c } Message* MessageMap::find(MasterSymbolString& master, bool anyDestination, - const bool withRead, const bool withWrite, const bool withPassive, const bool onlyAvailable) { + const bool withRead, const bool withWrite, const bool withPassive, const bool onlyAvailable) const { if (master.size() >= 5 && master[4] == 0 && anyDestination && master[2] == 0x07 && master[3] == 0x04) { return m_scanMessage; } @@ -2448,7 +2474,7 @@ Message* MessageMap::find(MasterSymbolString& master, bool anyDestination, } } } - map >::iterator it; + map >::const_iterator it; if (withPassive) { it = m_messagesByKey.find(key); if (it != m_messagesByKey.end()) { @@ -2596,17 +2622,16 @@ Message* MessageMap::getNextPoll() { return ret; } -void MessageMap::dump(ostream& output, bool withConditions) { +void MessageMap::dump(ostream& output, bool withConditions) const { bool first = true; Message::dumpHeader(output, NULL); output << endl; - for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { - if (it->first[0] == '-') { // skip instances stored multiple times (key starting with "-") + for (auto it : m_messagesByName) { + if (it.first[0] == '-') { // skip instances stored multiple times (key starting with "-") continue; } if (m_addAll) { - for (vector::iterator mit = it->second.begin(); mit != it->second.end(); mit++) { - Message* message = *mit; + for (auto message : it.second) { if (!message) { continue; } @@ -2618,7 +2643,7 @@ void MessageMap::dump(ostream& output, bool withConditions) { message->dump(output, NULL, withConditions); } } else { - Message* message = getFirstAvailable(it->second); + Message* message = getFirstAvailable(it.second); if (!message) { continue; } diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 5a6cbb2e..3a43ead1 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -108,7 +108,7 @@ class MessageMap; /** * Defines parameters of a message sent or received on the bus. */ -class Message { +class Message : public AttributedItem { friend class MessageMap; public: /** @@ -119,7 +119,7 @@ class Message { * @param isWrite whether this is a write message. * @param isPassive true if message can only be initiated by a participant other than us, * false if message can be initiated by any participant. - * @param comment the comment. + * @param attributes the additional named attributes. * @param srcAddress the source address, or @a SYN for any (only relevant if passive). * @param dstAddress the destination address, or @a SYN for any (set later). * @param id the primary, secondary, and optional further ID bytes. @@ -129,10 +129,10 @@ class Message { * @param condition the @a Condition for this message, or NULL. */ Message(const string circuit, const string level, const string name, - const bool isWrite, const bool isPassive, const string comment, + const bool isWrite, const bool isPassive, const map& attributes, const symbol_t srcAddress, const symbol_t dstAddress, const vector id, - DataField* data, const bool deleteData, + const DataField* data, const bool deleteData, const size_t pollPriority = 0, Condition* condition = NULL); @@ -151,7 +151,7 @@ class Message { */ Message(const string circuit, const string level, const string name, const symbol_t pb, const symbol_t sb, - const bool broadcast, DataField* data, const bool deleteData); + const bool broadcast, const DataField* data, const bool deleteData); public: @@ -184,6 +184,15 @@ class Message { static uint64_t createKey(MasterSymbolString& master, size_t maxIdLength, bool anyDestination = false); + /** + * Calculate the key for a scan message. + * @param pb the primary ID byte. + * @param sb the secondary ID byte. + * @param broadcast true for broadcast scan message, false for scan message to be sent to a slave address. + * @return the key for the scan message. + */ + static uint64_t createKey(const symbol_t pb, const symbol_t sb, const bool broadcast); + /** * Get the length field from the key. * @param key the key. @@ -243,7 +252,7 @@ class Message { * Return whether this is a special scanning @a Message instance. * @return whether this is a special scanning @a Message instance. */ - bool isScanMessage() { return m_isScanMessage; } + bool isScanMessage() const { return m_isScanMessage; } /** * Derive a new @a Message from this message. @@ -253,7 +262,7 @@ class Message { * @return the derived @a Message instance. */ virtual Message* derive(const symbol_t dstAddress, const symbol_t srcAddress = SYN, - const string circuit = ""); + const string circuit = "") const; /** * Derive a new @a Message from this message. @@ -261,7 +270,7 @@ class Message { * @param extendCircuit whether to extend the current circuit name with a dot and the new destination address in hex. * @return the derived @a ScanMessage instance. */ - Message* derive(const symbol_t dstAddress, const bool extendCircuit); + Message* derive(const symbol_t dstAddress, const bool extendCircuit) const; /** * Get the optional circuit name. @@ -282,7 +291,7 @@ class Message { * level to check. * @return true when access is granted. */ - bool hasLevel(const string levels, bool includeEmpty = true) { + bool hasLevel(const string levels, bool includeEmpty = true) const { return m_level.empty() ? (includeEmpty || levels.empty()) : checkLevel(m_level, levels); } @@ -294,18 +303,12 @@ class Message { */ static bool checkLevel(const string level, const string checkLevels); - /** - * Get the message name (unique within the same circuit and type). - * @return the message name (unique within the same circuit and type). - */ - string getName() const { return m_name; } - /** * Get the specified field name. * @param fieldIndex the index of the field. * @return the field name, or the index as string if not unique or not available. */ - virtual string getFieldName(ssize_t fieldIndex) const { return m_data->getName(fieldIndex); } + virtual string getFieldName(const ssize_t fieldIndex) const { return m_data->getName(fieldIndex); } /** * Get whether this is a write message. @@ -320,12 +323,6 @@ class Message { */ bool isPassive() const { return m_isPassive; } - /** - * Get the comment. - * @return the comment. - */ - string getComment() const { return m_comment; } - /** * Get the source address. * @return the source address, or @a SYN for any. @@ -361,7 +358,7 @@ class Message { * @param id the ID bytes to check against. * @return true if the full command ID starts with the given value. */ - bool checkIdPrefix(vector& id); + bool checkIdPrefix(const vector& id) const; /** * Check the ID against the master @a SymbolString data. @@ -369,27 +366,27 @@ class Message { * @param index the variable in which to store the message part index, or NULL to ignore. * @return true if the ID matches, false otherwise. */ - virtual bool checkId(MasterSymbolString& master, size_t* index = NULL); + virtual bool checkId(const MasterSymbolString& master, size_t* index = NULL) const; /** * Check the ID against the other @a Message. * @param other the other @a Message to check against. * @return true if the ID matches, false otherwise. */ - virtual bool checkId(Message& other); + virtual bool checkId(Message& other) const; /** * Return the key for storing in @a MessageMap. * @return the key for storing in @a MessageMap. */ - uint64_t getKey() { return m_key; } + uint64_t getKey() const { return m_key; } /** * Return the derived key for storing in @a MessageMap. * @param dstAddress the destination address for the derivation. * @return the derived key for storing in @a MessageMap. */ - uint64_t getDerivedKey(const symbol_t dstAddress); + uint64_t getDerivedKey(const symbol_t dstAddress) const; /** * Get the polling priority, or 0 for no polling at all. @@ -427,12 +424,12 @@ class Message { * @param numeric true for a numeric field, false for a string field. * @return true if the field is available. */ - bool hasField(const char* fieldName, bool numeric = true); + bool hasField(const char* fieldName, bool numeric = true) const; /** * @return the number of parts this message is composed of. */ - virtual size_t getCount() { return 1; } + virtual size_t getCount() const { return 1; } /** * Prepare the master @a SymbolString for sending a query or command to the bus. @@ -505,7 +502,7 @@ class Message { * @return @a RESULT_OK on success, or an error code. */ virtual result_t decodeLastMasterData(ostringstream& output, OutputFormat outputFormat = 0, - bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1); + bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) const; /** * Decode the value from the last stored slave data. @@ -517,7 +514,7 @@ class Message { * @return @a RESULT_OK on success, or an error code. */ virtual result_t decodeLastSlaveData(ostringstream& output, OutputFormat outputFormat = 0, - bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1); + bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) const; /** * Decode the value from the last stored data. @@ -529,7 +526,7 @@ class Message { * @return @a RESULT_OK on success, or an error code. */ virtual result_t decodeLastData(ostringstream& output, OutputFormat outputFormat = 0, - bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1); + bool leadingSeparator = false, const char* fieldName = NULL, ssize_t fieldIndex = -1) const; /** * Decode a particular numeric field value from the last stored data. @@ -538,44 +535,44 @@ class Message { * @param fieldIndex the optional index of the named field, or -1. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t decodeLastDataNumField(unsigned int& output, const char* fieldName, ssize_t fieldIndex = -1); + virtual result_t decodeLastDataNumField(unsigned int& output, const char* fieldName, ssize_t fieldIndex = -1) const; /** * Get the last seen master data. * @return the last seen @a MasterSymbolString. */ - MasterSymbolString& getLastMasterData() { return m_lastMasterData; } + const MasterSymbolString& getLastMasterData() const { return m_lastMasterData; } /** * Get the last seen slave data. * @return the last seen @a SlaveSymbolString. */ - SlaveSymbolString& getLastSlaveData() { return m_lastSlaveData; } + const SlaveSymbolString& getLastSlaveData() const { return m_lastSlaveData; } /** * Get the time when this message was last seen with reasonable data. * @return the time when this message was last seen, or 0. */ - time_t getLastUpdateTime() { return m_lastUpdateTime; } + time_t getLastUpdateTime() const { return m_lastUpdateTime; } /** * Get the time when the message data was last changed. * @return the time when the message data was last changed, or 0 if this message was not decoded yet. */ - time_t getLastChangeTime() { return m_lastChangeTime; } + time_t getLastChangeTime() const { return m_lastChangeTime; } /** * Get the time when this message was last polled for. * @return the time when this message was last polled for, or 0 for never. */ - time_t getLastPollTime() { return m_lastPollTime; } + time_t getLastPollTime() const { return m_lastPollTime; } /** * Return whether this @a Message needs to be polled after the other one. * @param other the other @a Message to compare with. * @return true if this @a Message needs to be polled after the other one. */ - bool isLessPollWeight(const Message* other); + bool isLessPollWeight(const Message* other) const; /** * Write the message definition header or parts of it to the @a ostream. @@ -590,7 +587,7 @@ class Message { * @param fieldIds the list of field IDs to write, or NULL for all (see @p MESSAGEFIELD_TYPE constants). * @param withConditions whether to include the optional conditions prefix. */ - void dump(ostream& output, vector* fieldIds = NULL, bool withConditions = false); + void dump(ostream& output, vector* fieldIds = NULL, bool withConditions = false) const; /** * Write the specified field to the @a ostream. @@ -598,7 +595,7 @@ class Message { * @param fieldId the field ID to write (see @p MESSAGEFIELD_TYPE constants). * @param withConditions whether to include the optional conditions prefix. */ - virtual void dumpField(ostream& output, size_t fieldId, bool withConditions = false); + virtual void dumpField(ostream& output, size_t fieldId, bool withConditions = false) const; protected: @@ -608,9 +605,6 @@ class Message { /** the optional access level. */ const string m_level; - /** the message name (unique within the same circuit and type). */ - const string m_name; - /** whether this is a write message. */ const bool m_isWrite; @@ -618,8 +612,8 @@ class Message { * false if message can be initiated by any participant. */ const bool m_isPassive; - /** the comment. */ - const string m_comment; + /** the additional named attributes. */ + const map m_attributes; /** the source address, or @a SYN for any (only relevant if passive). */ const symbol_t m_srcAddress; @@ -628,7 +622,7 @@ class Message { const symbol_t m_dstAddress; /** the primary, secondary, and optionally further command ID bytes. */ - vector m_id; + const vector m_id; /** * the key for storing in @a MessageMap. @@ -651,10 +645,10 @@ class Message { *
  • bytes 3-0: ID bytes (with cyclic xor if more than 4)
  • * */ - uint64_t m_key; + const uint64_t m_key; /** the @a DataField for encoding/decoding the message. */ - DataField* m_data; + const DataField* m_data; /** whether to delete the @a DataField during destruction. */ const bool m_deleteData; @@ -702,7 +696,7 @@ class ChainedMessage : public Message { * @param level the optional access level. * @param name the message name (unique within the same circuit and type). * @param isWrite whether this is a write message. - * @param comment the comment. + * @param attributes the additional named attributes. * @param srcAddress the source address, or @a SYN for any (only relevant if passive). * @param dstAddress the destination address, or @a SYN for any (set later). * @param id the primary, secondary, and optional further ID bytes common to each part of the chain. @@ -714,11 +708,11 @@ class ChainedMessage : public Message { * @param condition the @a Condition for this message, or NULL. */ ChainedMessage(const string circuit, const string level, const string name, - const bool isWrite, const string comment, + const bool isWrite, const map& attributes, const symbol_t srcAddress, const symbol_t dstAddress, const vector id, vector< vector > ids, vector lengths, - DataField* data, const bool deleteData, + const DataField* data, const bool deleteData, const size_t pollPriority, Condition* condition = NULL); @@ -726,19 +720,19 @@ class ChainedMessage : public Message { // @copydoc Message* derive(const symbol_t dstAddress, const symbol_t srcAddress = SYN, - const string circuit = "") override; + const string circuit = "") const override; // @copydoc size_t getIdLength() const override { return m_ids[0].size() - 2; } // @copydoc - bool checkId(MasterSymbolString& master, size_t* index = NULL) override; + bool checkId(const MasterSymbolString& master, size_t* index = NULL) const override; // @copydoc - bool checkId(Message& other) override; + bool checkId(Message& other) const override; // @copydoc - size_t getCount() override { return m_ids.size(); } + size_t getCount() const override { return m_ids.size(); } protected: @@ -765,7 +759,7 @@ class ChainedMessage : public Message { protected: // @copydoc - void dumpField(ostream& output, size_t fieldId, bool withConditions = false) override; + void dumpField(ostream& output, size_t fieldId, bool withConditions = false) const override; private: @@ -860,14 +854,14 @@ class Condition { * @param valueList the @a string with the new list of values. * @return the derived @a SimpleCondition instance, or NULL if the value list is invalid. */ - virtual SimpleCondition* derive(string valueList) { return NULL; } + virtual SimpleCondition* derive(string valueList) const { return NULL; } /** * Write the condition definition or resolved expression to the @a ostream. * @param output the @a ostream to append to. * @param matched true for dumping the matched value if the condition is true, false for dumping the definition. */ - virtual void dump(ostream& output, bool matched = false) = 0; + virtual void dump(ostream& output, bool matched = false) const = 0; /** * Combine this condition with another instance using a logical and. @@ -931,10 +925,10 @@ class SimpleCondition : public Condition { virtual ~SimpleCondition() {} // @copydoc - SimpleCondition* derive(string valueList) override; + SimpleCondition* derive(string valueList) const override; // @copydoc - void dump(ostream& output, bool matched = false) override; + void dump(ostream& output, bool matched = false) const override; // @copydoc CombinedCondition* combineAnd(Condition* other) override; @@ -950,7 +944,7 @@ class SimpleCondition : public Condition { * Return whether the condition is based on a numeric value. * @return whether the condition is based on a numeric value. */ - virtual bool isNumeric() { return true; } + virtual bool isNumeric() const { return true; } protected: @@ -1062,7 +1056,7 @@ class SimpleStringCondition : public SimpleCondition { virtual ~SimpleStringCondition() {} // @copydoc - bool isNumeric() override { return false; } + bool isNumeric() const override { return false; } protected: @@ -1093,7 +1087,7 @@ class CombinedCondition : public Condition { virtual ~CombinedCondition() {} // @copydoc - void dump(ostream& output, bool matched = false) override; + void dump(ostream& output, bool matched = false) const override; // @copydoc CombinedCondition* combineAnd(Condition* other) override { m_conditions.push_back(other); return this; } @@ -1124,7 +1118,7 @@ class Instruction { * executed for the same source file. * @param defaults the mapped definition defaults. */ - Instruction(Condition* condition, const bool singleton, map& defaults) + Instruction(Condition* condition, const bool singleton, const map& defaults) : m_condition(condition), m_singleton(singleton), m_defaults(defaults) { } /** @@ -1158,13 +1152,13 @@ class Instruction { * @return whether this @a Instruction belongs to a set of instructions of which only the first one may be executed * for the same source file. */ - bool isSingleton() { return m_singleton; } + bool isSingleton() const { return m_singleton; } /** * Return a string describing the destination from the stored default values. * @return a string describing the destination. */ - string getDestination(); + string getDestination() const; /** * Execute the instruction. @@ -1182,7 +1176,7 @@ class Instruction { /** whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the * same source file. */ - bool m_singleton; + const bool m_singleton; protected: @@ -1227,17 +1221,6 @@ class LoadInstruction : public Instruction { */ class LoadedFileInfo { public: - /** - * Constructor. - * @param comment the optional comment for the file. - * @param hash the hash of the file. - * @param size the normalized size of the file. - * @param time the modification time of the file. - */ - /*explicit LoadedFileInfo(string comment, size_t hash, size_t size, time_t time) - : m_comment(comment), m_hash(hash), m_size(size), m_time(time) {} - LoadedFileInfo(const LoadedFileInfo& copyFrom) - : m_comment(copyFrom.m_comment), m_hash(copyFrom.m_hash), m_size(copyFrom.m_size), m_time(copyFrom.m_time) {}*/ /** the optional comment for the file. */ string m_comment; @@ -1293,7 +1276,7 @@ class MessageMap : public MappedFileReader { result_t add(Message* message, bool storeByName = true); // @copydoc - result_t getFieldMap(vector& row, string& errorDescription) override; + result_t getFieldMap(vector& row, string& errorDescription) const override; // @copydoc result_t addDefaultFromFile(map& row, vector< map >& subRows, @@ -1311,7 +1294,7 @@ class MessageMap : public MappedFileReader { // @copydoc bool extractDefaultsFromFilename(string filename, map& defaults, - symbol_t* destAddress = NULL, unsigned int* software = NULL, unsigned int* hardware = NULL) override; + symbol_t* destAddress = NULL, unsigned int* software = NULL, unsigned int* hardware = NULL) const override; // @copydoc result_t readFromFile(const string filename, string& errorDescription, bool verbose = false, @@ -1332,7 +1315,7 @@ class MessageMap : public MappedFileReader { * Return whether additional scan @a Message instances are available. * @return whether additional scan @a Message instances are available. */ - bool hasAdditionalScanMessages() { return m_additionalScanMessages; } + bool hasAdditionalScanMessages() const { return m_additionalScanMessages; } /** * Resolve all @a Condition instances. @@ -1374,13 +1357,13 @@ class MessageMap : public MappedFileReader { * @param address the slave address. * @return the loaded configuration files (list of file names with relative path). */ - vector& getLoadedFiles(symbol_t address); + const vector& getLoadedFiles(symbol_t address) const; /** * Get all loaded files. * @return the loaded configuration files (list of file names with relative path). */ - vector getLoadedFiles(); + vector getLoadedFiles() const; /** * Get the infos for a loaded file. @@ -1392,7 +1375,7 @@ class MessageMap : public MappedFileReader { * @return true if the file info was found, false otherwise. */ bool getLoadedFileInfo(string filename, string& comment, size_t* hash = NULL, size_t* size = NULL, - time_t* time = NULL); + time_t* time = NULL) const; /** * Get the stored @a Message instances for the key. @@ -1400,7 +1383,7 @@ class MessageMap : public MappedFileReader { * @return the found @a Message instances, or NULL. * Note: the caller may not free the returned instances. */ - vector* getByKey(const uint64_t key); + const vector* getByKey(const uint64_t key) const; /** * Find the @a Message instance for the specified circuit and name. @@ -1413,7 +1396,7 @@ class MessageMap : public MappedFileReader { * Note: the caller may not free the returned instance. */ Message* find(const string& circuit, const string& name, const string& levels, const bool isWrite, - const bool isPassive = false); + const bool isPassive = false) const; /** * Find all active get @a Message instances for the specified circuit and name. @@ -1439,7 +1422,7 @@ class MessageMap : public MappedFileReader { deque findAll(const string& circuit, const string& name, const string& levels, const bool completeMatch = true, const bool withRead = true, const bool withWrite = false, const bool withPassive = false, const bool includeEmptyLevel = true, const bool onlyAvailable = true, - const time_t since = 0, const time_t until = 0); + const time_t since = 0, const time_t until = 0) const; /** * Find the @a Message instance for the specified master data. @@ -1454,7 +1437,7 @@ class MessageMap : public MappedFileReader { * Note: the caller may not free the returned instance. */ Message* find(MasterSymbolString& master, bool anyDestination = false, const bool withRead = true, - const bool withWrite = true, const bool withPassive = true, const bool onlyAvailable = true); + const bool withWrite = true, const bool withPassive = true, const bool onlyAvailable = true) const; /** * Invalidate cached data of the @a Message and all other instances with a matching name key. @@ -1478,25 +1461,25 @@ class MessageMap : public MappedFileReader { * Get the number of all stored @a Message instances. * @return the the number of all stored @a Message instances. */ - size_t size() { return m_messageCount; } + size_t size() const { return m_messageCount; } /** * Get the number of stored conditional @a Message instances. * @return the the number of stored conditional @a Message instances. */ - size_t sizeConditional() { return m_conditionalMessageCount; } + size_t sizeConditional() const { return m_conditionalMessageCount; } /** * Get the number of stored passive @a Message instances. * @return the the number of stored passive @a Message instances. */ - size_t sizePassive() { return m_passiveMessageCount; } + size_t sizePassive() const { return m_passiveMessageCount; } /** * Get the number of stored @a Message instances with a poll priority. * @return the the number of stored @a Message instances with a poll priority. */ - size_t sizePoll() { return m_pollMessages.size(); } + size_t sizePoll() const { return m_pollMessages.size(); } /** * Get the next @a Message to poll. @@ -1509,23 +1492,26 @@ class MessageMap : public MappedFileReader { * Get the number of stored @a Condition instances. * @return the number of stored @a Condition instances. */ - size_t sizeConditions() { return m_conditions.size(); } + size_t sizeConditions() const { return m_conditions.size(); } /** * Get the stored @a Condition instances. * @return the @a Condition instances by filename and condition name. */ - map& getConditions() { return m_conditions; } + const map& getConditions() const { return m_conditions; } /** * Write the message definitions to the @a ostream. * @param output the @a ostream to append the formatted messages to. * @param withConditions whether to include the optional conditions prefix. */ - void dump(ostream& output, bool withConditions = false); + void dump(ostream& output, const bool withConditions = false) const; private: + /** empty vector for @a getLoadedFiles(). */ + static vector s_noFiles; + /** whether to add all messages, even if duplicate. */ const bool m_addAll; diff --git a/src/lib/ebus/symbol.cpp b/src/lib/ebus/symbol.cpp index d56c262e..bdadd89e 100644 --- a/src/lib/ebus/symbol.cpp +++ b/src/lib/ebus/symbol.cpp @@ -144,7 +144,7 @@ result_t SymbolString::parseHexEscaped(const string& str) { return inEscape ? RESULT_ERR_ESC : RESULT_OK; } -const string SymbolString::getStr(size_t skipFirstSymbols) { +const string SymbolString::getStr(size_t skipFirstSymbols) const { ostringstream sstr; for (size_t i = 0; i < m_data.size(); i++) { if (skipFirstSymbols > 0) { diff --git a/src/lib/ebus/symbol.h b/src/lib/ebus/symbol.h index ce5a317a..2ac29313 100644 --- a/src/lib/ebus/symbol.h +++ b/src/lib/ebus/symbol.h @@ -156,7 +156,7 @@ class SymbolString { * @param skipFirstSymbols the number of first symbols to skip. * @return the symbols as hex string. */ - const string getStr(size_t skipFirstSymbols = 0); + const string getStr(size_t skipFirstSymbols = 0) const; /** * Return a reference to the symbol at the specified index. @@ -245,6 +245,19 @@ class SymbolString { return m_data.size() < lengthOffset + 1 + ret ? m_data.size() - lengthOffset - 1 : ret; } + /** + * Return the data byte at the specified index (within DD). + * @param index the index of the data byte (within DD) to return. + * @return the data byte at the specified index, or 0 if not available. + */ + symbol_t dataAt(const size_t index) const { + size_t offset = (m_isMaster ? 5 : 1) + index; + if (offset < m_data.size()) { + return m_data[offset]; + } + return 0; + } + /** * Return a reference to the data byte at the specified index (within DD). * @param index the index of the data byte (within DD) to return. diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index 2edae37e..eb155f6f 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -53,7 +53,7 @@ class TestReader : public MappedFileReader { TestReader(DataFieldTemplates* templates, bool isSet, bool isMasterDest) : MappedFileReader::MappedFileReader(true), m_templates(templates), m_isSet(isSet), m_isMasterDest(isMasterDest), m_fields(NULL) {} - result_t getFieldMap(vector& row, string& errorDescription) override { + result_t getFieldMap(vector& row, string& errorDescription) const override { if (row.empty()) { row.push_back("*name"); row.push_back("part"); @@ -84,7 +84,7 @@ class TestReader : public MappedFileReader { const bool m_isSet; const bool m_isMasterDest; public: - DataField* m_fields; + const DataField* m_fields; }; @@ -509,7 +509,7 @@ int main() { string errorDescription; vector row; templates->readLineFromStream(dummystr, errorDescription, "inline", lineNo, row); - DataField* fields = NULL; + const DataField* fields = NULL; for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { string check[5] = checks[i]; istringstream isstr(check[0]); diff --git a/src/lib/ebus/test/test_filereader.cpp b/src/lib/ebus/test/test_filereader.cpp index 04c9bb0d..9542649d 100644 --- a/src/lib/ebus/test/test_filereader.cpp +++ b/src/lib/ebus/test/test_filereader.cpp @@ -82,7 +82,7 @@ class NoopReader : public FileReader { class TestReader : public MappedFileReader { public: TestReader(size_t expectedCols) : MappedFileReader::MappedFileReader(false), m_expectedCols(expectedCols) {} - result_t getFieldMap(vector& row, string& errorDescription) override { + result_t getFieldMap(vector& row, string& errorDescription) const override { if (row.size() == m_expectedCols) { cout << "get field map: split OK" << endl; return RESULT_OK;