mainloop: moved instruction execution to main loop, do not send intial scan when readonly, added "--inject" option, allow --readonly with --scanconfig[=none], store scans initiated by other participants, removed unused code
This commit is contained in:
+23
-13
@@ -384,6 +384,7 @@ void BusHandler::run() {
|
||||
lastTime += 2;
|
||||
logNotice(lf_bus, "bus started with own address %2.2x/%2.2x%s", m_ownMasterAddress, m_ownSlaveAddress,
|
||||
m_answer?" in answer mode":"");
|
||||
|
||||
do {
|
||||
if (m_device->isValid() && !m_reconnect) {
|
||||
result_t result = handleSymbol();
|
||||
@@ -569,7 +570,6 @@ result_t BusHandler::handleSymbol() {
|
||||
// receive next symbol (optionally check reception of sent symbol)
|
||||
symbol_t recvSymbol;
|
||||
result = m_device->recv(timeout+m_transferLatency, recvSymbol);
|
||||
|
||||
if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0
|
||||
&& timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) {
|
||||
// check if acting as AUTO-SYN generator is required
|
||||
@@ -1102,6 +1102,21 @@ void BusHandler::receiveCompleted() {
|
||||
logNotice(lf_update, "unknown MM cmd: %s", m_command.getStr().c_str());
|
||||
} else {
|
||||
logNotice(lf_update, "unknown MS cmd: %s / %s", m_command.getStr().c_str(), m_response.getStr().c_str());
|
||||
if (m_command.size() >= 5 && m_command[2] == 0x07 && m_command[3] == 0x04) {
|
||||
Message* message = m_messages->getScanMessage(dstAddress);
|
||||
if (message && (message->getLastUpdateTime() == 0 || message->getLastSlaveData().getDataSize() < 10)) {
|
||||
result_t result = message->storeLastData(m_command, m_response);
|
||||
if (result == RESULT_OK) {
|
||||
ostringstream output;
|
||||
result = message->decodeLastData(output, 0, true);
|
||||
if (result == RESULT_OK) {
|
||||
string str = output.str();
|
||||
setScanResult(dstAddress, 0, str);
|
||||
}
|
||||
}
|
||||
logNotice(lf_update, "store %2.2x ident: %s", dstAddress, getResultCode(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_messages->invalidateCache(message);
|
||||
@@ -1141,7 +1156,9 @@ result_t BusHandler::prepareScan(symbol_t slave, bool full, string levels, bool&
|
||||
if (scanMessage == NULL) {
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
if (m_device->isReadOnly()) {
|
||||
return RESULT_OK;
|
||||
}
|
||||
deque<Message*> messages = m_messages->findAll("scan", "", levels, true);
|
||||
auto it = messages.begin();
|
||||
while (it != messages.end()) {
|
||||
@@ -1463,6 +1480,7 @@ result_t BusHandler::scanAndWait(symbol_t dstAddress, bool loadScanConfig, bool
|
||||
result = loadScanConfigFile(m_messages, dstAddress, file);
|
||||
}
|
||||
if (result == RESULT_OK) {
|
||||
executeInstructions(m_messages);
|
||||
setScanConfigLoaded(dstAddress, file);
|
||||
if (!hasAdditionalScanMessages && m_messages->hasAdditionalScanMessages()) {
|
||||
// additional scan messages now available
|
||||
@@ -1499,7 +1517,7 @@ void BusHandler::formatGrabResult(const bool unknown, ostringstream& output, con
|
||||
}
|
||||
}
|
||||
|
||||
symbol_t BusHandler::getNextScanAddress(symbol_t lastAddress, bool onlyScanned) {
|
||||
symbol_t BusHandler::getNextScanAddress(symbol_t lastAddress) {
|
||||
if (lastAddress == SYN) {
|
||||
return SYN;
|
||||
}
|
||||
@@ -1507,22 +1525,14 @@ symbol_t BusHandler::getNextScanAddress(symbol_t lastAddress, bool onlyScanned)
|
||||
if (!isValidAddress(lastAddress, false) || isMaster(lastAddress)) {
|
||||
continue;
|
||||
}
|
||||
if (onlyScanned) {
|
||||
if ((m_seenAddresses[lastAddress]&(LOAD_INIT|SCAN_DONE)) == SCAN_DONE) {
|
||||
return lastAddress;
|
||||
}
|
||||
} else if ((m_seenAddresses[lastAddress]&(SEEN|LOAD_INIT)) == SEEN) {
|
||||
if ((m_seenAddresses[lastAddress]&(SEEN|LOAD_INIT)) == SEEN) {
|
||||
return lastAddress;
|
||||
}
|
||||
symbol_t master = getMasterAddress(lastAddress);
|
||||
if (master == SYN || (m_seenAddresses[master]&SEEN) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (onlyScanned) {
|
||||
if ((m_seenAddresses[lastAddress]&(LOAD_INIT|SCAN_DONE)) == SCAN_DONE) {
|
||||
return lastAddress;
|
||||
}
|
||||
} else if ((m_seenAddresses[lastAddress]&LOAD_INIT) == 0) {
|
||||
if ((m_seenAddresses[lastAddress]&LOAD_INIT) == 0) {
|
||||
return lastAddress;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -413,6 +413,19 @@ class BusHandler : public WaitThread {
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Inject a message from outside and treat it as regularly retrieved from the bus.
|
||||
* @param master the @a MasterSymbolString with the master data.
|
||||
* @param slave the @a SlaveSymbolString with the slave data.
|
||||
*/
|
||||
void injectMessage(MasterSymbolString& master, SlaveSymbolString& slave) {
|
||||
m_command = master;
|
||||
m_response = slave;
|
||||
m_addressConflict = true; // avoid conflict messages
|
||||
receiveCompleted();
|
||||
m_addressConflict = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message on the bus and wait for the answer.
|
||||
* @param master the @a MasterSymbolString with the master data to send.
|
||||
@@ -489,7 +502,7 @@ class BusHandler : public WaitThread {
|
||||
* Send a scan message on the bus and wait for the answer.
|
||||
* @param dstAddress the destination slave address to send to.
|
||||
* @param loadScanConfig true to immediately load the message definitions matching the scan result.
|
||||
* @param reload true to fully reload the scan results.
|
||||
* @param reload true to fully reload the scan results, false when the slave ID was already retrieved.
|
||||
* @return the result code.
|
||||
*/
|
||||
result_t scanAndWait(symbol_t dstAddress, bool loadScanConfig = false, bool reload = false);
|
||||
@@ -541,10 +554,9 @@ class BusHandler : public WaitThread {
|
||||
/**
|
||||
* Get the next slave address that still needs to be scanned or loaded.
|
||||
* @param lastAddress the last returned slave address, or 0 for returning the first one.
|
||||
* @param onlyScanned true to return only already scanned addresses.
|
||||
* @return the next slave address that still needs to be scanned or loaded, or @a SYN.
|
||||
*/
|
||||
symbol_t getNextScanAddress(symbol_t lastAddress, bool onlyScanned = false);
|
||||
symbol_t getNextScanAddress(symbol_t lastAddress);
|
||||
|
||||
/**
|
||||
* Set the state of the participant to configuration @a LOADED.
|
||||
|
||||
+70
-39
@@ -86,11 +86,12 @@ static struct options opt = {
|
||||
|
||||
CONFIG_PATH, // configPath
|
||||
false, // scanConfig
|
||||
BROADCAST, // initialScan
|
||||
0, // initialScan
|
||||
getenv("LANG"), // preferLanguage
|
||||
false, // checkConfig
|
||||
false, // dumpConfig
|
||||
5, // pollInterval
|
||||
false, // injectMessages
|
||||
|
||||
0x31, // address
|
||||
false, // answer
|
||||
@@ -177,13 +178,16 @@ static const struct argp_option argpoptions[] = {
|
||||
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH [" CONFIG_PATH "]", 0 },
|
||||
{"scanconfig", 's', "ADDR", OPTION_ARG_OPTIONAL, "Pick CSV config files matching initial scan (ADDR="
|
||||
"\"none\" or empty for no initial scan message, \"full\" for full scan, or a single hex address to scan, "
|
||||
"default is broadcast ident message). If combined with --checkconfig, you can add scan message data as "
|
||||
"arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\".", 0 },
|
||||
"default is broadcast ident message). If combined with --checkconfig and --inject, you can add scan message "
|
||||
"data as arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\".",
|
||||
0 },
|
||||
{"configlang", O_CFGLNG, "LANG", 0,
|
||||
"Prefer LANG in multilingual configuration files [system default language]", 0 },
|
||||
{"checkconfig", O_CHKCFG, NULL, 0, "Check CSV config files, then stop", 0 },
|
||||
{"dumpconfig", O_DMPCFG, NULL, 0, "Check and dump CSV config files, then stop", 0 },
|
||||
{"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]", 0 },
|
||||
{"inject", 'i', NULL, 0, "Inject remaining arguments as already seen messages (e.g. "
|
||||
"\"FF08070400/0AB5454850303003277201\")", 0 },
|
||||
|
||||
{NULL, 0, NULL, 0, "eBUS options:", 3 },
|
||||
{"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 },
|
||||
@@ -261,13 +265,18 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
opt->noDeviceCheck = true;
|
||||
break;
|
||||
case 'r': // --readonly
|
||||
if (opt->answer || opt->generateSyn) {
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn");
|
||||
if (opt->answer || opt->generateSyn || opt->initialSend
|
||||
|| (opt->scanConfig && opt->initialScan != 0 && opt->initialScan != ESC)) {
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->readOnly = true;
|
||||
break;
|
||||
case O_INISND: // --initsend
|
||||
if (opt->readOnly) {
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->initialSend = true;
|
||||
break;
|
||||
case O_DEVLAT: // --latency=10000
|
||||
@@ -307,6 +316,10 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
opt->initialScan = getSlaveAddress(opt->initialScan);
|
||||
}
|
||||
}
|
||||
if (opt->readOnly && opt->initialScan != ESC) {
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case O_CFGLNG: // --configlang=LANG
|
||||
@@ -330,6 +343,9 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case 'i': // --inject
|
||||
opt->injectMessages = true;
|
||||
break;
|
||||
|
||||
// eBUS options:
|
||||
case 'a': // --address=31
|
||||
@@ -341,7 +357,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
break;
|
||||
case O_ANSWER: // --answer
|
||||
if (opt->readOnly) {
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn");
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->answer = true;
|
||||
@@ -383,7 +399,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
break;
|
||||
case O_GENSYN: // --generatesyn
|
||||
if (opt->readOnly) {
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn");
|
||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->generateSyn = true;
|
||||
@@ -539,7 +555,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
break;
|
||||
|
||||
case ARGP_KEY_ARG:
|
||||
if (!opt->checkConfig) {
|
||||
if (!opt->injectMessages) {
|
||||
argp_error(state, "invalid arguments starting with \"%s\"", arg);
|
||||
return EINVAL;
|
||||
}
|
||||
@@ -830,11 +846,6 @@ void readMessage(Message* message) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for executing all loaded and resolvable instructions.
|
||||
* @param messages the @a MessageMap instance.
|
||||
* @param verbose whether to verbosely log all problems.
|
||||
*/
|
||||
void executeInstructions(MessageMap* messages, bool verbose) {
|
||||
string errorDescription;
|
||||
result_t result = messages->resolveConditions(errorDescription, verbose);
|
||||
@@ -875,13 +886,12 @@ result_t loadConfigFiles(MessageMap* messages, bool verbose, bool denyRecursive)
|
||||
logError(lf_main, "error reading config files: %s, last error: %s", getResultCode(result),
|
||||
errorDescription.c_str());
|
||||
}
|
||||
executeInstructions(messages, verbose);
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
result_t loadScanConfigFile(MessageMap* messages, symbol_t address, string& relativeFile, bool verbose) {
|
||||
Message* message = messages->getScanMessage(address);
|
||||
if (!message) {
|
||||
if (!message || message->getLastUpdateTime() == 0) {
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
const SlaveSymbolString& data = message->getLastSlaveData();
|
||||
@@ -1042,10 +1052,33 @@ result_t loadScanConfigFile(MessageMap* messages, symbol_t address, string& rela
|
||||
}
|
||||
logNotice(lf_main, "read scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d", best.c_str(), ident.c_str(), sw, hw);
|
||||
relativeFile = best.substr(strlen(opt.configPath)+1);
|
||||
executeInstructions(messages, verbose);
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
bool parseMessage(const string& arg, MasterSymbolString& master, SlaveSymbolString& slave, bool onlyMasterSlave) {
|
||||
size_t pos = arg.find_first_of('/');
|
||||
if (pos == string::npos) {
|
||||
logError(lf_main, "invalid message %s: missing \"/\"", arg.c_str());
|
||||
return false;
|
||||
}
|
||||
result_t result = master.parseHex(arg.substr(0, pos));
|
||||
if (result == RESULT_OK) {
|
||||
result = slave.parseHex(arg.substr(pos+1));
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
logError(lf_main, "invalid message %s: %s", arg.c_str(), getResultCode(result));
|
||||
return false;
|
||||
}
|
||||
if (master.size() < 5) { // skip QQ ZZ PB SB NN
|
||||
logError(lf_main, "invalid message %s: master part too short", arg.c_str());
|
||||
return false;
|
||||
}
|
||||
if (!isMaster(master[0])) {
|
||||
logError(lf_main, "invalid message %s: QQ is no master", arg.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function.
|
||||
@@ -1063,6 +1096,9 @@ int main(int argc, char* argv[]) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (!opt.readOnly && opt.scanConfig && opt.initialScan == 0) {
|
||||
opt.initialScan = BROADCAST;
|
||||
}
|
||||
if (opt.logAreas != -1 || opt.logLevel != ll_COUNT) {
|
||||
setFacilitiesLogLevel(LF_ALL, ll_none);
|
||||
setFacilitiesLogLevel(opt.logAreas, opt.logLevel);
|
||||
@@ -1073,27 +1109,12 @@ int main(int argc, char* argv[]) {
|
||||
logNotice(lf_main, PACKAGE_STRING "." REVISION " performing configuration check...");
|
||||
|
||||
result_t result = loadConfigFiles(s_messageMap, true, opt.scanConfig && arg_index < argc);
|
||||
|
||||
executeInstructions(s_messageMap, true);
|
||||
MasterSymbolString master;
|
||||
SlaveSymbolString slave;
|
||||
while (result == RESULT_OK && opt.scanConfig && arg_index < argc) {
|
||||
// check scan config for each passed ident message
|
||||
string arg = argv[arg_index++];
|
||||
size_t pos = arg.find_first_of('/');
|
||||
if (pos == string::npos) {
|
||||
logError(lf_main, "invalid scan message %s: missing \"/\"", arg.c_str());
|
||||
continue;
|
||||
}
|
||||
MasterSymbolString master;
|
||||
SlaveSymbolString slave;
|
||||
result_t res = master.parseHex(arg.substr(0, pos));
|
||||
if (res == RESULT_OK) {
|
||||
res = slave.parseHex(arg.substr(pos+1));
|
||||
}
|
||||
if (res != RESULT_OK) {
|
||||
logError(lf_main, "invalid scan message %s: %s", arg.c_str(), getResultCode(res));
|
||||
continue;
|
||||
}
|
||||
if (master.size() < 5) { // skip QQ ZZ PB SB NN
|
||||
logError(lf_main, "invalid scan message %s: master part too short", arg.c_str());
|
||||
if (!parseMessage(argv[arg_index++], master, slave, true)) {
|
||||
continue;
|
||||
}
|
||||
symbol_t address = master[1];
|
||||
@@ -1103,7 +1124,8 @@ int main(int argc, char* argv[]) {
|
||||
} else {
|
||||
message->storeLastData(master, slave);
|
||||
string file;
|
||||
res = loadScanConfigFile(s_messageMap, address, file, true);
|
||||
result_t res = loadScanConfigFile(s_messageMap, address, file, true);
|
||||
executeInstructions(s_messageMap, true);
|
||||
if (res == RESULT_OK) {
|
||||
logInfo(lf_main, "scan config %2.2x: file %s loaded", address, file.c_str());
|
||||
}
|
||||
@@ -1139,12 +1161,21 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// load configuration files
|
||||
loadConfigFiles(s_messageMap);
|
||||
if (s_messageMap->sizeConditions() > 0 && opt.pollInterval == 0) {
|
||||
logError(lf_main, "conditions require a poll interval > 0");
|
||||
}
|
||||
|
||||
// create the MainLoop and start it
|
||||
s_mainLoop = new MainLoop(opt, device, s_messageMap);
|
||||
if (opt.injectMessages) {
|
||||
BusHandler* busHandler = s_mainLoop->getBusHandler();
|
||||
MasterSymbolString master;
|
||||
SlaveSymbolString slave;
|
||||
while (arg_index < argc) {
|
||||
// add each passed message
|
||||
if (!parseMessage(argv[arg_index++], master, slave, false)) {
|
||||
continue;
|
||||
}
|
||||
busHandler->injectMessage(master, slave);
|
||||
}
|
||||
}
|
||||
s_mainLoop->start("mainloop");
|
||||
|
||||
// wait for end of MainLoop
|
||||
|
||||
@@ -49,6 +49,7 @@ struct options {
|
||||
bool checkConfig; //!< check CSV config files, then stop
|
||||
bool dumpConfig; //!< dump CSV config files, then stop
|
||||
unsigned int pollInterval; //!< poll interval in seconds, 0 to disable [5]
|
||||
bool injectMessages; //!< inject remaining arguments as already seen messages
|
||||
|
||||
symbol_t address; //!< own bus address [31]
|
||||
bool answer; //!< answer to requests from other masters
|
||||
@@ -111,6 +112,13 @@ result_t loadConfigFiles(MessageMap* messages, bool verbose = false, bool denyRe
|
||||
*/
|
||||
result_t loadScanConfigFile(MessageMap* messages, symbol_t address, string& relativeFile, bool verbose = false);
|
||||
|
||||
/**
|
||||
* Helper method for executing all loaded and resolvable instructions.
|
||||
* @param messages the @a MessageMap instance.
|
||||
* @param verbose whether to verbosely log all problems.
|
||||
*/
|
||||
void executeInstructions(MessageMap* messages, bool verbose = false);
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
#endif // EBUSD_MAIN_H_
|
||||
|
||||
@@ -99,8 +99,8 @@ result_t UserList::addFromFile(map<string, string>& row, vector< map<string, str
|
||||
|
||||
MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* messages)
|
||||
: Thread(), m_device(device), m_reconnectCount(0), m_userList(opt.accessLevel), m_messages(messages),
|
||||
m_address(opt.address), m_scanConfig(opt.scanConfig),
|
||||
m_initialScan(opt.initialScan), m_enableHex(opt.enableHex), m_shutdown(false) {
|
||||
m_address(opt.address), m_scanConfig(opt.scanConfig), m_initialScan(opt.readOnly ? ESC : opt.initialScan),
|
||||
m_polling(opt.pollInterval>0), m_enableHex(opt.enableHex), m_shutdown(false) {
|
||||
// open Device
|
||||
result_t result = m_device->open();
|
||||
if (result != RESULT_OK) {
|
||||
@@ -287,6 +287,13 @@ void MainLoop::run() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (reload && m_busHandler->hasSignal()) {
|
||||
reload = false;
|
||||
// execute initial instructions
|
||||
executeInstructions(m_messages);
|
||||
if (m_messages->sizeConditions() > 0 && !m_polling) {
|
||||
logError(lf_main, "conditions require a poll interval > 0");
|
||||
}
|
||||
}
|
||||
if (!m_shutdown && now > nextCheckRun) {
|
||||
TCPClient client;
|
||||
|
||||
@@ -349,6 +349,9 @@ class MainLoop : public Thread, DeviceListener {
|
||||
* (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address). */
|
||||
const symbol_t m_initialScan;
|
||||
|
||||
/** true when the poll interval is non zero. */
|
||||
const bool m_polling;
|
||||
|
||||
/** whether to enable the hex command. */
|
||||
const bool m_enableHex;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user