diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index ff066960..0b2ad941 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -669,11 +669,11 @@ void signalHandler(int sig) { break; case SIGINT: logNotice(lf_main, "SIGINT received"); - shutdown(); + s_mainLoop->shutdown(); break; case SIGTERM: logNotice(lf_main, "SIGTERM received"); - shutdown(); + s_mainLoop->shutdown(); break; default: logNotice(lf_main, "undefined signal %s", strsignal(sig)); diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 10ce22aa..333a719b 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -100,7 +100,7 @@ result_t UserList::addFromFile(map& row, vector< mapopen(); if (result != RESULT_OK) { @@ -120,6 +120,9 @@ MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* message m_logRawFile = NULL; } m_logRawEnabled = opt.logRaw; + m_logRawBytes = false; + m_logRawLastReceived = true; + m_logRawLastSymbol = SYN; if (opt.aclFile[0]) { string errorDescription; result_t result = m_userList.readFromFile(opt.aclFile, errorDescription); @@ -152,6 +155,7 @@ MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* message } MainLoop::~MainLoop() { + m_shutdown = true; join(); for (list::iterator it = m_dataHandlers.begin(); it != m_dataHandlers.end(); it++) { @@ -208,7 +212,7 @@ void MainLoop::run() { } (*it)->start(); } - while (true) { + while (!m_shutdown) { // pick the next message to handle NetMessage* netMessage = m_netQueue.pop(taskDelay); time(&now); @@ -437,14 +441,40 @@ void MainLoop::notifyDeviceData(const symbol_t symbol, bool received) { if (received && m_dumpFile) { m_dumpFile->write((unsigned char*)&symbol, 1); } - if (m_logRawFile) { - m_logRawFile->write((unsigned char*)&symbol, 1, received); - } else if (m_logRawEnabled) { - if (received) { - logNotice(lf_bus, "<%02x", symbol); - } else { - logNotice(lf_bus, ">%02x", symbol); + if (!m_logRawFile && !m_logRawEnabled) { + return; + } + if (m_logRawBytes) { + if (m_logRawFile) { + m_logRawFile->write((unsigned char*)&symbol, 1, received); + } else if (m_logRawEnabled) { + if (received) { + logNotice(lf_bus, "<%02x", symbol); + } else { + logNotice(lf_bus, ">%02x", symbol); + } } + return; + } + if (symbol != SYN) { + if (received && !m_logRawLastReceived && symbol == m_logRawLastSymbol) { + return; // skip received echo of previously sent symbol + } + if (m_logRawBuffer.tellp() == 0 || received != m_logRawLastReceived) { + m_logRawLastReceived = received; + m_logRawBuffer << (received ? "<" : ">"); + } + m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast(symbol); + m_logRawLastSymbol = symbol; + } + if (symbol == SYN && m_logRawBuffer.tellp() > 0) { // flush + if (m_logRawFile) { + const char* str = m_logRawBuffer.str().c_str(); + m_logRawFile->write((const unsigned char*)str, strlen(str), received, false); + } else { + logNotice(lf_bus, m_logRawBuffer.str().c_str()); + } + m_logRawBuffer.str(""); } } @@ -608,7 +638,7 @@ string MainLoop::executeAuth(vector &args, string &user) { string MainLoop::executeRead(vector &args, const string levels) { size_t argPos = 1; - bool hex = false, numeric = false; + bool hex = false, numeric = false, valueName = false; OutputFormat verbosity = 0; time_t maxAge = 5*60; string circuit, params; @@ -637,6 +667,9 @@ string MainLoop::executeRead(vector &args, const string levels) { verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; } else if (args[argPos] == "-n") { numeric = true; + } else if (args[argPos] == "-N") { + numeric = true; + valueName = true; } else if (args[argPos] == "-m") { argPos++; if (args.size() > argPos) { @@ -764,7 +797,7 @@ string MainLoop::executeRead(vector &args, const string levels) { return getResultCode(ret); } if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2) { - return "usage: read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-c CIRCUIT] [-p PRIO] [-v|-V] [-n] [-i VALUE[;VALUE]*]" + return "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" @@ -777,6 +810,7 @@ string MainLoop::executeRead(vector &args, const string levels) { " -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" @@ -808,7 +842,8 @@ string MainLoop::executeRead(vector &args, const string levels) { if (message != NULL && pollPriority > 0 && message->setPollPriority(pollPriority)) { m_messages->addPollMessage(message); } - + verbosity |= valueName ? OF_VALUENAME : numeric ? OF_NUMERIC : 0; + result_t ret; if (srcAddress == SYN && dstAddress == SYN && maxAge > 0 && params.length() == 0) { Message* cacheMessage = m_messages->find(circuit, args[argPos], levels, false, true); bool hasCache = cacheMessage != NULL; @@ -821,8 +856,8 @@ string MainLoop::executeRead(vector &args, const string levels) { if (verbosity & OF_NAMES) { result << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " "; } - result_t ret = cacheMessage->decodeLastData(result, verbosity|(numeric?OF_NUMERIC:0), false, - fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex); + ret = cacheMessage->decodeLastData(result, verbosity, false, fieldIndex == -2 ? NULL : fieldName.c_str(), + fieldIndex); if (ret != RESULT_OK) { if (ret < RESULT_OK) { logError(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), @@ -847,15 +882,15 @@ string MainLoop::executeRead(vector &args, const string levels) { return getResultCode(RESULT_ERR_INVALID_ADDR); } // read directly from bus - result_t ret = m_busHandler->readFromBus(message, params, dstAddress, srcAddress); + ret = m_busHandler->readFromBus(message, params, dstAddress, srcAddress); if (ret != RESULT_OK) { return getResultCode(ret); } if (verbosity & OF_NAMES) { result << message->getCircuit() << " " << message->getName() << " "; } - ret = message->decodeLastSlaveData(result, verbosity|(numeric?OF_NUMERIC:0), false, - fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex); + ret = message->decodeLastSlaveData(result, verbosity, false, fieldIndex == -2 ? NULL : fieldName.c_str(), + fieldIndex); if (ret < RESULT_OK) { logError(lf_main, "read %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); @@ -1426,11 +1461,13 @@ string MainLoop::executeLog(vector &args) { } string MainLoop::executeRaw(vector &args) { - if (args.size() != 1) { - return "usage: raw\n" - " Toggle logging of each byte."; + bool bytes = args.size() == 2 && args[1] == "bytes"; + if (args.size() != 1 && !bytes) { + return "usage: raw [bytes]\n" + " Toggle logging of messages or each byte."; } bool enabled; + m_logRawBytes = bytes; if (m_logRawFile) { enabled = !m_logRawFile->isEnabled(); m_logRawFile->setEnabled(enabled); @@ -1509,7 +1546,7 @@ string MainLoop::executeQuit(vector &args, bool& connected) { string MainLoop::executeHelp() { return "usage:\n" - " read|r Read value(s): read [-f] [-m SECONDS] [-s QQ] [-d ZZ] [-c CIRCUIT] [-p PRIO] [-v|-V] [-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" " 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" @@ -1526,7 +1563,7 @@ string MainLoop::executeHelp() { " scan Scan slaves: scan [full|ZZ]\n" " Report scan result: scan result\n" " log Set log area level: log [AREA[,AREA]* LEVEL]\n" - " raw Toggle logging of each byte\n" + " raw Toggle logging of messages or each byte.\n" " dump Toggle binary dump of received bytes\n" " reload Reload CSV config files\n" " quit|q Close connection\n" @@ -1535,7 +1572,7 @@ string MainLoop::executeHelp() { string MainLoop::executeGet(vector &args, bool& connected) { result_t ret = RESULT_OK; - bool numeric = false, required = false, full = false; + bool numeric = false, valueName = false, required = false, full = false; OutputFormat verbosity = OF_NAMES; size_t argPos = 1; string uri = args[argPos++]; @@ -1585,6 +1622,8 @@ string MainLoop::executeGet(vector &args, bool& connected) { } } else if (qname == "numeric") { numeric = value.length() == 0 || value == "1"; + } else if (qname == "valuename") { + valueName = value.length() == 0 || value == "1"; } else if (qname == "full") { full = value.length() == 0 || value == "1"; } else if (qname == "required") { @@ -1608,9 +1647,8 @@ string MainLoop::executeGet(vector &args, bool& connected) { time_t maxLastUp = 0; if (ret == RESULT_OK) { deque messages = m_messages->findAll(circuit, name, getUserLevels(user), exact, true, false, true); - bool first = true; - verbosity |= (numeric ? OF_NUMERIC : 0) | OF_JSON | (full ? OF_ALL_ATTRS : 0); + verbosity |= (valueName ? OF_VALUENAME : numeric ? OF_NUMERIC : 0) | OF_JSON | (full ? OF_ALL_ATTRS : 0); for (deque::iterator it = messages.begin(); it != messages.end();) { Message* message = *it++; symbol_t dstAddress = message->getDstAddress(); diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index db3b4f1d..d9d558d2 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -112,6 +112,11 @@ class MainLoop : public Thread, DeviceListener { */ ~MainLoop(); + /** + * Shutdown the main loop. + */ + void shutdown() { m_shutdown = true; } + /** * Get the @a BusHandler instance. * @return the created @a BusHandler instance. @@ -313,6 +318,18 @@ class MainLoop : public Thread, DeviceListener { /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */ bool m_logRawEnabled; + /** whether to log raw bytes instead of messages with @a m_logRawEnabled. */ + bool m_logRawBytes; + + /** the buffer for building log raw message. */ + ostringstream m_logRawBuffer; + + /** true when the last byte in @a m_logRawBuffer was receive, false if it was sent. */ + bool m_logRawLastReceived; + + /** the last sent/received symbol.*/ + symbol_t m_logRawLastSymbol; + /** the @a RotateFile for dumping received data, or NULL. */ RotateFile* m_dumpFile; @@ -335,6 +352,9 @@ class MainLoop : public Thread, DeviceListener { /** whether to enable the hex command. */ const bool m_enableHex; + /** set to true to shutdown. */ + bool m_shutdown; + /** the created @a BusHandler instance. */ BusHandler* m_busHandler; diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 0220b959..9ccddcf9 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -718,8 +718,16 @@ result_t ValueListDataField::readSymbols(const SymbolString& input, } else if (outputFormat & OF_NUMERIC) { output << setw(0) << dec << static_cast(value); } else if (outputFormat & OF_JSON) { - output << '"' << it->second << '"'; + if (outputFormat & OF_VALUENAME) { + output << "{\"value\":" << setw(0) << dec << static_cast(value); + output << ",\"name\":\"" << it->second << "\"}"; + } else { + output << '"' << it->second << '"'; + } } else { + if (outputFormat & OF_VALUENAME) { + output << setw(0) << dec << static_cast(value) << '='; + } output << it->second; } return RESULT_OK; diff --git a/src/lib/ebus/datatype.h b/src/lib/ebus/datatype.h index 019a2466..7b595694 100644 --- a/src/lib/ebus/datatype.h +++ b/src/lib/ebus/datatype.h @@ -94,14 +94,17 @@ typedef int OutputFormat; /** bit flag for @a OutputFormat: numeric format (keep numeric value of value=name pairs). */ #define OF_NUMERIC 0x08 +/** bit flag for @a OutputFormat: value=name format for such pairs. */ +#define OF_VALUENAME 0x10 + /** bit flag for @a OutputFormat: JSON format. */ -#define OF_JSON 0x10 +#define OF_JSON 0x20 /** bit flag for @a OutputFormat: short format (only name and value, no indentation). */ -#define OF_SHORT 0x20 +#define OF_SHORT 0x40 /** bit flag for @a OutputFormat: include all attributes. */ -#define OF_ALL_ATTRS 0x40 +#define OF_ALL_ATTRS 0x80 /** the message part in which a data field is stored. */ enum PartType { diff --git a/src/lib/utils/rotatefile.cpp b/src/lib/utils/rotatefile.cpp index 57f58882..0133b61f 100644 --- a/src/lib/utils/rotatefile.cpp +++ b/src/lib/utils/rotatefile.cpp @@ -54,7 +54,7 @@ bool RotateFile::setEnabled(bool enabled) { return true; } -void RotateFile::write(unsigned char* value, unsigned int size, bool received) { +void RotateFile::write(const unsigned char* value, const size_t size, const bool received, const bool bytes) { if (!m_enabled || !m_stream) { return; } @@ -63,12 +63,16 @@ void RotateFile::write(unsigned char* value, unsigned int size, bool received) { struct tm td; clockGettime(&ts); localtime_r(&ts.tv_sec, &td); - fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c", + fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld ", td.tm_year+1900, td.tm_mon+1, td.tm_mday, - td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, - received ? '<' : '>'); - for (unsigned int pos = 0; pos < size; pos++) { - fprintf(m_stream, "%2.2x ", value[pos]); + td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000); + if (bytes) { + fprintf(m_stream, received ? "<" : ">"); + for (unsigned int pos = 0; pos < size; pos++) { + fprintf(m_stream, "%2.2x ", value[pos]); + } + } else { + fwrite(value, 1, size, m_stream); } fprintf(m_stream, "\n"); m_fileSize += 25+3*size+1; diff --git a/src/lib/utils/rotatefile.h b/src/lib/utils/rotatefile.h index 57236f6c..ee111bf6 100644 --- a/src/lib/utils/rotatefile.h +++ b/src/lib/utils/rotatefile.h @@ -57,7 +57,7 @@ class RotateFile { * @param enabled @p true to enable writing to the file, @p false to disable it. * @return @p true when the state was changed, @p false otherwise. */ - bool setEnabled(bool enabled = true); + bool setEnabled(bool enabled); /** * Return whether writing to the file is enabled. @@ -70,8 +70,10 @@ class RotateFile { * @param value the pointer to the bytes to write. * @param size the number of bytes to write. * @param received @a true on reception, @a false on sending (only relevant in text mode). + * @param bytes whether to log single bytes (only relevant in text mode). */ - void write(unsigned char* value, unsigned int size, bool received = true); + void write(const unsigned char* value, const size_t size, const bool received = true, + const bool bytes = true); private: