diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 23521281..26701774 100755 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -103,6 +103,7 @@ static struct options opt = { "", // aclFile false, // foreground false, // enableHex + false, // enableDefine PID_FILE_NAME, // pidFile 8888, // port false, // localOnly @@ -159,7 +160,8 @@ static const char argpdoc[] = #define O_ACLDEF (O_GENSYN+1) #define O_ACLFIL (O_ACLDEF+1) #define O_HEXCMD (O_ACLFIL+1) -#define O_PIDFIL (O_HEXCMD+1) +#define O_DEFCMD (O_HEXCMD+1) +#define O_PIDFIL (O_DEFCMD+1) #define O_LOCAL (O_PIDFIL+1) #define O_HTTPPT (O_LOCAL+1) #define O_HTMLPA (O_HTTPPT+1) @@ -212,6 +214,7 @@ static const struct argp_option argpoptions[] = { {"aclfile", O_ACLFIL, "FILE", 0, "Read access control list from FILE", 0 }, {"foreground", 'f', NULL, 0, "Run in foreground", 0 }, {"enablehex", O_HEXCMD, NULL, 0, "Enable hex command", 0 }, + {"enabledefine", O_DEFCMD, NULL, 0, "Enable define command", 0 }, {"pidfile", O_PIDFIL, "FILE", 0, "PID file name (only for daemon) [" PID_FILE_NAME "]", 0 }, {"port", 'p', "PORT", 0, "Listen for command line connections on PORT [8888]", 0 }, {"localhost", O_LOCAL, NULL, 0, "Listen for command line connections on 127.0.0.1 interface only", 0 }, @@ -433,6 +436,9 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) { case O_HEXCMD: // --enablehex opt->enableHex = true; break; + case O_DEFCMD: // --enabledefine + opt->enableDefine = true; + break; case O_PIDFIL: // --pidfile=/var/run/ebusd.pid if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { argp_error(state, "invalid pidfile"); @@ -802,14 +808,27 @@ static result_t collectConfigFiles(const string& relPath, const string& prefix, } DataFieldTemplates* getTemplates(const string& filename) { - string path; - size_t pos = filename.find_last_of('/'); - if (pos != string::npos) { - path = filename.substr(0, pos); - } - const auto it = s_templatesByPath.find(path); - if (it != s_templatesByPath.end()) { - return it->second; + if (filename == "*") { + unsigned long maxLength = 0; + DataFieldTemplates* best = NULL; + for (auto it : s_templatesByPath) { + if (it.first.size() > maxLength) { + best = it.second; + } + } + if (best) { + return best; + } + } else { + string path; + size_t pos = filename.find_last_of('/'); + if (pos != string::npos) { + path = filename.substr(0, pos); + } + const auto it = s_templatesByPath.find(path); + if (it != s_templatesByPath.end()) { + return it->second; + } } return &s_globalTemplates; } @@ -843,7 +862,7 @@ static bool readTemplates(const string relPath, const string extension, bool ava string logPath = relPath.empty() ? "/" : relPath; logInfo(lf_main, "reading templates %s", logPath.c_str()); string file = (relPath.empty() ? "" : relPath + "/") + "_templates" + extension; - result_t result = loadDefinitionsFromConfigPath(templates, file, verbose, NULL, &errorDescription); + result_t result = loadDefinitionsFromConfigPath(templates, file, verbose, NULL, &errorDescription, true); if (result == RESULT_OK) { logInfo(lf_main, "read templates in %s", logPath.c_str()); return true; @@ -929,7 +948,7 @@ void executeInstructions(MessageMap* messages, bool verbose) { } result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, bool verbose, - map* defaults, string* errorDescription) { + map* defaults, string* errorDescription, bool replace) { istream* stream = NULL; time_t mtime = 0; if (s_configUriPrefix.empty()) { @@ -942,7 +961,7 @@ result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filenam } result_t result; if (stream) { - result = reader->readFromStream(stream, filename, mtime, verbose, defaults, errorDescription); + result = reader->readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, replace); delete(stream); } else { result = RESULT_ERR_NOTFOUND; diff --git a/src/ebusd/main.h b/src/ebusd/main.h index 213372f8..7e018b1c 100755 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -65,6 +65,7 @@ struct options { const char* aclFile; //!< ACL file name bool foreground; //!< run in foreground bool enableHex; //!< enable hex command + bool enableDefine; //!< enable define command const char* pidFile; //!< PID file name [/var/run/ebusd.pid] uint16_t port; //!< port to listen for command line connections [8888] bool localOnly; //!< listen on 127.0.0.1 interface only @@ -88,7 +89,8 @@ struct options { /** * Get the @a DataFieldTemplates for the specified configuration file. - * @param filename the full name of the configuration file. + * @param filename the full name of the configuration file, or "*" to get the non-root templates with the longest name + * or the root templates if not available. * @return the @a DataFieldTemplates. */ DataFieldTemplates* getTemplates(const string& filename); @@ -128,10 +130,11 @@ void executeInstructions(MessageMap* messages, bool verbose = false); * @param verbose whether to verbosely log problems. * @param defaults the default values by name (potentially overwritten by file name), or NULL to not use defaults. * @param errorDescription a string in which to store the error description in case of error. + * @param replace whether to replace an already existing entry. * @return @a RESULT_OK on success, or an error code. */ result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, bool verbose, - map* defaults, string* errorDescription); + map* defaults, string* errorDescription, bool replace = false); } // namespace ebusd diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index f4e68c3a..2f40ab3f 100755 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -73,7 +73,7 @@ result_t UserList::getFieldMap(const string& preferLanguage, vector* row } result_t UserList::addFromFile(const string& filename, unsigned int lineNo, map* row, - vector< map >* subRows, string* errorDescription) { + vector< map >* subRows, string* errorDescription, bool replace) { string name = (*row)["name"]; string secret = (*row)["secret"]; if (name.empty()) { @@ -165,6 +165,7 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag } else { logError(lf_main, "error registering data handlers"); } + m_newlyDefinedMessages = opt.enableDefine ? new MessageMap(true) : NULL; } MainLoop::~MainLoop() { @@ -199,6 +200,10 @@ MainLoop::~MainLoop() { while ((msg = m_netQueue.pop()) != NULL) { delete msg; } + if (m_newlyDefinedMessages) { + delete m_newlyDefinedMessages; + m_newlyDefinedMessages = NULL; + } } /** the delay for running the update check. */ @@ -546,6 +551,19 @@ result_t MainLoop::decodeMessage(const string &data, bool isHttp, bool* connecte if (cmd == "G" || cmd == "GRAB") { return executeGrab(args, ostream); } + if (cmd == "DEFINE") { + if (m_newlyDefinedMessages) { + return executeDefine(args, ostream); + } + *ostream << "ERR: command not enabled"; + return RESULT_OK; + } + /*if (cmd == "D" || cmd == "DECODE") { + return executeDecode(args, ostream); + } + if (cmd == "E" || cmd == "ENCODE") { + return executeEncode(args, ostream); + }*/ if (cmd == "SCAN") { return executeScan(args, getUserLevels(*user), ostream); } @@ -621,7 +639,7 @@ result_t MainLoop::executeAuth(const vector& args, string* user, ostring result_t MainLoop::executeRead(const vector& args, const string& levels, ostringstream* ostream) { size_t argPos = 1; - bool hex = false, numeric = false, valueName = false; + bool hex = false, newDefinition = false, numeric = false, valueName = false; OutputFormat verbosity = 0; time_t maxAge = 5*60; string circuit, params; @@ -630,8 +648,27 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, while (args.size() > argPos && args[argPos][0] == '-') { if (args[argPos] == "-h") { hex = true; + } else if (args[argPos] == "-def") { + if (!m_newlyDefinedMessages) { + *ostream << "ERR: command not enabled"; + return RESULT_OK; + } + newDefinition = true; } else if (args[argPos] == "-f") { maxAge = 0; + } else if (args[argPos] == "-m") { + argPos++; + if (args.size() > argPos) { + result_t result; + maxAge = parseInt(args[argPos].c_str(), 10, 0, 24*60*60, &result); + if (result != RESULT_OK) { + argPos = 0; // print usage + break; + } + } else { + argPos = 0; // print usage + break; + } } else if (args[argPos] == "-v") { switch (verbosity) { case 0: @@ -653,19 +690,6 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, } else if (args[argPos] == "-N") { numeric = true; valueName = true; - } else if (args[argPos] == "-m") { - argPos++; - if (args.size() > argPos) { - result_t result; - maxAge = parseInt(args[argPos].c_str(), 10, 0, 24*60*60, &result); - if (result != RESULT_OK) { - argPos = 0; // print usage - break; - } - } else { - argPos = 0; // print usage - break; - } } else if (args[argPos] == "-c") { argPos++; if (argPos >= args.size()) { @@ -714,15 +738,50 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, } argPos++; } - if (hex && (dstAddress != SYN || !circuit.empty() || verbosity != 0 || numeric || pollPriority > 0 - || args.size() < argPos + 1)) { + if ((hex && (newDefinition || numeric || verbosity != 0 || !circuit.empty() || !params.empty() || dstAddress != SYN + || pollPriority > 0 || args.size() < argPos + 1)) + || (newDefinition && (hex || !circuit.empty() || pollPriority > 0 || args.size() != argPos + 1))) { argPos = 0; // print usage } + if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2) { + *ostream << + "usage: read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-c CIRCUIT] [-p PRIO] [-v|-V] [-n|-N] [-i VALUE[;VALUE]*]" + " NAME [FIELD[.N]]\n" + " or: read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-v|-V] [-n|-N] [-i VALUE[;VALUE]*] -def DEFINITION\n" + " or: read [-f] [-m SECONDS] [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" + " Read value(s) or hex message.\n" + " -f force reading from the bus (same as '-m 0')\n" + " -m SECONDS only return cached value if age is less than SECONDS [300]\n" + " -c CIRCUIT limit to messages of CIRCUIT\n" + " -s QQ override source address QQ\n" + " -d ZZ override destination address ZZ\n" + " -p PRIO set the message poll priority (1-9)\n" + " -v increase verbosity (include names/units/comments)\n" + " -V be very verbose (include names, units, and comments)\n" + " -n use numeric value of value=name pairs\n" + " -N use numeric and named value of value=name pairs\n" + " -i VALUE read additional message parameters from VALUE\n" + " NAME NAME of the message to send\n" + " FIELD only retrieve the field named FIELD\n" + " N only retrieve the N'th field named FIELD (0-based)\n"; + if (m_newlyDefinedMessages) { + *ostream << + " -def read with explicit message definition:\n" + " DEFINITION message definition to use instead of known definition\n"; + } + *ostream << + " -h send hex read message (or answer from cache):\n" + " ZZ destination address\n" + " PB SB primary/secondary command byte\n" + " NN number of following data bytes\n" + " Dx data byte(s) to send"; + return RESULT_OK; + } time_t now; time(&now); - if (hex && argPos > 0) { + if (hex) { MasterSymbolString master; result_t ret = parseHexMaster(args, argPos, srcAddress, &master); if (ret != RESULT_OK) { @@ -769,48 +828,22 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, } if (ret >= RESULT_OK) { logInfo(lf_main, "read hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), - result.str().c_str()); + result.str().c_str()); } else { logError(lf_main, "read hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), - getResultCode(ret)); + getResultCode(ret)); } *ostream << slave.getStr(); return RESULT_OK; } logError(lf_main, "read hex %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), - getResultCode(ret)); + getResultCode(ret)); return ret; } - if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2) { - *ostream << - "usage: read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-c CIRCUIT] [-p PRIO] [-v|-V] [-n|-N] [-i VALUE[;VALUE]*]" - " NAME [FIELD[.N]]\n" - " or: read [-f] [-m SECONDS] [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" - " Read value(s) or hex message.\n" - " -f force reading from the bus (same as '-m 0')\n" - " -m SECONDS only return cached value if age is less than SECONDS [300]\n" - " -c CIRCUIT limit to messages of CIRCUIT\n" - " -s QQ override source address QQ\n" - " -d ZZ override destination address ZZ\n" - " -p PRIO set the message poll priority (1-9)\n" - " -v increase verbosity (include names/units/comments)\n" - " -V be very verbose (include names, units, and comments)\n" - " -n use numeric value of value=name pairs\n" - " -N use numeric and named value of value=name pairs\n" - " -i VALUE read additional message parameters from VALUE\n" - " NAME NAME of the message to send\n" - " FIELD only retrieve the field named FIELD\n" - " N only retrieve the N'th field named FIELD (0-based)\n" - " -h send hex read message (or answer from cache):\n" - " ZZ destination address\n" - " PB SB primary/secondary command byte\n" - " NN number of following data bytes\n" - " Dx data byte(s) to send"; - return RESULT_OK; - } + string fieldName; ssize_t fieldIndex = -2; - if (args.size() == argPos + 2) { + if (!newDefinition && args.size() == argPos + 2) { fieldName = args[argPos + 1]; fieldIndex = -1; size_t pos = fieldName.find_last_of('.'); @@ -823,44 +856,65 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, } } - Message* message = m_messages->find(circuit, args[argPos], levels, false); + string name; + Message* message; + result_t ret; + if (newDefinition) { + time_t now; + time(&now); + string errorDescription; + istringstream istr = istringstream("#\n" + args[argPos]); // ensure first line is not used for determining col names + m_newlyDefinedMessages->clear(); + ret = m_newlyDefinedMessages->readFromStream(&istr, "temporary", now, true, NULL, &errorDescription); + if (ret != RESULT_OK) { + *ostream << "ERR: bad definition: " << errorDescription; + return RESULT_OK; + } + deque messages; + m_newlyDefinedMessages->findAll("", "", levels, false, true, false, false, true, false, 0, 0, &messages); + if (messages.empty()) { + *ostream << "ERR: bad definition: no read message"; + return RESULT_OK; + } + message = *messages.begin(); + } else { + name = args[argPos]; + message = m_messages->find(circuit, name, levels, false); + } // adjust poll priority - if (message != NULL && pollPriority > 0 && message->setPollPriority(pollPriority)) { + if (!newDefinition && message != NULL && pollPriority > 0 && message->setPollPriority(pollPriority)) { m_messages->addPollMessage(false, message); } verbosity |= valueName ? OF_VALUENAME : numeric ? OF_NUMERIC : 0; - result_t ret; - if (srcAddress == SYN && dstAddress == SYN && maxAge > 0 && params.length() == 0) { - Message* cacheMessage = m_messages->find(circuit, args[argPos], levels, false, true); - bool hasCache = cacheMessage != NULL; - if (!hasCache || (message != NULL && message->getLastUpdateTime() > cacheMessage->getLastUpdateTime())) { - cacheMessage = message; // message is newer/better - } - if (cacheMessage != NULL - && (cacheMessage->getLastUpdateTime() + maxAge > now - || (cacheMessage->isPassive() && cacheMessage->getLastUpdateTime() != 0))) { - if (verbosity & OF_NAMES) { - *ostream << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " "; - } - ret = cacheMessage->decodeLastData(false, fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex, verbosity, - ostream); - if (ret != RESULT_OK) { - if (ret < RESULT_OK) { - logError(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), - cacheMessage->getName().c_str(), getResultCode(ret)); - } - return ret; - } - logInfo(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), cacheMessage->getName().c_str(), - ostream->str().c_str()); - return RESULT_OK; - } - - if (message == NULL && hasCache) { - *ostream << "ERR: no data stored"; - return RESULT_OK; - } // else: read directly from bus + bool allowCache = !newDefinition && srcAddress == SYN && dstAddress == SYN && maxAge > 0 && params.length() == 0; + Message* cacheMessage = allowCache ? m_messages->find(circuit, name, levels, false, true) : NULL; + bool hasCache = cacheMessage != NULL; + if (!hasCache || (allowCache && message && message->getLastUpdateTime() > cacheMessage->getLastUpdateTime())) { + cacheMessage = message; // message is newer/better } + if (cacheMessage && (cacheMessage->getLastUpdateTime() + maxAge > now + || (cacheMessage->isPassive() && cacheMessage->getLastUpdateTime() != 0))) { + if (verbosity & OF_NAMES) { + *ostream << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " "; + } + ret = cacheMessage->decodeLastData(false, fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex, verbosity, + ostream); + if (ret != RESULT_OK) { + if (ret < RESULT_OK) { + logError(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), + cacheMessage->getName().c_str(), getResultCode(ret)); + } + return ret; + } + logInfo(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), cacheMessage->getName().c_str(), + ostream->str().c_str()); + return RESULT_OK; + } + + if (!message && hasCache) { + *ostream << "ERR: no data stored"; + return RESULT_OK; + } // else: read directly from bus if (message == NULL) { return RESULT_ERR_NOTFOUND; @@ -894,12 +948,18 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, result_t MainLoop::executeWrite(const vector& args, const string levels, ostringstream* ostream) { size_t argPos = 1; - bool hex = false; + bool hex = false, newDefinition = false; string circuit; symbol_t srcAddress = SYN, dstAddress = SYN; while (args.size() > argPos && args[argPos][0] == '-') { if (args[argPos] == "-h") { hex = true; + } else if (args[argPos] == "-def") { + if (!m_newlyDefinedMessages) { + *ostream << "ERR: command not enabled"; + return RESULT_OK; + } + newDefinition = true; } else if (args[argPos] == "-s" || args[argPos] == "-d") { bool dest = args[argPos] == "-d"; argPos++; @@ -931,10 +991,31 @@ result_t MainLoop::executeWrite(const vector& args, const string levels, argPos++; } - if (hex && (dstAddress != SYN || !circuit.empty() || args.size() < argPos + 1)) { + if ((hex && (newDefinition || dstAddress != SYN || !circuit.empty() || args.size() < argPos + 1)) + || (newDefinition && (hex || !circuit.empty() || args.size() < argPos + 1 || args.size() > argPos + 2))) { argPos = 0; // print usage } + if (argPos == 0 || (!newDefinition && (circuit.empty() || (args.size() != argPos + 2 && args.size() != argPos + 1)))) { + *ostream << "usage: write [-s QQ] [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" + " or: write [-s QQ] [-d ZZ] -def DEFINITION [VALUE[;VALUE]*]\n" + " or: write [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" + " Write value(s) or hex message.\n" + " -s QQ override source address QQ\n" + " -d ZZ override destination address ZZ\n" + " -c CIRCUIT CIRCUIT of the message to send\n" + " NAME NAME of the message to send\n" + " VALUE a single field VALUE\n" + " -def write with explicit message definition:\n" + " DEFINITION message definition to use instead of known definition\n" + " -h send hex write message:\n" + " ZZ destination address\n" + " PB SB primary/secondary command byte\n" + " NN number of following data bytes\n" + " Dx data byte(s) to send"; + return RESULT_OK; + } + if (hex && argPos > 0) { MasterSymbolString master; result_t ret = parseHexMaster(args, argPos, srcAddress, &master); @@ -991,23 +1072,29 @@ result_t MainLoop::executeWrite(const vector& args, const string levels, return ret; } - if (argPos == 0 || circuit.empty() || (args.size() != argPos + 2 && args.size() != argPos + 1)) { - *ostream << "usage: write [-s QQ] [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" - " or: write [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" - " Write value(s) or hex message.\n" - " -s QQ override source address QQ\n" - " -d ZZ override destination address ZZ\n" - " -c CIRCUIT CIRCUIT of the message to send\n" - " NAME NAME of the message to send\n" - " VALUE a single field VALUE\n" - " -h send hex write message:\n" - " ZZ destination address\n" - " PB SB primary/secondary command byte\n" - " NN number of following data bytes\n" - " Dx data byte(s) to send"; - return RESULT_OK; + Message* message; + result_t ret; + if (newDefinition) { + time_t now; + time(&now); + string errorDescription; + istringstream istr = istringstream("#\n" + args[argPos]); // ensure first line is not used for determining col names + m_newlyDefinedMessages->clear(); + ret = m_newlyDefinedMessages->readFromStream(&istr, "temporary", now, true, NULL, &errorDescription); + if (ret != RESULT_OK) { + *ostream << "ERR: bad definition: " << errorDescription; + return RESULT_OK; + } + deque messages; + m_newlyDefinedMessages->findAll("", "", levels, false, false, true, false, true, false, 0, 0, &messages); + if (messages.empty()) { + *ostream << "ERR: bad definition: no write message"; + return RESULT_OK; + } + message = *messages.begin(); + } else { + message = m_messages->find(circuit, args[argPos], levels, true); } - Message* message = m_messages->find(circuit, args[argPos], levels, true); if (message == NULL) { return RESULT_ERR_NOTFOUND; @@ -1016,8 +1103,7 @@ result_t MainLoop::executeWrite(const vector& args, const string levels, return RESULT_ERR_INVALID_ADDR; } // allow missing values - result_t ret = m_busHandler->readFromBus(message, args.size() == argPos + 1 ? "" : args[argPos + 1], dstAddress, - srcAddress); + ret = m_busHandler->readFromBus(message, args.size() == argPos + 1 ? "" : args[argPos + 1], dstAddress, srcAddress); if (ret != RESULT_OK) { logError(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); @@ -1385,6 +1471,139 @@ result_t MainLoop::executeGrab(const vector& args, ostringstream* ostrea return RESULT_OK; } +result_t MainLoop::executeDefine(const vector& args, ostringstream* ostream) { + size_t argPos = 1; + bool replace = false; + while (args.size() > argPos && args[argPos][0] == '-') { + if (args[argPos] == "-r") { + replace = true; + } else { + argPos = 0; // print usage + break; + } + argPos++; + } + + if (argPos == 0 || args.size() != argPos + 1) { + *ostream << + "usage: define [-r] DEFINITION\n" + " Define a new message.\n" + " -r replace an already existing definition\n" + " DEFINITION message definition to add"; + return RESULT_OK; + } + + time_t now; + time(&now); + string errorDescription; + istringstream istr = istringstream("#\n" + args[argPos]); // ensure first line is not used for determining col names + return m_messages->readFromStream(&istr, "temporary", now, true, NULL, &errorDescription, replace); +} + + +/*result_t MainLoop::executeDecode(const vector& args, ostringstream* ostream) { + size_t argPos = 1; + bool numeric = false, valueName = false; + OutputFormat verbosity = 0; + while (args.size() > argPos && args[argPos][0] == '-') { + if (args[argPos] == "-v") { + switch (verbosity) { + case 0: + verbosity = OF_NAMES; + break; + case OF_NAMES: + verbosity |= OF_UNITS; + break; + case OF_NAMES|OF_UNITS: + verbosity |= OF_COMMENTS; + break; + } + } else if (args[argPos] == "-vv") { + verbosity |= OF_NAMES|OF_UNITS; + } else if (args[argPos] == "-vvv" || args[argPos] == "-V") { + verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; + } else if (args[argPos] == "-n") { + numeric = true; + } else if (args[argPos] == "-N") { + numeric = true; + valueName = true; + } else { + argPos = 0; // print usage + break; + } + argPos++; + } + if (args.size() < argPos + 2) { + argPos = 0; // print usage + } + + if (argPos == 0 || args.size() != argPos + 2) { + *ostream << + "usage: decode [-v|-V] [-n|-N] DEFINITION Dx\n" + " Decode a field by definition and hex data.\n" + " -v increase verbosity (include names/units/comments)\n" + " -V be very verbose (include names, units, and comments)\n" + " -n use numeric value of value=name pairs\n" + " -N use numeric and named value of value=name pairs\n" + " DEFINITION field definition (type,divisor/values,unit,comment)\n" + " Dx data byte(s) to decode"; + return RESULT_OK; + } + + time_t now; + time(&now); + verbosity |= valueName ? OF_VALUENAME : numeric ? OF_NUMERIC : 0; + istringstream istr = istringstream("#\n" + args[argPos]); // ensure first line is not used for determining col names + string errorDescription; + DataFieldTemplates* templates = getTemplates("*"); + LoadableDataFieldSet fields("", templates); + result_t ret = fields.readFromStream(&istr, "temporary", now, true, NULL, &errorDescription); + if (ret != RESULT_OK) { + return ret; + } + SlaveSymbolString slave; + slave.push_back(0); // dummy length + ret = slave.parseHex(args[argPos+1]); + if (ret != RESULT_OK) { + return ret; + } + slave[0] = slave.size() - 1; // adjust length + return fields.read(slave, 0, false, NULL, -1, verbosity, -1, ostream); +} + + +result_t MainLoop::executeEncode(const vector& args, ostringstream* ostream) { + size_t argPos = 1; + if (argPos == 0 || args.size() != argPos + 2) { + *ostream << + "usage: encode DEFINITION VALUE\n" + " Encode a field by definition and decoded value.\n" + " DEFINITION field definition (type,divisor/values,unit,comment)\n" + " VALUE single field VALUE to encode"; + return RESULT_OK; + } + + time_t now; + time(&now); + istringstream istr = istringstream("#\n" + args[argPos]); // ensure first line is not used for determining col names + string errorDescription; + DataFieldTemplates* templates = getTemplates("*"); + LoadableDataFieldSet fields("", templates); + result_t ret = fields.readFromStream(&istr, "temporary", now, true, NULL, &errorDescription); + if (ret != RESULT_OK) { + return ret; + } + istr = istringstream(args[argPos+1]); + SlaveSymbolString slave; + ret = fields.write(FIELD_SEPARATOR, 0, &istr, &slave, NULL); + if (ret != RESULT_OK) { + return ret; + } + *ostream << slave.getStr(1); + return ret; +}*/ + + result_t MainLoop::executeScan(const vector& args, const string& levels, ostringstream* ostream) { if (args.size() == 1) { result_t result = m_busHandler->startScan(false, levels); @@ -1564,28 +1783,34 @@ result_t MainLoop::executeQuit(const vector& args, bool *connected, ostr result_t MainLoop::executeHelp(ostringstream* ostream) { *ostream << "usage:\n" - " read|r Read value(s): read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-c CIRCUIT] [-p PRIO] [-v|-V] [-n|-N]" + " read|r Read value(s): read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-c CIRCUIT] [-p PRIO] [-v|-V] [-n|-N]" " [-i VALUE[;VALUE]*] NAME [FIELD[.N]]\n" - " Read hex message: read [-f] [-m SECONDS] [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" - " write|w Write value(s): write [-s QQ] [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" - " Write hex message: write [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" - " auth|a Authenticate user: auth USER SECRET\n" - " hex Send hex data: hex [-s QQ] ZZPBSBNNDx\n" - " find|f Find message(s): find [-v|-V] [-r] [-w] [-p] [-a] [-d] [-h] [-i ID] [-f] [-F COL[,COL]*] [-e]" + " Read by new defintion: read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-v|-V] [-n|-N]" + " [-i VALUE[;VALUE]*] -def DEFINITION\n" + " Read hex message: read [-f] [-m SECONDS] [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" + " write|w Write value(s): write [-s QQ] [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" + " Write by new def.: write [-s QQ] [-d ZZ] -def DEFINITION [VALUE[;VALUE]*]\n" + " Write hex message: write [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" + " auth|a Authenticate user: auth USER SECRET\n" + " hex Send hex data: hex [-s QQ] ZZPBSBNNDx\n" + " find|f Find message(s): find [-v|-V] [-r] [-w] [-p] [-a] [-d] [-h] [-i ID] [-f] [-F COL[,COL]*] [-e]" " [-c CIRCUIT] [-l LEVEL] [NAME]\n" - " listen|l Listen for updates: listen [stop]\n" - " state|s Report bus state\n" - " info|i Report information about the daemon, the configuration, and seen devices.\n" - " grab|g Grab messages: grab [stop]\n" - " Report the messages: grab result [all]\n" - " scan Scan slaves: scan [full|ZZ]\n" - " Report scan result: scan result\n" - " log Set log area level: log [AREA[,AREA]* LEVEL]\n" - " raw Toggle logging of messages or each byte.\n" - " dump Toggle binary dump of received bytes\n" - " reload Reload CSV config files\n" - " quit|q Close connection\n" - " help|? Print help help [COMMAND], COMMMAND ?"; + " listen|l Listen for updates: listen [stop]\n" + " state|s Report bus state\n" + " info|i Report information about the daemon, the configuration, and seen devices.\n" + " grab|g Grab messages: grab [stop]\n" + " Report the messages: grab result [all]\n" + " define Define new message: define [-r] DEFINITION\n" + //" decode|d Decode a field: decode [-v|-V] [-n|-N] DEFINITION Dx\n" + //" encode|e Encode a field: encode DEFINITION VALUE\n" + " scan Scan slaves: scan [full|ZZ]\n" + " Report scan result: scan result\n" + " log Set log area level: log [AREA[,AREA]* LEVEL]\n" + " raw Toggle logging of messages or each byte.\n" + " dump Toggle binary dump of received bytes\n" + " reload Reload CSV config files\n" + " quit|q Close connection\n" + " help|? Print help help [COMMAND], COMMMAND ?"; return RESULT_OK; } diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 7237479c..a8f4a737 100755 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -66,7 +66,7 @@ class UserList : public UserInfo, public MappedFileReader { // @copydoc result_t addFromFile(const string& filename, unsigned int lineNo, map* row, - vector< map >* subRows, string* errorDescription) override; + vector< map >* subRows, string* errorDescription, bool replace) override; // @copydoc bool hasUser(const string& user) const override { @@ -240,6 +240,30 @@ class MainLoop : public Thread, DeviceListener { */ result_t executeGrab(const vector& args, ostringstream* ostream); + /** + * Execute the define command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param ostream the @a ostringstream to format the result string to. + * @return the result code. + */ + result_t executeDefine(const vector& args, ostringstream* ostream); + + /** + * Execute the decode command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param ostream the @a ostringstream to format the result string to. + * @return the result code. + */ + //result_t executeDecode(const vector& args, ostringstream* ostream); + + /** + * Execute the encode command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param ostream the @a ostringstream to format the result string to. + * @return the result code. + */ + //result_t executeEncode(const vector& args, ostringstream* ostream); + /** * Execute the scan command. * @param args the arguments passed to the command (starting with the command itself), or empty for help. @@ -373,6 +397,9 @@ class MainLoop : public Thread, DeviceListener { /** whether to enable the hex command. */ const bool m_enableHex; + /** the MessageMap for handling newly defined messages for testing (if enabled), or NULL. */ + MessageMap* m_newlyDefinedMessages; + /** set to true to shutdown. */ bool m_shutdown; diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 08872efe..85cebc85 100755 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -1130,7 +1130,7 @@ result_t DataFieldSet::write(char separator, size_t offset, istringstream* input DataFieldTemplates::DataFieldTemplates(const DataFieldTemplates& other) - : MappedFileReader::MappedFileReader(false) { +: MappedFileReader::MappedFileReader(false) { for (const auto it : other.m_fieldsByName) { m_fieldsByName[it.first] = it.second->clone(); } @@ -1250,7 +1250,7 @@ result_t DataFieldTemplates::getFieldMap(const string& preferLanguage, vector* row, - vector< map >* subRows, string* errorDescription) { + vector< map >* subRows, string* errorDescription, bool replace) { string name = (*row)["name"]; // required string firstFieldName; size_t colon = name.find(':'); @@ -1271,7 +1271,7 @@ result_t DataFieldTemplates::addFromFile(const string& filename, unsigned int li if (result != RESULT_OK) { return result; } - result = add(field, name, true); + result = add(field, name, replace); if (result == RESULT_ERR_DUPLICATE_NAME) { *errorDescription = name; } diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index 77f47037..4fc65bcd 100755 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -746,7 +746,7 @@ class DataFieldTemplates : public MappedFileReader { // @copydoc result_t addFromFile(const string& filename, unsigned int lineNo, map* row, - vector< map >* subRows, string* errorDescription) override; + vector< map >* subRows, string* errorDescription, bool replace) override; /** * Gets the template @a DataField instance with the specified name. diff --git a/src/lib/ebus/filereader.cpp b/src/lib/ebus/filereader.cpp index 702f4bfb..47474672 100755 --- a/src/lib/ebus/filereader.cpp +++ b/src/lib/ebus/filereader.cpp @@ -60,7 +60,7 @@ istream* FileReader::openFile(const string& filename, string* errorDescription, } result_t FileReader::readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, - map* defaults, string* errorDescription, size_t* hash, size_t* size) { + map* defaults, string* errorDescription, bool replace, size_t* hash, size_t* size) { if (hash) { *hash = 0; } @@ -71,20 +71,20 @@ result_t FileReader::readFromStream(istream* stream, const string& filename, con vector row; result_t result = RESULT_OK; while (stream->peek() != EOF && result == RESULT_OK) { - result = readLineFromStream(stream, filename, verbose, &lineNo, &row, errorDescription, hash, size); + result = readLineFromStream(stream, filename, verbose, &lineNo, &row, errorDescription, replace, hash, size); } return result; } result_t FileReader::readLineFromStream(istream* stream, const string& filename, bool verbose, - unsigned int* lineNo, vector* row, string* errorDescription, size_t* hash, size_t* size) { + unsigned int* lineNo, vector* row, string* errorDescription, bool replace, size_t* hash, size_t* size) { result_t result; if (!splitFields(stream, row, lineNo, hash, size)) { *errorDescription = "blank line"; result = RESULT_ERR_EOF; } else { *errorDescription = ""; - result = addFromFile(filename, *lineNo, row, errorDescription); + result = addFromFile(filename, *lineNo, row, errorDescription, replace); } if (result != RESULT_OK) { if (!errorDescription->empty()) { @@ -242,7 +242,7 @@ const string MappedFileReader::normalizeLanguage(const string& lang) { } result_t MappedFileReader::readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, - map* defaults, string* errorDescription, size_t* hash, size_t* size) { + map* defaults, string* errorDescription, bool replace, size_t* hash, size_t* size) { m_mutex.lock(); m_columnNames.clear(); m_lastDefaults.clear(); @@ -253,13 +253,14 @@ result_t MappedFileReader::readFromStream(istream* stream, const string& filenam size_t lastSep = filename.find_last_of('/'); string defaultsPart = lastSep == string::npos ? filename : filename.substr(lastSep+1); extractDefaultsFromFilename(defaultsPart, &m_lastDefaults[""]); - result_t result = FileReader::readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, hash, size); + result_t result + = FileReader::readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, replace, hash, size); m_mutex.unlock(); return result; } result_t MappedFileReader::addFromFile(const string& filename, unsigned int lineNo, vector* row, - string* errorDescription) { + string* errorDescription, bool replace) { result_t result; if (lineNo == 1) { // first line defines column names result = getFieldMap(m_preferLanguage, row, errorDescription); @@ -325,7 +326,7 @@ result_t MappedFileReader::addFromFile(const string& filename, unsigned int line if (isDefault) { return addDefaultFromFile(filename, lineNo, &rowMapped, &subRowsMapped, errorDescription); } - return addFromFile(filename, lineNo, &rowMapped, &subRowsMapped, errorDescription); + return addFromFile(filename, lineNo, &rowMapped, &subRowsMapped, errorDescription, replace); } const string MappedFileReader::combineRow(const map& row) { diff --git a/src/lib/ebus/filereader.h b/src/lib/ebus/filereader.h index 0a5cc18f..2a342795 100755 --- a/src/lib/ebus/filereader.h +++ b/src/lib/ebus/filereader.h @@ -92,12 +92,14 @@ class FileReader { * @param verbose whether to verbosely log problems. * @param defaults the default values by name (potentially overwritten by file name), or NULL to not use defaults. * @param errorDescription a string in which to store the error description in case of error. + * @param replace whether to replace an already existing entry. * @param hash optional pointer to a @a size_t value for storing the hash of the file, or NULL. * @param size optional pointer to a @a size_t value for storing the normalized size of the file, or NULL. * @return @a RESULT_OK on success, or an error code. */ virtual result_t readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, - map* defaults, string* errorDescription, size_t* hash = NULL, size_t* size = NULL); + map* defaults, string* errorDescription, bool replace = false, size_t* hash = NULL, + size_t* size = NULL); /** * Read a single line definition from the stream. @@ -107,12 +109,13 @@ class FileReader { * @param lineNo the last line number (incremented with each line read). * @param row the definition row to clear and update with the read data (for performance reasons only). * @param errorDescription a string in which to store the error description in case of error. + * @param replace whether to replace an already existing entry. * @param hash optional pointer to a @a size_t value for updating with the hash of the line, or NULL. * @param size optional pointer to a @a size_t value for updating with the normalized length of the line, or NULL. * @return @a RESULT_OK on success, or an error code. */ virtual result_t readLineFromStream(istream* stream, const string& filename, bool verbose, - unsigned int* lineNo, vector* row, string* errorDescription, size_t* hash, size_t* size); + unsigned int* lineNo, vector* row, string* errorDescription, bool replace, size_t* hash, size_t* size); /** * Add a definition that was read from a file. @@ -120,10 +123,11 @@ class FileReader { * @param lineNo the current line number in the file being read. * @param row the definition row (allowed to be modified). * @param errorDescription a string in which to store the error description in case of error. + * @param replace whether to replace an already existing entry. * @return @a RESULT_OK on success, or an error code. */ virtual result_t addFromFile(const string& filename, unsigned int lineNo, vector* row, - string* errorDescription) = 0; + string* errorDescription, bool replace) = 0; /** * Left and right trim the string. @@ -205,7 +209,8 @@ class MappedFileReader : public FileReader { // @copydoc result_t readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, - map* defaults, string* errorDescription, size_t* hash = NULL, size_t* size = NULL) override; + map* defaults, string* errorDescription, bool replace = false, size_t* hash = NULL, + size_t* size = NULL) override; /** * Extract default values from the file name. @@ -223,7 +228,7 @@ class MappedFileReader : public FileReader { // @copydoc result_t addFromFile(const string& filename, unsigned int lineNo, vector* row, - string* errorDescription) override; + string* errorDescription, bool replace) override; /** * Get the field mapping from the given first line. @@ -258,10 +263,11 @@ class MappedFileReader : public FileReader { * @param row the main definition row by field name (may be modified). * @param subRows the sub definition rows, each by field name (may be modified). * @param errorDescription a string in which to store the error description in case of error. + * @param replace whether to replace an already existing entry. * @return @a RESULT_OK on success, or an error code. */ virtual result_t addFromFile(const string& filename, unsigned int lineNo, map* row, - vector< map >* subRows, string* errorDescription) = 0; + vector< map >* subRows, string* errorDescription, bool replace = false) = 0; /** * @return a reference to all previously extracted default values by type and field name. diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index b5d8cdca..3bf81e6c 100755 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -82,7 +82,7 @@ static const char* defaultMessageFieldMap[] = { // access level not included in extern DataFieldTemplates* getTemplates(const string& filename); extern result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, bool verbose, - map* defaults, string* errorDescription); + map* defaults, string* errorDescription, bool replace = false); Message::Message(const string& circuit, const string& level, const string& name, @@ -1733,22 +1733,35 @@ result_t LoadInstruction::execute(MessageMap* messages, ostringstream* log) { vector MessageMap::s_noFiles; -result_t MessageMap::add(bool storeByName, Message* message) { +result_t MessageMap::add(bool storeByName, Message* message, bool replace) { uint64_t key = message->getKey(); bool conditional = message->isConditional(); if (!m_addAll) { + lock(); const auto keyIt = m_messagesByKey.find(key); if (keyIt != m_messagesByKey.end()) { - Message* other = getFirstAvailable(keyIt->second, message); - if (other != NULL) { - if (!conditional) { - return RESULT_ERR_DUPLICATE; // duplicate key + if (replace) { + vector removeMessages; + for (auto other : keyIt->second) { + if (!other || !message->checkId(*other)) { + continue; + } + if (!conditional || !other->isConditional() || other->m_condition == message->m_condition) { + removeMessages.push_back(other); + } } - if (!other->isConditional()) { + for (auto other : removeMessages) { + remove(other); + } + } else { + Message *other = getFirstAvailable(keyIt->second, message); + if (other != NULL && (!conditional || !other->isConditional())) { + unlock(); return RESULT_ERR_DUPLICATE; // duplicate key } } } + unlock(); } bool isPassive = message->isPassive(); if (storeByName) { @@ -1763,13 +1776,29 @@ result_t MessageMap::add(bool storeByName, Message* message) { string suffix = FIELD_SEPARATOR + name + (isPassive ? "P" : (isWrite ? "W" : "R")); string nameKey = circuit + suffix; if (!m_addAll) { + lock(); const auto nameIt = m_messagesByName.find(nameKey); if (nameIt != m_messagesByName.end()) { vector* messages = &nameIt->second; - if (!message->isConditional() || !messages->front()->isConditional()) { + if (replace) { + vector removeMessages; + for (auto other : *messages) { + if (!other) { + continue; + } + if (!conditional || !other->isConditional() || other->m_condition == message->m_condition) { + removeMessages.push_back(other); + } + } + for (auto other : removeMessages) { + remove(other); + } + } else if (!conditional || !messages->front()->isConditional()) { + unlock(); return RESULT_ERR_DUPLICATE_NAME; // duplicate key } } + unlock(); } m_messagesByName[nameKey].push_back(message); nameKey = suffix; // also store without circuit @@ -1808,6 +1837,71 @@ result_t MessageMap::add(bool storeByName, Message* message) { return RESULT_OK; } +void MessageMap::remove(Message* message) { + if (message == NULL) { + return; + } + lock(); + uint64_t key = message->getKey(); + bool conditional = message->isConditional(); + const auto keyIt = m_messagesByKey.find(key); + bool deleted = false; + if (keyIt != m_messagesByKey.end()) { + vector messages = keyIt->second; + for (auto it = messages.begin(); it != messages.end(); ) { + Message* other = *it; + if (other == message) { + if (!deleted) { + deleted = true; + delete(other); + } + messages.erase(it); + } else { + ++it; + } + } + if (messages.empty()) { + m_messagesByKey.erase(keyIt); + } + } + bool storedByName = false; + for (auto nameIt = m_messagesByName.begin(); nameIt != m_messagesByName.end(); ) { + vector messages = nameIt->second; + for (auto it = messages.begin(); it != messages.end(); ) { + Message* other = *it; + if (other == message) { + storedByName = true; + if (!deleted) { + deleted = true; + delete(other); + } + messages.erase(it); + } else { + ++it; + } + } + if (messages.empty()) { + m_messagesByName.erase(nameIt); + } else { + ++nameIt; + } + } + if (storedByName) { + bool isPassive = message->isPassive(); + m_messageCount--; + if (conditional) { + m_conditionalMessageCount--; + } + if (isPassive) { + m_passiveMessageCount--; + } + } + if (message->getPollPriority() > 0) { + m_pollMessages.remove(message); + } + unlock(); +} + result_t MessageMap::getFieldMap(const string& preferLanguage, vector* row, string* errorDescription) const { // type,circuit,name,[comment],[QQ],ZZ,PBSB,[ID],*name,[part],type,divisor/values,unit,comment // minimum: type,name,PBSB,*type @@ -2105,7 +2199,7 @@ bool MessageMap::extractDefaultsFromFilename(const string& filename, map* defaults, string* errorDescription, size_t* hash, size_t* size) { + map* defaults, string* errorDescription, bool replace, size_t* hash, size_t* size) { size_t localHash, localSize; if (!hash) { hash = &localHash; @@ -2113,8 +2207,8 @@ result_t MessageMap::readFromStream(istream* stream, const string& filename, con if (!size) { size = &localSize; } - result_t result = MappedFileReader::readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, hash, - size); + result_t result + = MappedFileReader::readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, replace, hash, size); if (defaults) { string circuit = AttributedItem::pluck("circuit", defaults); if (!circuit.empty() && m_circuitData.find(circuit) == m_circuitData.end()) { @@ -2134,7 +2228,7 @@ result_t MessageMap::readFromStream(istream* stream, const string& filename, con } result_t MessageMap::addFromFile(const string& filename, unsigned int lineNo, map* row, - vector< map >* subRows, string* errorDescription) { + vector< map >* subRows, string* errorDescription, bool replace) { Condition* condition = NULL; string types = AttributedItem::pluck("type", row); result_t result = readConditions(filename, &types, errorDescription, &condition); @@ -2188,7 +2282,7 @@ result_t MessageMap::addFromFile(const string& filename, unsigned int lineNo, ma } for (const auto message : messages) { if (result == RESULT_OK) { - result = add(true, message); + result = add(true, message, replace); if (result == RESULT_ERR_DUPLICATE_NAME) { *errorDescription = "invalid name"; } else if (result == RESULT_ERR_DUPLICATE) { diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 150e87b0..39fceb2e 100755 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -782,6 +782,18 @@ class MessagePriorityQueue } priority_queue, compareMessagePriority>::push(__x); } + /** + * Remove data from the queue. + * @param __x the element to remove. + */ + void remove(const value_type& __x) { + for (vector::iterator it = c.begin(); it != c.end(); it++) { + if (*it == __x) { + c.erase(it); + break; + } + } + } }; @@ -1233,10 +1245,17 @@ class MessageMap : public MappedFileReader { * Add a @a Message instance to this set. * @param message the @a Message instance to add. * @param storeByName whether to store the @a Message by name. + * @param replace whether to replace an already existing entry. * @return @a RESULT_OK on success, or an error code. * Note: the caller may not free the added instance on success. */ - result_t add(bool storeByName, Message* message); + result_t add(bool storeByName, Message* message, bool replace = false); + + /** + * Remove a previously added @a Message. + * @param message the @a Message to remove. + */ + void remove(Message* message); // @copydoc result_t getFieldMap(const string& preferLanguage, vector* row, string* errorDescription) const override; @@ -1261,11 +1280,12 @@ class MessageMap : public MappedFileReader { // @copydoc result_t readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, - map* defaults, string* errorDescription, size_t* hash = NULL, size_t* size = NULL) override; + map* defaults, string* errorDescription, bool replace = false, size_t* hash = NULL, + size_t* size = NULL) override; // @copydoc result_t addFromFile(const string& filename, unsigned int lineNo, map* row, - vector< map >* subRows, string* errorDescription) override; + vector< map >* subRows, string* errorDescription, bool replace) override; /** * Get the scan @a Message instance for the specified address.