added command help, nicer implementation of quit and stop commands, nicer help

This commit is contained in:
john30
2015-02-01 13:04:15 +01:00
parent 5b373377a4
commit 0e8a669890
4 changed files with 184 additions and 86 deletions
+123 -58
View File
@@ -41,11 +41,11 @@ MainLoop::MainLoop(const struct options opt, DataFieldTemplates* templates, Mess
m_address, opt.answer,
opt.acquireRetries, opt.sendRetries,
opt.acquireTimeout, opt.receiveTimeout,
opt.numberMasters, opt.pollInterval);
opt.masterCount, opt.pollInterval);
m_busHandler->start("bushandler");
// create network
m_network = new Network(opt.localhost, opt.port, &m_netQueue);
m_network = new Network(opt.localOnly, opt.port, &m_netQueue);
m_network->start("network");
}
@@ -74,10 +74,12 @@ MainLoop::~MainLoop()
void MainLoop::run()
{
for (;;) {
bool running = true;
while (running == true) {
string result;
// recv new message from client
// pick the next message to handle
NetMessage* message = m_netQueue.remove();
string data = message->getData();
@@ -87,17 +89,13 @@ void MainLoop::run()
if (listening == false)
since = until;
bool connected = true;
if (data.length() > 0) {
data.erase(remove(data.begin(), data.end(), '\r'), data.end());
data.erase(remove(data.begin(), data.end(), '\n'), data.end());
logNotice(lf_main, ">>> %s", data.c_str());
// decode message
if (strcasecmp(data.c_str(), "STOP") != 0)
result = decodeMessage(data, listening);
else
result = "done";
result = decodeMessage(data, connected, listening, running);
logNotice(lf_main, "<<< %s", result.c_str());
result += "\n\n";
@@ -107,11 +105,7 @@ void MainLoop::run()
}
// send result to client
message->setResult(result, listening, until);
// stop daemon
if (strcasecmp(data.c_str(), "STOP") == 0)
return;
message->setResult(result, listening, until, connected == false);
}
}
@@ -122,7 +116,7 @@ void MainLoop::logRaw(const unsigned char byte, bool received) {
logNotice(lf_bus, ">%02x", byte);
}
string MainLoop::decodeMessage(const string& data, bool& listening)
string MainLoop::decodeMessage(const string& data, bool& connected, bool& listening, bool& running)
{
ostringstream result;
@@ -157,8 +151,16 @@ string MainLoop::decodeMessage(const string& data, bool& listening)
if (args.size() == 0)
return "command missing";
const char* str = args[0].c_str();
if (args.size() == 2) {
// check for "CMD -h"
if (strcasecmp(args[1].c_str(), "-h") == 0 || strcasecmp(args[1].c_str(), "-?") == 0 || strcasecmp(args[1].c_str(), "--help") == 0)
args.clear(); // empty args is used as command help indicator
else if (strcasecmp(args[0].c_str(), "H") == 0 || strcasecmp(args[0].c_str(), "HELP") == 0) { // check for "HELP CMD"
str = args[1].c_str();
args.clear(); // empty args is used as command help indicator
}
}
if (strcasecmp(str, "R") == 0 || strcasecmp(str, "READ") == 0)
return executeRead(args);
if (strcasecmp(str, "W") == 0 || strcasecmp(str, "WRITE") == 0)
@@ -168,7 +170,7 @@ string MainLoop::decodeMessage(const string& data, bool& listening)
if (strcasecmp(str, "L") == 0 || strcasecmp(str, "LISTEN") == 0)
return executeListen(args, listening);
if (strcasecmp(str, "STATE") == 0)
return executeState();
return executeState(args);
if (strcasecmp(str, "SCAN") == 0)
return executeScan(args);
if (strcasecmp(str, "LOG") == 0)
@@ -179,6 +181,10 @@ string MainLoop::decodeMessage(const string& data, bool& listening)
return executeDump(args);
if (strcasecmp(str, "RELOAD") == 0)
return executeReload(args);
if (strcasecmp(str, "STOP") == 0)
return executeStop(args, running);
if (strcasecmp(str, "Q") == 0 || strcasecmp(str, "QUIT") == 0)
return executeQuit(args, connected);
if (strcasecmp(str, "H") == 0 || strcasecmp(str, "HELP") == 0)
return executeHelp();
@@ -228,7 +234,14 @@ string MainLoop::executeRead(vector<string> &args)
argPos++;
}
if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2)
return "usage: 'read [-v] [-f] [-m seconds] [-c class] name [field]'";
return "usage: 'read [-v] [-f] [-m SECONDS] [-c CLASS] NAME [FIELD]'\n"
" Read value(s).\n"
" -v be verbose (include field names, units, and comments)\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 CLASS limit to messages of CLASS\n"
" NAME the NAME of the message to send\n"
" FIELD only retrieve the single FIELD";
if (args.size() == argPos + 2)
maxAge = 0; // force refresh to filter single field
@@ -289,12 +302,13 @@ string MainLoop::executeRead(vector<string> &args)
string MainLoop::executeWrite(vector<string> &args)
{
size_t argPos = 1;
if (args.size() > argPos && args[argPos] == "-h") {
while (args.size() > argPos && args[argPos] == "-h") {
argPos++;
if (args.size() < argPos + 1)
return "usage: 'write -h ZZPBSBNNDx'";
if (args.size() < argPos + 1) {
argPos = 0;
break;
}
ostringstream msg;
msg << hex << setw(2) << setfill('0') << static_cast<unsigned>(m_address) << setw(0);
while (argPos < args.size()) {
@@ -323,8 +337,18 @@ string MainLoop::executeWrite(vector<string> &args)
return getResultCode(ret);
}
if (args.size() != argPos + 3)
return "usage: 'write class name value[;value]*' or 'write -h ZZPBSBNNDx'";
if (argPos == 0 || args.size() != argPos + 3)
return "usage: 'write CLASS NAME VALUE[;VALUE]*'\n"
" or: 'write -h ZZPBSBNNDx' or 'write -h ZZ PB SB NN Dx'\n"
" Write value(s).\n"
" CLASS the CLASS of the message to send\n"
" NAME the NAME of the message to send\n"
" VALUE a single field VALUE\n"
" -h directly write hex message:\n"
" ZZ destination address\n"
" PB SB primary/secondary command byte\n"
" NN number of data bytes to send\n"
" Dx the data byte(s) to send";
Message* message = m_messages->find(args[argPos], args[argPos + 1], true);
@@ -407,7 +431,13 @@ string MainLoop::executeFind(vector<string> &args)
argPos++;
}
if (argPos == 0 || args.size() < argPos || args.size() > argPos + 1)
return "usage: 'find [-v] [-r] [-w] [-p] [-d] [-c class] [name]'";
return "usage: 'find [-v] [-r] [-w] [-p] [-d] [-c CLASS] [NAME]'\n"
" Find value(s)."
" -v be verbose (include field names, units, and comments)\n"
" -r limit to active read messages (default all types)\n"
" -w limit to active write messages (default all types)\n"
" -p limit to passive messages (default all types)\n"
" -d only retrieve messages with actual data";
deque<Message*> messages;
if (args.size() == argPos)
@@ -454,7 +484,7 @@ string MainLoop::executeFind(vector<string> &args)
string MainLoop::executeListen(vector<string> &args, bool& listening)
{
if (args.size() <= 1) {
if (args.size() == 1) {
if (listening == true)
return "listen continued";
@@ -463,14 +493,19 @@ string MainLoop::executeListen(vector<string> &args, bool& listening)
}
if (args.size() != 2 || args[1] != "stop")
return "usage: 'listen [stop]'";
return "usage: 'listen [stop]'\n"
" Listen for updates (or stop it).";
listening = false;
return "listen stopped";
}
string MainLoop::executeState()
string MainLoop::executeState(vector<string> &args)
{
if (args.size() == 0)
return "usage: 'state'\n"
" Report bus state.";
if (m_busHandler->hasSignal() == true)
return "signal acquired";
@@ -479,7 +514,7 @@ string MainLoop::executeState()
string MainLoop::executeScan(vector<string> &args)
{
if (args.size() <= 1) {
if (args.size() == 1) {
result_t result = m_busHandler->startScan();
if (result == RESULT_OK)
return "scan initiated";
@@ -488,7 +523,7 @@ string MainLoop::executeScan(vector<string> &args)
return getResultCode(result);
}
if (strcasecmp(args[1].c_str(), "FULL") == 0) {
if (args.size() == 2 && strcasecmp(args[1].c_str(), "FULL") == 0) {
result_t result = m_busHandler->startScan(true);
if (result == RESULT_OK)
return "done";
@@ -497,27 +532,31 @@ string MainLoop::executeScan(vector<string> &args)
return getResultCode(result);
}
if (strcasecmp(args[1].c_str(), "RESULT") == 0) {
if (args.size() == 2 && strcasecmp(args[1].c_str(), "RESULT") == 0) {
ostringstream result;
m_busHandler->formatScanResult(result);
return result.str();
}
return "usage: 'scan'\n"
" 'scan full'\n"
" 'scan result'";
" or: 'scan full'\n"
" or: 'scan result'\n"
" Scan seen or all slaves, or report scan result.";
}
string MainLoop::executeLog(vector<string> &args)
{
bool result;
if (args.size() == 3 && strcasecmp(args[1].c_str(), "AREAS") == 0)
result = setLogFacilities(args[2].c_str());
if ((args.size() == 3 || args.size() == 2) && strcasecmp(args[1].c_str(), "AREAS") == 0)
result = setLogFacilities(args.size() == 3 ? args[2].c_str() : "");
else if (args.size() == 3 && strcasecmp(args[1].c_str(), "LEVEL") == 0)
result = setLogLevel(args[2].c_str());
else
return "usage: 'log areas area,area,..' (area: main|network|bus|update|all)\n"
" 'log level level' (level: error|notice|info|debug)";
return "usage: 'log areas AREA,AREA,...'\n"
" or: 'log level LEVEL'\n"
" Set log area(s) or log level.\n"
" AREA the log area(s) to include (main|network|bus|update|all)\n"
" LEVEL the log level to set (error|notice|info|debug)";
if (result == true)
return "done";
@@ -528,7 +567,8 @@ string MainLoop::executeLog(vector<string> &args)
string MainLoop::executeRaw(vector<string> &args)
{
if (args.size() != 1)
return "usage: 'raw'";
return "usage: 'raw'\n"
" Toggle log raw data.";
bool enabled = !m_port->getLogRaw();
m_port->setLogRaw(enabled);
@@ -539,7 +579,8 @@ string MainLoop::executeRaw(vector<string> &args)
string MainLoop::executeDump(vector<string> &args)
{
if (args.size() != 1)
return "usage: 'dump'";
return "usage: 'dump'\n"
" Toggle raw dump.";
bool enabled = !m_port->getDumpRaw();
m_port->setDumpRaw(enabled);
@@ -550,7 +591,8 @@ string MainLoop::executeDump(vector<string> &args)
string MainLoop::executeReload(vector<string> &args)
{
if (args.size() != 1)
return "usage: 'reload'";
return "usage: 'reload'\n"
" Reload config files.";
// reload commands
result_t result = loadConfigFiles(m_templates, m_messages);
@@ -560,25 +602,48 @@ string MainLoop::executeReload(vector<string> &args)
return getResultCode(result);
}
string MainLoop::executeStop(vector<string> &args, bool& running)
{
if (args.size() == 1) {
running = false;
return "daemon stopped";
}
return "usage: 'stop'\n"
" Stop the daemon.";
}
string MainLoop::executeQuit(vector<string> &args, bool& connected)
{
if (args.size() == 1) {
connected = false;
return "connection closed";
}
return "usage: 'quit'\n"
" Close client connection.";
}
string MainLoop::executeHelp()
{
return "commands:\n"
" read read value(s) 'read [-v] [-f] [-m seconds] [-c class] name [field]'\n"
" write write value(s) 'write class name value[;value]*' or 'write -h ZZPBSBNNDx'\n"
" find find value(s) 'find [-v] [-r] [-w] [-p] [-d] [-c class] [name]'\n"
" listen listen for updates 'listen [stop]'\n"
" state report bus state 'state'\n"
" scan scan seen slaves 'scan'\n"
" scan all slaves 'scan full'\n"
" show scan results 'scan result'\n"
" log set log areas 'log areas area,area,..' (area: main|network|bus|update|all)\n"
" set log level 'log level level' (level: error|notice|info|debug)\n"
" raw toggle log raw data 'raw'\n"
" dump toggle dump state 'dump'\n"
" reload reload config files 'reload'\n"
" stop stop daemon 'stop'\n"
" quit close connection 'quit'\n"
" help print this page 'help'";
return "usage:\n"
" read Read value(s) 'read [-v] [-f] [-m SECONDS] [-c CLASS] NAME [FIELD]'\n"
" write Write value(s) 'write CLASS NAME VALUE[;VALUE]*' or 'write -h ZZPBSBNNDx'\n"
" find Find value(s) 'find [-v] [-r] [-w] [-p] [-d] [-c CLASS] [NAME]'\n"
" listen Listen for updates 'listen [stop]'\n"
" state Report bus state 'state'\n"
" scan Scan seen slaves 'scan'\n"
" Scan all slaves 'scan full'\n"
" Report scan result 'scan result'\n"
" log Set log areas 'log areas AREA,AREA,...' (AREA: main|network|bus|update|all)\n"
" Set log level 'log level LEVEL' (LEVEL: error|notice|info|debug)\n"
" raw Toggle log raw data 'raw'\n"
" dump Toggle raw dump 'dump'\n"
" reload Reload config files 'reload'\n"
" stop Stop the daemon 'stop'\n"
" quit Close connection 'quit'\n"
" help Print this help page 'help'\n"
" Print command usage 'help COMMAND'";
}
string MainLoop::getUpdates(time_t since, time_t until)
+30 -12
View File
@@ -92,35 +92,37 @@ private:
/**
* Decode and execute client message.
* @param data the data string to decode (may be empty).
* @param connected set to false when the client connection shall be closed.
* @param listening set to true when the client is in listening mode.
* @param running set to false when the server shall be stopped.
* @return result string to send back to the client.
*/
string decodeMessage(const string& data, bool& listening);
string decodeMessage(const string& data, bool& connected, bool& listening, bool& running);
/**
* Execute the read command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeRead(vector<string> &args);
/**
* Execute the write command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeWrite(vector<string> &args);
/**
* Execute the find command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeFind(vector<string> &args);
/**
* Execute the listen command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @param listening set to true when the client is in listening mode.
* @return the result string.
*/
@@ -128,46 +130,62 @@ private:
/**
* Execute the state command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeState();
string executeState(vector<string> &args);
/**
* Execute the scan command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeScan(vector<string> &args);
/**
* Execute the log command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeLog(vector<string> &args);
/**
* Execute the raw command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeRaw(vector<string> &args);
/**
* Execute the dump command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeDump(vector<string> &args);
/**
* Execute the reload command.
* @param args the arguments passed to the command (starting with the command itself).
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string.
*/
string executeReload(vector<string> &args);
/**
* Execute the stop command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @param running set to false when the server shall be stopped.
* @return the result string.
*/
string executeStop(vector<string> &args, bool& running);
/**
* Execute the quit command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @param connected set to false when the client connection shall be closed.
* @return the result string.
*/
string executeQuit(vector<string> &args, bool& connected);
/**
* Execute the help command.
* @return the result string.
+7 -4
View File
@@ -113,13 +113,13 @@ void Connection::run()
size_t datalen = 0;
if (newData == true) {
if (m_socket->isValid() == true)
datalen = m_socket->recv(data, sizeof(data)-1);
else
if (m_socket->isValid() == false)
break;
datalen = m_socket->recv(data, sizeof(data)-1);
// remove closed socket
if (datalen <= 0 || strcasecmp(data, "Q") == 0 || strcasecmp(data, "QUIT") == 0)
if (datalen <= 0)
break;
}
@@ -137,6 +137,9 @@ void Connection::run()
m_socket->send(result.c_str(), result.size());
m_listening = message.isListening(listenSince);
if (message.isDisconnect())
break;
}
}
+24 -12
View File
@@ -47,7 +47,7 @@ public:
* @param listenSince start timestamp of listening update.
*/
NetMessage(const string data, const bool listening, const time_t listenSince)
: m_data(data), m_resultSet(false), m_listening(listening), m_listenSince(listenSince)
: m_data(data), m_resultSet(false), m_disconnect(false), m_listening(listening), m_listenSince(listenSince)
{
pthread_mutex_init(&m_mutex, NULL);
pthread_cond_init(&m_cond, NULL);
@@ -81,7 +81,8 @@ public:
* Wait for the result being set and return the result string.
* @return the result string.
*/
string getResult() {
string getResult()
{
pthread_mutex_lock(&m_mutex);
while (m_resultSet == false)
@@ -97,17 +98,19 @@ public:
* @param result the result string.
* @param listening whether the client is in listening mode.
* @param listenUntil the end time to which to updates were added (exclusive).
* @param disconnect true when the client shall be disconnected.
*/
void setResult(const string result, const bool listening, const time_t listenUntil)
{
m_result = result;
m_listening = listening;
m_listenSince = listenUntil;
m_resultSet = true;
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
}
void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect)
{
m_result = result;
m_disconnect = disconnect;
m_listening = listening;
m_listenSince = listenUntil;
m_resultSet = true;
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
}
/**
* Return whether the client is in listening mode.
@@ -116,6 +119,12 @@ public:
*/
bool isListening(time_t& listenSince) { listenSince = m_listenSince; return m_listening; }
/**
* Return whether the client shall be disconnected.
* @retur true when the client shall be disconnected.
*/
bool isDisconnect() { return m_disconnect; }
private:
/** the data string */
string m_data;
@@ -126,6 +135,9 @@ private:
/** the result string */
string m_result;
/** set to true when the client shall be disconnected. */
bool m_disconnect;
/** mutex variable for exclusive lock */
pthread_mutex_t m_mutex;