added "-def" option to read and write commands for using inline message definition (fixes #154), added support for replacing messages during load to FileReader and subclasses, added MessageMap::remove(), prepared decode/encode commands

This commit is contained in:
john30
2018-04-08 13:48:49 +02:00
parent 8ad3ad9c5b
commit 000d68f3c2
10 changed files with 566 additions and 171 deletions
+31 -12
View File
@@ -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<string, string>* defaults, string* errorDescription) {
map<string, string>* 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;
+5 -2
View File
@@ -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<string, string>* defaults, string* errorDescription);
map<string, string>* defaults, string* errorDescription, bool replace = false);
} // namespace ebusd
+347 -122
View File
@@ -73,7 +73,7 @@ result_t UserList::getFieldMap(const string& preferLanguage, vector<string>* row
}
result_t UserList::addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) {
vector< map<string, string> >* 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<string>& args, string* user, ostring
result_t MainLoop::executeRead(const vector<string>& 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<string>& 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<string>& 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<string>& 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<string>& 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<string>& 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<Message*> 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<string>& args, const string& levels,
result_t MainLoop::executeWrite(const vector<string>& 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<string>& 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<string>& 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<Message*> 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<string>& 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<string>& args, ostringstream* ostrea
return RESULT_OK;
}
result_t MainLoop::executeDefine(const vector<string>& 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<string>& 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<string>& 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<string>& 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<string>& 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;
}
+28 -1
View File
@@ -66,7 +66,7 @@ class UserList : public UserInfo, public MappedFileReader {
// @copydoc
result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) override;
vector< map<string, string> >* 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<string>& 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<string>& 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<string>& 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<string>& 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;
+3 -3
View File
@@ -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<st
}
result_t DataFieldTemplates::addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) {
vector< map<string, string> >* 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;
}
+1 -1
View File
@@ -746,7 +746,7 @@ class DataFieldTemplates : public MappedFileReader {
// @copydoc
result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) override;
vector< map<string, string> >* subRows, string* errorDescription, bool replace) override;
/**
* Gets the template @a DataField instance with the specified name.
+9 -8
View File
@@ -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<string, string>* defaults, string* errorDescription, size_t* hash, size_t* size) {
map<string, string>* 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<string> 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<string>* row, string* errorDescription, size_t* hash, size_t* size) {
unsigned int* lineNo, vector<string>* 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<string, string>* defaults, string* errorDescription, size_t* hash, size_t* size) {
map<string, string>* 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<string>* 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<string, string>& row) {
+12 -6
View File
@@ -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<string, string>* defaults, string* errorDescription, size_t* hash = NULL, size_t* size = NULL);
map<string, string>* 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<string>* row, string* errorDescription, size_t* hash, size_t* size);
unsigned int* lineNo, vector<string>* 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<string>* 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<string, string>* defaults, string* errorDescription, size_t* hash = NULL, size_t* size = NULL) override;
map<string, string>* 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<string>* 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<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) = 0;
vector< map<string, string> >* subRows, string* errorDescription, bool replace = false) = 0;
/**
* @return a reference to all previously extracted default values by type and field name.
+107 -13
View File
@@ -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<string, string>* defaults, string* errorDescription);
map<string, string>* 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<string> 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<Message*> 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<Message*>* messages = &nameIt->second;
if (!message->isConditional() || !messages->front()->isConditional()) {
if (replace) {
vector<Message*> 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<Message*> 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<Message*> 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<string>* 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<string,
}
result_t MessageMap::readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose,
map<string, string>* defaults, string* errorDescription, size_t* hash, size_t* size) {
map<string, string>* 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<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) {
vector< map<string, string> >* 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) {
+23 -3
View File
@@ -782,6 +782,18 @@ class MessagePriorityQueue
}
priority_queue<Message*, vector<Message*>, compareMessagePriority>::push(__x);
}
/**
* Remove data from the queue.
* @param __x the element to remove.
*/
void remove(const value_type& __x) {
for (vector<Message*>::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<string>* 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<string, string>* defaults, string* errorDescription, size_t* hash = NULL, size_t* size = NULL) override;
map<string, string>* 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<string, string>* row,
vector< map<string, string> >* subRows, string* errorDescription) override;
vector< map<string, string> >* subRows, string* errorDescription, bool replace) override;
/**
* Get the scan @a Message instance for the specified address.