implemented reload command, allow reading config from symlinks, added verbose and maxage (defaults to 5 minutes) options to read command, use "write -h" instead "hex" for hex commands
This commit is contained in:
+115
-87
@@ -20,6 +20,7 @@
|
|||||||
#include "baseloop.h"
|
#include "baseloop.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "appl.h"
|
#include "appl.h"
|
||||||
|
#include "data.h"
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
@@ -30,26 +31,10 @@ extern Appl& A;
|
|||||||
|
|
||||||
BaseLoop::BaseLoop()
|
BaseLoop::BaseLoop()
|
||||||
{
|
{
|
||||||
// create commands DB
|
// load messages and templates
|
||||||
m_templates = new DataFieldTemplates();
|
m_templates = new DataFieldTemplates();
|
||||||
m_messages = new MessageMap();
|
m_messages = new MessageMap();
|
||||||
|
loadMessages();
|
||||||
string confdir = A.getOptVal<const char*>("ebusconfdir");
|
|
||||||
L.log(bas, trace, "ebus configuration dir: %s", confdir.c_str());
|
|
||||||
result_t result = m_templates->readFromFile(confdir+"/_types.csv");
|
|
||||||
if (result == RESULT_OK)
|
|
||||||
L.log(bas, trace, "read templates");
|
|
||||||
else
|
|
||||||
L.log(bas, error, "error reading templates: %s", getResultCode(result));
|
|
||||||
result = readConfigFiles(confdir, ".csv");
|
|
||||||
if (result == RESULT_OK)
|
|
||||||
L.log(bas, trace, "read config files");
|
|
||||||
else
|
|
||||||
L.log(bas, error, "error reading config files: %s", getResultCode(result));
|
|
||||||
|
|
||||||
L.log(bas, event, "message DB: %d ", m_messages->size());
|
|
||||||
L.log(bas, event, "updates DB: %d ", m_messages->size(true));
|
|
||||||
L.log(bas, event, "polling DB: %d ", m_messages->sizePoll());
|
|
||||||
|
|
||||||
m_ownAddress = A.getOptVal<int>("address") & 0xff;
|
m_ownAddress = A.getOptVal<int>("address") & 0xff;
|
||||||
const bool answer = A.getOptVal<bool>("answer");
|
const bool answer = A.getOptVal<bool>("answer");
|
||||||
@@ -113,6 +98,31 @@ BaseLoop::~BaseLoop()
|
|||||||
delete m_templates;
|
delete m_templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result_t BaseLoop::loadMessages()
|
||||||
|
{
|
||||||
|
string confdir = A.getOptVal<const char*>("ebusconfdir");
|
||||||
|
L.log(bas, trace, "ebus configuration dir: %s", confdir.c_str());
|
||||||
|
m_messages->clear();
|
||||||
|
m_templates->clear();
|
||||||
|
result_t result = m_templates->readFromFile(confdir+"/_types.csv");
|
||||||
|
if (result == RESULT_OK)
|
||||||
|
L.log(bas, trace, "read templates");
|
||||||
|
else
|
||||||
|
L.log(bas, error, "error reading templates: %s", getResultCode(result));
|
||||||
|
|
||||||
|
result = readConfigFiles(confdir, ".csv");
|
||||||
|
if (result == RESULT_OK) {
|
||||||
|
L.log(bas, trace, "read config files");
|
||||||
|
|
||||||
|
L.log(bas, event, "message DB: %d ", m_messages->size());
|
||||||
|
L.log(bas, event, "updates DB: %d ", m_messages->size(true));
|
||||||
|
L.log(bas, event, "polling DB: %d ", m_messages->sizePoll());
|
||||||
|
} else
|
||||||
|
L.log(bas, error, "error reading config files: %s", getResultCode(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
result_t BaseLoop::readConfigFiles(const string path, const string extension)
|
result_t BaseLoop::readConfigFiles(const string path, const string extension)
|
||||||
{
|
{
|
||||||
DIR* dir = opendir(path.c_str());
|
DIR* dir = opendir(path.c_str());
|
||||||
@@ -132,7 +142,7 @@ result_t BaseLoop::readConfigFiles(const string path, const string extension)
|
|||||||
if (result != RESULT_OK)
|
if (result != RESULT_OK)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} else if (d->d_type == DT_REG) {
|
} else if (d->d_type == DT_REG || d->d_type == DT_LNK) {
|
||||||
string fn = d->d_name;
|
string fn = d->d_name;
|
||||||
|
|
||||||
if (fn.find(extension, (fn.length() - extension.length())) != string::npos
|
if (fn.find(extension, (fn.length() - extension.length())) != string::npos
|
||||||
@@ -208,7 +218,6 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
return "command missing";
|
return "command missing";
|
||||||
|
|
||||||
size_t argPos = 1;
|
size_t argPos = 1;
|
||||||
bool force = false;
|
|
||||||
|
|
||||||
switch (getCase(args[0])) {
|
switch (getCase(args[0])) {
|
||||||
case ct_invalid:
|
case ct_invalid:
|
||||||
@@ -216,24 +225,51 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ct_read: {
|
case ct_read: {
|
||||||
if (args.size() > argPos && args[argPos] == "-f") {
|
unsigned int maxAge = 5*60;
|
||||||
force = true;
|
bool verbose = false;
|
||||||
|
while (args.size() > argPos && args[argPos][0] == '-') {
|
||||||
|
if (args[argPos]== "-f") {
|
||||||
|
maxAge = 0;
|
||||||
|
} else if (args[argPos] == "-v") {
|
||||||
|
verbose = 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 = args.size(); // print usage
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
argPos = args.size(); // print usage
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
argPos = args.size(); // print usage
|
||||||
|
break;
|
||||||
|
}
|
||||||
argPos++;
|
argPos++;
|
||||||
}
|
}
|
||||||
if (args.size() < argPos + 1 || args.size() > argPos + 3) {
|
if (args.size() < argPos + 1 || args.size() > argPos + 3) {
|
||||||
result << "usage: 'read [-f] [class] cmd' or 'read [-f] class cmd sub'";
|
result << "usage: 'read [-f] [-v] [class] cmd'"; // TODO or 'read [-f] class cmd sub'";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
|
||||||
Message* updateMessage = NULL;
|
Message* updateMessage = NULL;
|
||||||
if (force == false) {
|
if (maxAge > 0 && verbose == false) {
|
||||||
if (args.size() == argPos + 1)
|
if (args.size() == argPos + 1)
|
||||||
updateMessage = m_messages->find("", args[argPos], false, true);
|
updateMessage = m_messages->find("", args[argPos], false, true);
|
||||||
else
|
else
|
||||||
updateMessage = m_messages->find(args[argPos], args[argPos + 1], false, true);
|
updateMessage = m_messages->find(args[argPos], args[argPos + 1], false, true);
|
||||||
|
|
||||||
if (updateMessage != NULL) {
|
if (updateMessage != NULL && updateMessage->getLastUpdateTime() + maxAge > now) {
|
||||||
token = updateMessage->getLastValue();
|
token = updateMessage->getLastValue(); // TODO switch from last value to last master/slave to support verbose cached/polled values as well
|
||||||
if (token.empty() == false) {
|
if (token.empty() == false) {
|
||||||
result << token;
|
result << token;
|
||||||
break;
|
break;
|
||||||
@@ -248,8 +284,8 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
message = m_messages->find(args[argPos], args[argPos + 1], false);
|
message = m_messages->find(args[argPos], args[argPos + 1], false);
|
||||||
|
|
||||||
if (message != NULL) {
|
if (message != NULL) {
|
||||||
|
if (maxAge > 0 && m_pollActive == true && message->getPollPriority() > 0
|
||||||
if (m_pollActive == true && message->getPollPriority() > 0) {
|
&& message->getLastUpdateTime() + maxAge > now) {
|
||||||
// get polldata
|
// get polldata
|
||||||
token = message->getLastValue();
|
token = message->getLastValue();
|
||||||
if (token.empty() == false) {
|
if (token.empty() == false) {
|
||||||
@@ -274,7 +310,7 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
|
|
||||||
if (ret == RESULT_OK) {
|
if (ret == RESULT_OK) {
|
||||||
// TODO reduce to requested variable only
|
// TODO reduce to requested variable only
|
||||||
ret = message->decode(pt_slaveData, slave, result); // decode data
|
ret = message->decode(pt_slaveData, slave, result, false, verbose); // decode data
|
||||||
}
|
}
|
||||||
if (ret != RESULT_OK) {
|
if (ret != RESULT_OK) {
|
||||||
L.log(bas, error, "read: %s", getResultCode(ret));
|
L.log(bas, error, "read: %s", getResultCode(ret));
|
||||||
@@ -289,8 +325,49 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ct_write: {
|
case ct_write: {
|
||||||
|
if (args.size() > argPos && args[argPos] == "-h") {
|
||||||
|
argPos++;
|
||||||
|
|
||||||
|
if (args.size() < argPos + 1) {
|
||||||
|
result << "usage: 'write -h ZZPBSBNNDx'";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostringstream msg;
|
||||||
|
msg << hex << setw(2) << setfill('0') << static_cast<unsigned>(m_ownAddress) << setw(0);
|
||||||
|
while (argPos < args.size()) {
|
||||||
|
if ((args[argPos].length() % 2) != 0) {
|
||||||
|
result << "invalid hex string";
|
||||||
|
msg.str("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
msg << args[argPos++];
|
||||||
|
}
|
||||||
|
if (msg.str().length() == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
SymbolString master(msg.str());
|
||||||
|
L.log(bas, event, "write hex cmd: %s", master.getDataStr().c_str());
|
||||||
|
|
||||||
|
// send message
|
||||||
|
SymbolString slave;
|
||||||
|
result_t ret = m_busHandler->sendAndWait(master, slave);
|
||||||
|
|
||||||
|
if (ret == RESULT_OK) {
|
||||||
|
if (master[1] == BROADCAST || isMaster(master[1]))
|
||||||
|
result << "done";
|
||||||
|
else
|
||||||
|
result << slave.getDataStr();
|
||||||
|
}
|
||||||
|
if (ret != RESULT_OK) {
|
||||||
|
L.log(bas, error, "write hex: %s", getResultCode(ret));
|
||||||
|
result << getResultCode(ret);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.size() != argPos + 3) {
|
if (args.size() != argPos + 3) {
|
||||||
result << "usage: 'write class cmd value[;value]*'";
|
result << "usage: 'write class cmd value[;value]*' or 'write -h ZZPBSBNNDx'";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,44 +407,6 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ct_hex: {
|
|
||||||
if (args.size() < argPos + 1) {
|
|
||||||
result << "usage: 'hex value' (value: ZZPBSBNNDx)";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ostringstream msg;
|
|
||||||
msg << hex << setw(2) << setfill('0') << static_cast<unsigned>(m_ownAddress) << setw(0);
|
|
||||||
while (argPos < args.size()) {
|
|
||||||
if ((args[argPos].length() % 2) != 0) {
|
|
||||||
result << "invalid hex string";
|
|
||||||
msg.str("");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg << args[argPos++];
|
|
||||||
}
|
|
||||||
if (msg.str().length() == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
SymbolString master(msg.str());
|
|
||||||
L.log(bas, event, "hex cmd: %s", master.getDataStr().c_str());
|
|
||||||
|
|
||||||
// send message
|
|
||||||
SymbolString slave;
|
|
||||||
result_t ret = m_busHandler->sendAndWait(master, slave);
|
|
||||||
|
|
||||||
if (ret == RESULT_OK) {
|
|
||||||
if (master[1] == BROADCAST || isMaster(master[1]))
|
|
||||||
result << "done";
|
|
||||||
else
|
|
||||||
result << slave.getDataStr();
|
|
||||||
}
|
|
||||||
if (ret != RESULT_OK) {
|
|
||||||
L.log(bas, error, "hex: %s", getResultCode(ret));
|
|
||||||
result << getResultCode(ret);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ct_scan: {
|
case ct_scan: {
|
||||||
if (args.size() == argPos) {
|
if (args.size() == argPos) {
|
||||||
result_t ret = m_busHandler->startScan();
|
result_t ret = m_busHandler->startScan();
|
||||||
@@ -408,7 +447,6 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check for possible areas and level
|
|
||||||
if (strcasecmp(args[argPos].c_str(), "AREAS") == 0) {
|
if (strcasecmp(args[argPos].c_str(), "AREAS") == 0) {
|
||||||
L.getSink(0)->setAreas(calcAreas(args[argPos + 1]));
|
L.getSink(0)->setAreas(calcAreas(args[argPos + 1]));
|
||||||
result << "done";
|
result << "done";
|
||||||
@@ -447,31 +485,23 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
result << (enabled ? "dump enabled" : "dump disabled");
|
result << (enabled ? "dump enabled" : "dump disabled");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/*case ct_reload:
|
case ct_reload: {
|
||||||
if (cmd.size() != 1) {
|
if (args.size() != 1) {
|
||||||
result << "usage: 'reload'";
|
result << "usage: 'reload'";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
// create commands DB
|
// create commands DB
|
||||||
Commands* commands = ConfigCommands(A.getOptVal<const char*>("ebusconfdir"), ft_csv).getCommands();
|
result_t ret = loadMessages();
|
||||||
L.log(bas, trace, "ebus configuration dir: %s", A.getOptVal<const char*>("ebusconfdir"));
|
if (ret == RESULT_OK)
|
||||||
L.log(bas, event, "commands DB: %d ", m_commands->sizeCmdDB());
|
|
||||||
L.log(bas, event, " cycle DB: %d ", m_commands->sizeCycDB());
|
|
||||||
L.log(bas, event, " polling DB: %d ", m_commands->sizePollDB());
|
|
||||||
|
|
||||||
delete m_commands;
|
|
||||||
m_commands = commands;
|
|
||||||
m_busloop->reload(m_commands);
|
|
||||||
|
|
||||||
result << "done";
|
result << "done";
|
||||||
|
else
|
||||||
|
result << getResultCode(ret);
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
case ct_help:
|
case ct_help:
|
||||||
result << "commands:" << endl
|
result << "commands:" << endl
|
||||||
<< " read - read ebus values 'read [-f] [class] cmd [sub]'" << endl
|
<< " read - read ebus values 'read [-f] [-v] [class] cmd'" << endl
|
||||||
<< " write - write ebus values 'write class cmd value[;value]*'" << endl
|
<< " write - write ebus values 'write class cmd value[;value]*'" << endl
|
||||||
<< " hex - send given hex value 'hex type value' (value: ZZPBSBNNDx)" << endl << endl
|
<< " hex - send given hex value 'hex type value' (value: ZZPBSBNNDx)" << endl << endl
|
||||||
<< " scan - scan ebus kown addresses 'scan'" << endl
|
<< " scan - scan ebus kown addresses 'scan'" << endl
|
||||||
@@ -487,8 +517,6 @@ string BaseLoop::decodeMessage(const string& data)
|
|||||||
<< " help - print this page 'help'";
|
<< " help - print this page 'help'";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.str();
|
return result.str();
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ using namespace std;
|
|||||||
enum CommandType {
|
enum CommandType {
|
||||||
ct_read, /*!< read ebus values */
|
ct_read, /*!< read ebus values */
|
||||||
ct_write, /*!< write ebus values */
|
ct_write, /*!< write ebus values */
|
||||||
ct_hex, /*!< send hex data */
|
|
||||||
ct_scan, /*!< scan ebus */
|
ct_scan, /*!< scan ebus */
|
||||||
ct_log, /*!< logger settings */
|
ct_log, /*!< logger settings */
|
||||||
ct_raw, /*!< toggle log raw data */
|
ct_raw, /*!< toggle log raw data */
|
||||||
@@ -59,10 +58,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
~BaseLoop();
|
~BaseLoop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the message definitions.
|
||||||
|
* @return the result code.
|
||||||
|
*/
|
||||||
|
result_t loadMessages();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read the configuration files from the specified path.
|
* @brief Read the configuration files from the specified path.
|
||||||
* @param path the path from which to read the files.
|
* @param path the path from which to read the files.
|
||||||
* @param extension the filename extension of the files to read.
|
* @param extension the filename extension of the files to read.
|
||||||
|
* @return the result code.
|
||||||
*/
|
*/
|
||||||
result_t readConfigFiles(const string path, const string extension);
|
result_t readConfigFiles(const string path, const string extension);
|
||||||
|
|
||||||
@@ -119,7 +125,6 @@ private:
|
|||||||
{
|
{
|
||||||
if (strcasecmp(item.c_str(), "READ") == 0) return ct_read;
|
if (strcasecmp(item.c_str(), "READ") == 0) return ct_read;
|
||||||
if (strcasecmp(item.c_str(), "WRITE") == 0) return ct_write;
|
if (strcasecmp(item.c_str(), "WRITE") == 0) return ct_write;
|
||||||
if (strcasecmp(item.c_str(), "HEX") == 0) return ct_hex;
|
|
||||||
if (strcasecmp(item.c_str(), "SCAN") == 0) return ct_scan;
|
if (strcasecmp(item.c_str(), "SCAN") == 0) return ct_scan;
|
||||||
if (strcasecmp(item.c_str(), "LOG") == 0) return ct_log;
|
if (strcasecmp(item.c_str(), "LOG") == 0) return ct_log;
|
||||||
if (strcasecmp(item.c_str(), "RAW") == 0) return ct_raw;
|
if (strcasecmp(item.c_str(), "RAW") == 0) return ct_raw;
|
||||||
|
|||||||
Reference in New Issue
Block a user