From 33ee8ae0ed4e3d57bf0820f4a73268b50e106049 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 29 Apr 2017 15:13:05 +0200 Subject: [PATCH] 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 --- src/ebusd/bushandler.cpp | 36 ++++++++----- src/ebusd/bushandler.h | 18 +++++-- src/ebusd/main.cpp | 109 +++++++++++++++++++++++++-------------- src/ebusd/main.h | 8 +++ src/ebusd/mainloop.cpp | 11 +++- src/ebusd/mainloop.h | 3 ++ 6 files changed, 128 insertions(+), 57 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 24e77453..2ae63e3e 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -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 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; } } diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index d94b0911..f05bc6aa 100644 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -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. diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 552494f9..dea0e5b7 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -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 diff --git a/src/ebusd/main.h b/src/ebusd/main.h index 7bbdb0f9..21826b4e 100644 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -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_ diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 4d4e058a..72bd7ac4 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -99,8 +99,8 @@ result_t UserList::addFromFile(map& row, vector< map0), 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; diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index d9d558d2..b8a28b31 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -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;