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
+23 -4
View File
@@ -103,6 +103,7 @@ static struct options opt = {
"", // aclFile "", // aclFile
false, // foreground false, // foreground
false, // enableHex false, // enableHex
false, // enableDefine
PID_FILE_NAME, // pidFile PID_FILE_NAME, // pidFile
8888, // port 8888, // port
false, // localOnly false, // localOnly
@@ -159,7 +160,8 @@ static const char argpdoc[] =
#define O_ACLDEF (O_GENSYN+1) #define O_ACLDEF (O_GENSYN+1)
#define O_ACLFIL (O_ACLDEF+1) #define O_ACLFIL (O_ACLDEF+1)
#define O_HEXCMD (O_ACLFIL+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_LOCAL (O_PIDFIL+1)
#define O_HTTPPT (O_LOCAL+1) #define O_HTTPPT (O_LOCAL+1)
#define O_HTMLPA (O_HTTPPT+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 }, {"aclfile", O_ACLFIL, "FILE", 0, "Read access control list from FILE", 0 },
{"foreground", 'f', NULL, 0, "Run in foreground", 0 }, {"foreground", 'f', NULL, 0, "Run in foreground", 0 },
{"enablehex", O_HEXCMD, NULL, 0, "Enable hex command", 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 }, {"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 }, {"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 }, {"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 case O_HEXCMD: // --enablehex
opt->enableHex = true; opt->enableHex = true;
break; break;
case O_DEFCMD: // --enabledefine
opt->enableDefine = true;
break;
case O_PIDFIL: // --pidfile=/var/run/ebusd.pid case O_PIDFIL: // --pidfile=/var/run/ebusd.pid
if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid pidfile"); argp_error(state, "invalid pidfile");
@@ -802,6 +808,18 @@ static result_t collectConfigFiles(const string& relPath, const string& prefix,
} }
DataFieldTemplates* getTemplates(const string& filename) { DataFieldTemplates* getTemplates(const string& filename) {
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; string path;
size_t pos = filename.find_last_of('/'); size_t pos = filename.find_last_of('/');
if (pos != string::npos) { if (pos != string::npos) {
@@ -811,6 +829,7 @@ DataFieldTemplates* getTemplates(const string& filename) {
if (it != s_templatesByPath.end()) { if (it != s_templatesByPath.end()) {
return it->second; return it->second;
} }
}
return &s_globalTemplates; return &s_globalTemplates;
} }
@@ -843,7 +862,7 @@ static bool readTemplates(const string relPath, const string extension, bool ava
string logPath = relPath.empty() ? "/" : relPath; string logPath = relPath.empty() ? "/" : relPath;
logInfo(lf_main, "reading templates %s", logPath.c_str()); logInfo(lf_main, "reading templates %s", logPath.c_str());
string file = (relPath.empty() ? "" : relPath + "/") + "_templates" + extension; 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) { if (result == RESULT_OK) {
logInfo(lf_main, "read templates in %s", logPath.c_str()); logInfo(lf_main, "read templates in %s", logPath.c_str());
return true; return true;
@@ -929,7 +948,7 @@ void executeInstructions(MessageMap* messages, bool verbose) {
} }
result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, 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; istream* stream = NULL;
time_t mtime = 0; time_t mtime = 0;
if (s_configUriPrefix.empty()) { if (s_configUriPrefix.empty()) {
@@ -942,7 +961,7 @@ result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filenam
} }
result_t result; result_t result;
if (stream) { if (stream) {
result = reader->readFromStream(stream, filename, mtime, verbose, defaults, errorDescription); result = reader->readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, replace);
delete(stream); delete(stream);
} else { } else {
result = RESULT_ERR_NOTFOUND; result = RESULT_ERR_NOTFOUND;
+5 -2
View File
@@ -65,6 +65,7 @@ struct options {
const char* aclFile; //!< ACL file name const char* aclFile; //!< ACL file name
bool foreground; //!< run in foreground bool foreground; //!< run in foreground
bool enableHex; //!< enable hex command bool enableHex; //!< enable hex command
bool enableDefine; //!< enable define command
const char* pidFile; //!< PID file name [/var/run/ebusd.pid] const char* pidFile; //!< PID file name [/var/run/ebusd.pid]
uint16_t port; //!< port to listen for command line connections [8888] uint16_t port; //!< port to listen for command line connections [8888]
bool localOnly; //!< listen on 127.0.0.1 interface only 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. * 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. * @return the @a DataFieldTemplates.
*/ */
DataFieldTemplates* getTemplates(const string& filename); DataFieldTemplates* getTemplates(const string& filename);
@@ -128,10 +130,11 @@ void executeInstructions(MessageMap* messages, bool verbose = false);
* @param verbose whether to verbosely log problems. * @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 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 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. * @return @a RESULT_OK on success, or an error code.
*/ */
result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, 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 = false);
} // namespace ebusd } // namespace ebusd
+300 -75
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, 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 name = (*row)["name"];
string secret = (*row)["secret"]; string secret = (*row)["secret"];
if (name.empty()) { if (name.empty()) {
@@ -165,6 +165,7 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag
} else { } else {
logError(lf_main, "error registering data handlers"); logError(lf_main, "error registering data handlers");
} }
m_newlyDefinedMessages = opt.enableDefine ? new MessageMap(true) : NULL;
} }
MainLoop::~MainLoop() { MainLoop::~MainLoop() {
@@ -199,6 +200,10 @@ MainLoop::~MainLoop() {
while ((msg = m_netQueue.pop()) != NULL) { while ((msg = m_netQueue.pop()) != NULL) {
delete msg; delete msg;
} }
if (m_newlyDefinedMessages) {
delete m_newlyDefinedMessages;
m_newlyDefinedMessages = NULL;
}
} }
/** the delay for running the update check. */ /** 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") { if (cmd == "G" || cmd == "GRAB") {
return executeGrab(args, ostream); 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") { if (cmd == "SCAN") {
return executeScan(args, getUserLevels(*user), ostream); 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) { result_t MainLoop::executeRead(const vector<string>& args, const string& levels, ostringstream* ostream) {
size_t argPos = 1; size_t argPos = 1;
bool hex = false, numeric = false, valueName = false; bool hex = false, newDefinition = false, numeric = false, valueName = false;
OutputFormat verbosity = 0; OutputFormat verbosity = 0;
time_t maxAge = 5*60; time_t maxAge = 5*60;
string circuit, params; 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] == '-') { while (args.size() > argPos && args[argPos][0] == '-') {
if (args[argPos] == "-h") { if (args[argPos] == "-h") {
hex = true; 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") { } else if (args[argPos] == "-f") {
maxAge = 0; 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") { } else if (args[argPos] == "-v") {
switch (verbosity) { switch (verbosity) {
case 0: case 0:
@@ -653,19 +690,6 @@ result_t MainLoop::executeRead(const vector<string>& args, const string& levels,
} else if (args[argPos] == "-N") { } else if (args[argPos] == "-N") {
numeric = true; numeric = true;
valueName = 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") { } else if (args[argPos] == "-c") {
argPos++; argPos++;
if (argPos >= args.size()) { if (argPos >= args.size()) {
@@ -714,15 +738,50 @@ result_t MainLoop::executeRead(const vector<string>& args, const string& levels,
} }
argPos++; argPos++;
} }
if (hex && (dstAddress != SYN || !circuit.empty() || verbosity != 0 || numeric || pollPriority > 0 if ((hex && (newDefinition || numeric || verbosity != 0 || !circuit.empty() || !params.empty() || dstAddress != SYN
|| args.size() < argPos + 1)) { || pollPriority > 0 || args.size() < argPos + 1))
|| (newDefinition && (hex || !circuit.empty() || pollPriority > 0 || args.size() != argPos + 1))) {
argPos = 0; // print usage 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_t now;
time(&now); time(&now);
if (hex && argPos > 0) { if (hex) {
MasterSymbolString master; MasterSymbolString master;
result_t ret = parseHexMaster(args, argPos, srcAddress, &master); result_t ret = parseHexMaster(args, argPos, srcAddress, &master);
if (ret != RESULT_OK) { if (ret != RESULT_OK) {
@@ -781,36 +840,10 @@ result_t MainLoop::executeRead(const vector<string>& args, const string& levels,
getResultCode(ret)); getResultCode(ret));
return 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; string fieldName;
ssize_t fieldIndex = -2; ssize_t fieldIndex = -2;
if (args.size() == argPos + 2) { if (!newDefinition && args.size() == argPos + 2) {
fieldName = args[argPos + 1]; fieldName = args[argPos + 1];
fieldIndex = -1; fieldIndex = -1;
size_t pos = fieldName.find_last_of('.'); size_t pos = fieldName.find_last_of('.');
@@ -823,21 +856,43 @@ 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 // 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); m_messages->addPollMessage(false, message);
} }
verbosity |= valueName ? OF_VALUENAME : numeric ? OF_NUMERIC : 0; verbosity |= valueName ? OF_VALUENAME : numeric ? OF_NUMERIC : 0;
result_t ret; bool allowCache = !newDefinition && srcAddress == SYN && dstAddress == SYN && maxAge > 0 && params.length() == 0;
if (srcAddress == SYN && dstAddress == SYN && maxAge > 0 && params.length() == 0) { Message* cacheMessage = allowCache ? m_messages->find(circuit, name, levels, false, true) : NULL;
Message* cacheMessage = m_messages->find(circuit, args[argPos], levels, false, true);
bool hasCache = cacheMessage != NULL; bool hasCache = cacheMessage != NULL;
if (!hasCache || (message != NULL && message->getLastUpdateTime() > cacheMessage->getLastUpdateTime())) { if (!hasCache || (allowCache && message && message->getLastUpdateTime() > cacheMessage->getLastUpdateTime())) {
cacheMessage = message; // message is newer/better cacheMessage = message; // message is newer/better
} }
if (cacheMessage != NULL if (cacheMessage && (cacheMessage->getLastUpdateTime() + maxAge > now
&& (cacheMessage->getLastUpdateTime() + maxAge > now
|| (cacheMessage->isPassive() && cacheMessage->getLastUpdateTime() != 0))) { || (cacheMessage->isPassive() && cacheMessage->getLastUpdateTime() != 0))) {
if (verbosity & OF_NAMES) { if (verbosity & OF_NAMES) {
*ostream << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " "; *ostream << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " ";
@@ -856,11 +911,10 @@ result_t MainLoop::executeRead(const vector<string>& args, const string& levels,
return RESULT_OK; return RESULT_OK;
} }
if (message == NULL && hasCache) { if (!message && hasCache) {
*ostream << "ERR: no data stored"; *ostream << "ERR: no data stored";
return RESULT_OK; return RESULT_OK;
} // else: read directly from bus } // else: read directly from bus
}
if (message == NULL) { if (message == NULL) {
return RESULT_ERR_NOTFOUND; 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) { result_t MainLoop::executeWrite(const vector<string>& args, const string levels, ostringstream* ostream) {
size_t argPos = 1; size_t argPos = 1;
bool hex = false; bool hex = false, newDefinition = false;
string circuit; string circuit;
symbol_t srcAddress = SYN, dstAddress = SYN; symbol_t srcAddress = SYN, dstAddress = SYN;
while (args.size() > argPos && args[argPos][0] == '-') { while (args.size() > argPos && args[argPos][0] == '-') {
if (args[argPos] == "-h") { if (args[argPos] == "-h") {
hex = true; 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") { } else if (args[argPos] == "-s" || args[argPos] == "-d") {
bool dest = args[argPos] == "-d"; bool dest = args[argPos] == "-d";
argPos++; argPos++;
@@ -931,10 +991,31 @@ result_t MainLoop::executeWrite(const vector<string>& args, const string levels,
argPos++; 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 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) { if (hex && argPos > 0) {
MasterSymbolString master; MasterSymbolString master;
result_t ret = parseHexMaster(args, argPos, srcAddress, &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; return ret;
} }
if (argPos == 0 || circuit.empty() || (args.size() != argPos + 2 && args.size() != argPos + 1)) { Message* message;
*ostream << "usage: write [-s QQ] [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" result_t ret;
" or: write [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n" if (newDefinition) {
" Write value(s) or hex message.\n" time_t now;
" -s QQ override source address QQ\n" time(&now);
" -d ZZ override destination address ZZ\n" string errorDescription;
" -c CIRCUIT CIRCUIT of the message to send\n" istringstream istr = istringstream("#\n" + args[argPos]); // ensure first line is not used for determining col names
" NAME NAME of the message to send\n" m_newlyDefinedMessages->clear();
" VALUE a single field VALUE\n" ret = m_newlyDefinedMessages->readFromStream(&istr, "temporary", now, true, NULL, &errorDescription);
" -h send hex write message:\n" if (ret != RESULT_OK) {
" ZZ destination address\n" *ostream << "ERR: bad definition: " << errorDescription;
" PB SB primary/secondary command byte\n"
" NN number of following data bytes\n"
" Dx data byte(s) to send";
return RESULT_OK; return RESULT_OK;
} }
Message* message = m_messages->find(circuit, args[argPos], levels, true); 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);
}
if (message == NULL) { if (message == NULL) {
return RESULT_ERR_NOTFOUND; return RESULT_ERR_NOTFOUND;
@@ -1016,8 +1103,7 @@ result_t MainLoop::executeWrite(const vector<string>& args, const string levels,
return RESULT_ERR_INVALID_ADDR; return RESULT_ERR_INVALID_ADDR;
} }
// allow missing values // allow missing values
result_t ret = m_busHandler->readFromBus(message, args.size() == argPos + 1 ? "" : args[argPos + 1], dstAddress, ret = m_busHandler->readFromBus(message, args.size() == argPos + 1 ? "" : args[argPos + 1], dstAddress, srcAddress);
srcAddress);
if (ret != RESULT_OK) { if (ret != RESULT_OK) {
logError(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), logError(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(),
getResultCode(ret)); getResultCode(ret));
@@ -1385,6 +1471,139 @@ result_t MainLoop::executeGrab(const vector<string>& args, ostringstream* ostrea
return RESULT_OK; 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) { result_t MainLoop::executeScan(const vector<string>& args, const string& levels, ostringstream* ostream) {
if (args.size() == 1) { if (args.size() == 1) {
result_t result = m_busHandler->startScan(false, levels); result_t result = m_busHandler->startScan(false, levels);
@@ -1566,8 +1785,11 @@ result_t MainLoop::executeHelp(ostringstream* ostream) {
*ostream << "usage:\n" *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" " [-i VALUE[;VALUE]*] NAME [FIELD[.N]]\n"
" 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" " 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|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" " Write hex message: write [-s QQ] [-c CIRCUIT] -h ZZPBSBNNDx\n"
" auth|a Authenticate user: auth USER SECRET\n" " auth|a Authenticate user: auth USER SECRET\n"
" hex Send hex data: hex [-s QQ] ZZPBSBNNDx\n" " hex Send hex data: hex [-s QQ] ZZPBSBNNDx\n"
@@ -1578,6 +1800,9 @@ result_t MainLoop::executeHelp(ostringstream* ostream) {
" info|i Report information about the daemon, the configuration, and seen devices.\n" " info|i Report information about the daemon, the configuration, and seen devices.\n"
" grab|g Grab messages: grab [stop]\n" " grab|g Grab messages: grab [stop]\n"
" Report the messages: grab result [all]\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" " scan Scan slaves: scan [full|ZZ]\n"
" Report scan result: scan result\n" " Report scan result: scan result\n"
" log Set log area level: log [AREA[,AREA]* LEVEL]\n" " log Set log area level: log [AREA[,AREA]* LEVEL]\n"
+28 -1
View File
@@ -66,7 +66,7 @@ class UserList : public UserInfo, public MappedFileReader {
// @copydoc // @copydoc
result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row, 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 // @copydoc
bool hasUser(const string& user) const override { 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); 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. * Execute the scan command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @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. */ /** whether to enable the hex command. */
const bool m_enableHex; 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. */ /** set to true to shutdown. */
bool m_shutdown; bool m_shutdown;
+2 -2
View File
@@ -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, 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 name = (*row)["name"]; // required
string firstFieldName; string firstFieldName;
size_t colon = name.find(':'); size_t colon = name.find(':');
@@ -1271,7 +1271,7 @@ result_t DataFieldTemplates::addFromFile(const string& filename, unsigned int li
if (result != RESULT_OK) { if (result != RESULT_OK) {
return result; return result;
} }
result = add(field, name, true); result = add(field, name, replace);
if (result == RESULT_ERR_DUPLICATE_NAME) { if (result == RESULT_ERR_DUPLICATE_NAME) {
*errorDescription = name; *errorDescription = name;
} }
+1 -1
View File
@@ -746,7 +746,7 @@ class DataFieldTemplates : public MappedFileReader {
// @copydoc // @copydoc
result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row, 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. * 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, 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) { if (hash) {
*hash = 0; *hash = 0;
} }
@@ -71,20 +71,20 @@ result_t FileReader::readFromStream(istream* stream, const string& filename, con
vector<string> row; vector<string> row;
result_t result = RESULT_OK; result_t result = RESULT_OK;
while (stream->peek() != EOF && 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; return result;
} }
result_t FileReader::readLineFromStream(istream* stream, const string& filename, bool verbose, 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; result_t result;
if (!splitFields(stream, row, lineNo, hash, size)) { if (!splitFields(stream, row, lineNo, hash, size)) {
*errorDescription = "blank line"; *errorDescription = "blank line";
result = RESULT_ERR_EOF; result = RESULT_ERR_EOF;
} else { } else {
*errorDescription = ""; *errorDescription = "";
result = addFromFile(filename, *lineNo, row, errorDescription); result = addFromFile(filename, *lineNo, row, errorDescription, replace);
} }
if (result != RESULT_OK) { if (result != RESULT_OK) {
if (!errorDescription->empty()) { 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, 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_mutex.lock();
m_columnNames.clear(); m_columnNames.clear();
m_lastDefaults.clear(); m_lastDefaults.clear();
@@ -253,13 +253,14 @@ result_t MappedFileReader::readFromStream(istream* stream, const string& filenam
size_t lastSep = filename.find_last_of('/'); size_t lastSep = filename.find_last_of('/');
string defaultsPart = lastSep == string::npos ? filename : filename.substr(lastSep+1); string defaultsPart = lastSep == string::npos ? filename : filename.substr(lastSep+1);
extractDefaultsFromFilename(defaultsPart, &m_lastDefaults[""]); 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(); m_mutex.unlock();
return result; return result;
} }
result_t MappedFileReader::addFromFile(const string& filename, unsigned int lineNo, vector<string>* row, result_t MappedFileReader::addFromFile(const string& filename, unsigned int lineNo, vector<string>* row,
string* errorDescription) { string* errorDescription, bool replace) {
result_t result; result_t result;
if (lineNo == 1) { // first line defines column names if (lineNo == 1) { // first line defines column names
result = getFieldMap(m_preferLanguage, row, errorDescription); result = getFieldMap(m_preferLanguage, row, errorDescription);
@@ -325,7 +326,7 @@ result_t MappedFileReader::addFromFile(const string& filename, unsigned int line
if (isDefault) { if (isDefault) {
return addDefaultFromFile(filename, lineNo, &rowMapped, &subRowsMapped, errorDescription); 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) { 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 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 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 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 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. * @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. * @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, 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. * 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 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 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 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 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. * @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. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t readLineFromStream(istream* stream, const string& filename, bool verbose, 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. * 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 lineNo the current line number in the file being read.
* @param row the definition row (allowed to be modified). * @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 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. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t addFromFile(const string& filename, unsigned int lineNo, vector<string>* row, 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. * Left and right trim the string.
@@ -205,7 +209,8 @@ class MappedFileReader : public FileReader {
// @copydoc // @copydoc
result_t readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, 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. * Extract default values from the file name.
@@ -223,7 +228,7 @@ class MappedFileReader : public FileReader {
// @copydoc // @copydoc
result_t addFromFile(const string& filename, unsigned int lineNo, vector<string>* row, 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. * 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 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 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 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. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row, 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. * @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 DataFieldTemplates* getTemplates(const string& filename);
extern result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, bool verbose, 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, 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; 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(); uint64_t key = message->getKey();
bool conditional = message->isConditional(); bool conditional = message->isConditional();
if (!m_addAll) { if (!m_addAll) {
lock();
const auto keyIt = m_messagesByKey.find(key); const auto keyIt = m_messagesByKey.find(key);
if (keyIt != m_messagesByKey.end()) { if (keyIt != m_messagesByKey.end()) {
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);
}
}
for (auto other : removeMessages) {
remove(other);
}
} else {
Message *other = getFirstAvailable(keyIt->second, message); Message *other = getFirstAvailable(keyIt->second, message);
if (other != NULL) { if (other != NULL && (!conditional || !other->isConditional())) {
if (!conditional) { unlock();
return RESULT_ERR_DUPLICATE; // duplicate key
}
if (!other->isConditional()) {
return RESULT_ERR_DUPLICATE; // duplicate key return RESULT_ERR_DUPLICATE; // duplicate key
} }
} }
} }
unlock();
} }
bool isPassive = message->isPassive(); bool isPassive = message->isPassive();
if (storeByName) { 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 suffix = FIELD_SEPARATOR + name + (isPassive ? "P" : (isWrite ? "W" : "R"));
string nameKey = circuit + suffix; string nameKey = circuit + suffix;
if (!m_addAll) { if (!m_addAll) {
lock();
const auto nameIt = m_messagesByName.find(nameKey); const auto nameIt = m_messagesByName.find(nameKey);
if (nameIt != m_messagesByName.end()) { if (nameIt != m_messagesByName.end()) {
vector<Message*>* messages = &nameIt->second; 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 return RESULT_ERR_DUPLICATE_NAME; // duplicate key
} }
} }
unlock();
} }
m_messagesByName[nameKey].push_back(message); m_messagesByName[nameKey].push_back(message);
nameKey = suffix; // also store without circuit nameKey = suffix; // also store without circuit
@@ -1808,6 +1837,71 @@ result_t MessageMap::add(bool storeByName, Message* message) {
return RESULT_OK; 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 { 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 // type,circuit,name,[comment],[QQ],ZZ,PBSB,[ID],*name,[part],type,divisor/values,unit,comment
// minimum: type,name,PBSB,*type // 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, 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; size_t localHash, localSize;
if (!hash) { if (!hash) {
hash = &localHash; hash = &localHash;
@@ -2113,8 +2207,8 @@ result_t MessageMap::readFromStream(istream* stream, const string& filename, con
if (!size) { if (!size) {
size = &localSize; size = &localSize;
} }
result_t result = MappedFileReader::readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, hash, result_t result
size); = MappedFileReader::readFromStream(stream, filename, mtime, verbose, defaults, errorDescription, replace, hash, size);
if (defaults) { if (defaults) {
string circuit = AttributedItem::pluck("circuit", defaults); string circuit = AttributedItem::pluck("circuit", defaults);
if (!circuit.empty() && m_circuitData.find(circuit) == m_circuitData.end()) { 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, 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; Condition* condition = NULL;
string types = AttributedItem::pluck("type", row); string types = AttributedItem::pluck("type", row);
result_t result = readConditions(filename, &types, errorDescription, &condition); 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) { for (const auto message : messages) {
if (result == RESULT_OK) { if (result == RESULT_OK) {
result = add(true, message); result = add(true, message, replace);
if (result == RESULT_ERR_DUPLICATE_NAME) { if (result == RESULT_ERR_DUPLICATE_NAME) {
*errorDescription = "invalid name"; *errorDescription = "invalid name";
} else if (result == RESULT_ERR_DUPLICATE) { } else if (result == RESULT_ERR_DUPLICATE) {
+23 -3
View File
@@ -782,6 +782,18 @@ class MessagePriorityQueue
} }
priority_queue<Message*, vector<Message*>, compareMessagePriority>::push(__x); 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. * Add a @a Message instance to this set.
* @param message the @a Message instance to add. * @param message the @a Message instance to add.
* @param storeByName whether to store the @a Message by name. * @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. * @return @a RESULT_OK on success, or an error code.
* Note: the caller may not free the added instance on success. * 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 // @copydoc
result_t getFieldMap(const string& preferLanguage, vector<string>* row, string* errorDescription) const override; result_t getFieldMap(const string& preferLanguage, vector<string>* row, string* errorDescription) const override;
@@ -1261,11 +1280,12 @@ class MessageMap : public MappedFileReader {
// @copydoc // @copydoc
result_t readFromStream(istream* stream, const string& filename, const time_t& mtime, bool verbose, 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 // @copydoc
result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row, 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. * Get the scan @a Message instance for the specified address.