From 607fc00c0ec40ef968e200b6b042cd8b2392a4db Mon Sep 17 00:00:00 2001 From: John Date: Sat, 4 Nov 2023 08:50:18 +0100 Subject: [PATCH] move raw logging+dump to proto, more abstraction --- src/ebusd/Makefile.am | 4 +- src/ebusd/bushandler.h | 34 +++------- src/ebusd/main.cpp | 79 +++++++++++++++++----- src/ebusd/main.h | 2 - src/ebusd/mainloop.cpp | 139 +++----------------------------------- src/ebusd/mainloop.h | 56 +++------------ src/ebusd/mqtthandler.cpp | 2 +- src/lib/ebus/protocol.cpp | 122 ++++++++++++++++++++++++++++++++- src/lib/ebus/protocol.h | 99 +++++++++++++++++++++++++-- 9 files changed, 305 insertions(+), 232 deletions(-) diff --git a/src/ebusd/Makefile.am b/src/ebusd/Makefile.am index 174750a5..473c766f 100644 --- a/src/ebusd/Makefile.am +++ b/src/ebusd/Makefile.am @@ -14,8 +14,8 @@ ebusd_SOURCES = \ main.h main.cpp main_args.cpp -ebusd_LDADD = ../lib/utils/libutils.a \ - ../lib/ebus/libebus.a \ +ebusd_LDADD = ../lib/ebus/libebus.a \ + ../lib/utils/libutils.a \ -lpthread \ @EXTRA_LIBS@ diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index b7405ca8..fe6527b7 100755 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -29,7 +29,6 @@ #include "lib/ebus/data.h" #include "lib/ebus/symbol.h" #include "lib/ebus/result.h" -#include "lib/ebus/device.h" #include "lib/ebus/protocol.h" namespace ebusd { @@ -248,25 +247,15 @@ class BusHandler : public ProtocolListener { public: /** * Construct a new instance. - * @param device the @a Device instance for accessing the bus. * @param messages the @a MessageMap instance with all known @a Message instances. * @param scanHelper the @a ScanHelper instance. - * @param ownAddress the own master address. - * @param answer whether to answer queries for the own master/slave address. - * @param busLostRetries the number of times a send is repeated due to lost arbitration. - * @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration). - * @param busAcquireTimeout the maximum time in milliseconds for bus acquisition. - * @param slaveRecvTimeout the maximum time in milliseconds an addressed slave is expected to acknowledge. - * @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. - * @param generateSyn whether to enable AUTO-SYN symbol generation. * @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled. */ - BusHandler(Device* device, MessageMap* messages, ScanHelper* scanHelper, - const ebus_protocol_config_t config, unsigned int pollInterval) - : m_messages(messages), m_scanHelper(scanHelper), + BusHandler(MessageMap* messages, ScanHelper* scanHelper, + unsigned int pollInterval) + : m_protocol(nullptr), m_messages(messages), m_scanHelper(scanHelper), m_pollInterval(pollInterval), m_lastPoll(0), m_runningScans(0), m_grabMessages(true) { - m_protocol = ProtocolHandler::create(config, device, this); memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); } @@ -274,22 +263,19 @@ class BusHandler : public ProtocolListener { * Destructor. */ virtual ~BusHandler() { - if (m_protocol) { - delete m_protocol; - m_protocol = nullptr; - } } + /** + * Set the @a ProtocolHandler instance for accessing the bus. + * @param protocol the @a ProtocolHandler instance for accessing the bus. + */ + void setProtocol(ProtocolHandler* protocol) { m_protocol = protocol; } + /** * @return the @a ProtocolHandler instance for accessing the bus. */ ProtocolHandler* getProtocol() const { return m_protocol; } - /** - * @return the @a Device instance for accessing the bus. - */ - const Device* getDevice() const { return m_protocol->getDevice(); } - /** * Clear stored values (e.g. scan results). */ @@ -433,7 +419,7 @@ class BusHandler : public ProtocolListener { */ result_t prepareScan(symbol_t slave, bool full, const string& levels, bool* reload, ScanRequest** request); - /** the @a ProtocolHandler instance for accessing the bus. */ + /** the @a ProtocolHandler instance for accessing the bus (loosely coupled but set quickly after construction). */ ProtocolHandler* m_protocol; /** the @a MessageMap instance with all known @a Message instances. */ diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index c404edf2..871fd24a 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "ebusd/bushandler.h" #include "ebusd/mainloop.h" #include "ebusd/network.h" #include "lib/utils/log.h" @@ -37,16 +38,14 @@ namespace ebusd { -using std::dec; -using std::hex; -using std::setfill; -using std::setw; -using std::nouppercase; using std::cout; /** the previous config path part to rewrite to the current one. */ #define PREVIOUS_CONFIG_PATH_SUFFIX "://ebusd.eu/config/" +/** the second previous config path part to rewrite to the current one. */ +#define PREVIOUS_CONFIG_PATH_SUFFIX2 "://cfg.ebusd.eu/" + /** the opened PID file, or nullptr. */ static FILE* s_pidFile = nullptr; @@ -59,6 +58,12 @@ static MessageMap* s_messageMap = nullptr; /** the @a ScanHelper instance, or nullptr. */ static ScanHelper* s_scanHelper = nullptr; +/** the @a ProtocolHandler instance, or nullptr. */ +static ProtocolHandler* s_protocol = nullptr; + +/** the @a BusHandler instance, or nullptr. */ +static BusHandler* s_busHandler = nullptr; + /** the @a Request @a Queue instance, or nullptr. */ static Queue* s_requestQueue = nullptr; @@ -143,6 +148,14 @@ void cleanup() { delete s_requestQueue; s_requestQueue = nullptr; } + if (s_protocol != nullptr) { + delete s_protocol; + s_protocol = nullptr; + } + if (s_busHandler != nullptr) { + delete s_busHandler; + s_busHandler = nullptr; + } if (s_messageMap != nullptr) { delete s_messageMap; s_messageMap = nullptr; @@ -369,13 +382,31 @@ int main(int argc, char* argv[], char* envp[]) { return overallResult == RESULT_OK ? EXIT_SUCCESS : EXIT_FAILURE; } - // open the device - Device *device = Device::create(s_opt.device, s_opt.extraLatency, !s_opt.noDeviceCheck); - if (device == nullptr) { - logWrite(lf_main, ll_error, "unable to create device %s", s_opt.device); // force logging on exit + s_busHandler = new BusHandler(s_messageMap, s_scanHelper, s_opt.pollInterval); + + // create the protocol and open the device + ebus_protocol_config_t config = { + .device = s_opt.device, + .noDeviceCheck = s_opt.noDeviceCheck, + .readOnly = s_opt.readOnly, + .extraLatency = s_opt.extraLatency, + .ownAddress = s_opt.address, + .answer = s_opt.answer, + .busLostRetries = s_opt.acquireRetries, + .failedSendRetries = s_opt.sendRetries, + .busAcquireTimeout = s_opt.acquireTimeout, + .slaveRecvTimeout = s_opt.receiveTimeout, + .lockCount = s_opt.masterCount, + .generateSyn = s_opt.generateSyn, + .initialSend = s_opt.initialSend, + }; + s_protocol = ProtocolHandler::create(config, s_busHandler); + if (s_protocol == nullptr) { + logWrite(lf_main, ll_error, "unable to create protocol/device %s", s_opt.device); // force logging on exit cleanup(); return EINVAL; } + s_busHandler->setProtocol(s_protocol); if (!s_opt.foreground) { if (!setLogFile(s_opt.logFile)) { @@ -391,14 +422,28 @@ int main(int argc, char* argv[], char* envp[]) { signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); + if (s_opt.dumpFile[0]) { + s_protocol->setDumpFile(s_opt.dumpFile, s_opt.dumpSize, s_opt.dumpFlush); + if (s_opt.dump) { + s_protocol->toggleDump(); + } + } + if (s_opt.logRawFile[0] && strcmp(s_opt.logRawFile, s_opt.logFile) != 0) { + s_protocol->setLogRawFile(s_opt.logRawFile, s_opt.logRawSize); + } + if (s_opt.logRaw != 0) { + s_protocol->toggleLogRaw(s_opt.logRaw == 2); + } + + // open Device + s_protocol->open(); + // create the MainLoop s_requestQueue = new Queue(); - s_mainLoop = new MainLoop(s_opt, device, s_messageMap, s_scanHelper, s_requestQueue); - BusHandler* busHandler = s_mainLoop->getBusHandler(); - ProtocolHandler* protocol = busHandler->getProtocol(); + s_mainLoop = new MainLoop(s_opt, s_busHandler, s_messageMap, s_scanHelper, s_requestQueue); ostringstream ostream; - protocol->formatInfo(&ostream, false, true); + s_protocol->formatInfo(&ostream, false, true); string deviceInfoStr = ostream.str(); logNotice(lf_main, PACKAGE_STRING "." REVISION " started%s on device: %s", s_opt.scanConfig ? s_opt.initialScan == ESC ? " with auto scan" @@ -420,7 +465,7 @@ int main(int argc, char* argv[], char* envp[]) { if (!s_scanHelper->parseMessage(argv[arg_index], false, &master, &slave)) { continue; } - protocol->injectMessage(master, slave); + s_protocol->injectMessage(master, slave); if (s_opt.scanConfig && master.size() >= 5 && master[4] == 0 && master[2] == 0x07 && master[3] == 0x04 && isValidAddress(master[1], false) && !isMaster(master[1]) && !scanAddresses[master[1]]) { // scan message, simulate scanning @@ -428,11 +473,11 @@ int main(int argc, char* argv[], char* envp[]) { scanAdrCount++; } } - protocol->start("bushandler"); + s_protocol->start("bushandler"); for (symbol_t address = 0; scanAdrCount > 0; address++) { if (scanAddresses[address]) { scanAdrCount--; - busHandler->scanAndWait(address, true); + s_busHandler->scanAndWait(address, true); } } if (s_opt.stopAfterInject) { @@ -440,7 +485,7 @@ int main(int argc, char* argv[], char* envp[]) { return 0; } } else { - protocol->start("bushandler"); + s_protocol->start("bushandler"); } s_mainLoop->start("mainloop"); diff --git a/src/ebusd/main.h b/src/ebusd/main.h index 2935b1ce..d29e6582 100755 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -33,8 +33,6 @@ namespace ebusd { */ /** the config path part behind the scheme (scheme without "://"). */ -//#define CONFIG_PATH_SUFFIX "://ebus.github.io/cfg/de/" - #define CONFIG_PATH_SUFFIX "://cfg.ebusd.eu/" /** A structure holding all program options. */ diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 5da4dbab..aafb7491 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -32,8 +32,6 @@ namespace ebusd { using std::dec; -using std::hex; -using std::setfill; using std::setw; using std::endl; using std::ifstream; @@ -105,41 +103,19 @@ result_t UserList::addFromFile(const string& filename, unsigned int lineNo, map< #define VERBOSITY_4 (VERBOSITY_3 | OF_ALL_ATTRS) -MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messages, ScanHelper* scanHelper, - Queue* requestQueue) - : Thread(), m_device(device), m_reconnectCount(0), m_userList(opt.accessLevel), m_messages(messages), +MainLoop::MainLoop(const struct options& opt, BusHandler* busHandler, + MessageMap* messages, ScanHelper* scanHelper, Queue* requestQueue) + : Thread(), m_busHandler(busHandler), m_protocol(busHandler->getProtocol()), m_reconnectCount(0), + m_userList(opt.accessLevel), m_messages(messages), m_scanHelper(scanHelper), m_address(opt.address), m_scanConfig(opt.scanConfig), m_initialScan(opt.readOnly ? ESC : opt.initialScan), m_scanRetries(opt.scanRetries), m_scanStatus(SCAN_STATUS_NONE), m_polling(opt.pollInterval > 0), m_enableHex(opt.enableHex), m_shutdown(false), m_runUpdateCheck(opt.updateCheck), m_httpClient(), m_requestQueue(requestQueue) { - m_device->setListener(this); - // open Device - result_t result = m_device->open(); - if (result != RESULT_OK) { - logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); - } else if (!m_device->isValid()) { - logError(lf_bus, "device %s not available", m_device->getName()); - } - if (opt.dumpFile[0]) { - m_dumpFile = new RotateFile(opt.dumpFile, opt.dumpSize, false, opt.dumpFlush ? 1 : 16); - m_dumpFile->setEnabled(opt.dump); - } else { - m_dumpFile = nullptr; - } - m_logRawEnabled = opt.logRaw != 0; - if (opt.logRawFile[0] && strcmp(opt.logRawFile, opt.logFile) != 0) { - m_logRawFile = new RotateFile(opt.logRawFile, opt.logRawSize, true); - m_logRawFile->setEnabled(m_logRawEnabled); - } else { - m_logRawFile = nullptr; - } - m_logRawBytes = opt.logRaw == 2; - m_logRawLastReceived = true; - m_logRawLastSymbol = SYN; if (opt.aclFile[0]) { string errorDescription; time_t mtime = 0; istream* stream = FileReader::openFile(opt.aclFile, &errorDescription, &mtime); + result_t result; if (stream) { result = m_userList.readFromStream(stream, opt.aclFile, mtime, false, nullptr, &errorDescription); delete(stream); @@ -150,24 +126,7 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag logError(lf_main, "error reading ACL file \"%s\": %s", opt.aclFile, getResultCode(result)); } } - // create BusHandler - ebus_protocol_config_t config = { - .readOnly = opt.readOnly, - .ownAddress = m_address, - .answer = opt.answer, - .busLostRetries = opt.acquireRetries, - .failedSendRetries = opt.sendRetries, - .busAcquireTimeout = opt.acquireTimeout, - .slaveRecvTimeout = opt.receiveTimeout, - .lockCount = opt.masterCount, - .generateSyn = opt.generateSyn, - .initialSend = opt.initialSend, - }; - m_busHandler = new BusHandler(m_device, m_messages, scanHelper, - config, opt.pollInterval); - m_protocol = m_busHandler->getProtocol(); - // create network m_htmlPath = opt.htmlPath; logInfo(lf_main, "registering data handlers"); if (datahandler_register(&m_userList, m_busHandler, messages, &m_dataHandlers)) { @@ -191,23 +150,6 @@ MainLoop::~MainLoop() { delete dataHandler; } m_dataHandlers.clear(); - if (m_dumpFile) { - delete m_dumpFile; - m_dumpFile = nullptr; - } - if (m_logRawFile) { - delete m_logRawFile; - m_logRawFile = nullptr; - } - if (m_busHandler != nullptr) { - delete m_busHandler; - m_busHandler = nullptr; - m_protocol = nullptr; // ProtocolHandler is freed by BusHandler - } - if (m_device != nullptr) { - delete m_device; - m_device = nullptr; - } if (m_newlyDefinedMessages) { delete m_newlyDefinedMessages; m_newlyDefinedMessages = nullptr; @@ -480,62 +422,6 @@ void MainLoop::run() { } } -void MainLoop::notifyDeviceData(symbol_t symbol, bool received) { - if (received && m_dumpFile) { - m_dumpFile->write(&symbol, 1); - } - if (!m_logRawFile && !m_logRawEnabled) { - return; - } - if (m_logRawBytes) { - if (m_logRawFile) { - m_logRawFile->write(&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; - if (m_logRawBuffer.tellp() == 0 && m_logRawLastSymbol != SYN) { - m_logRawBuffer << "..."; - } - m_logRawBuffer << (received ? "<" : ">"); - } - m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast(symbol); - } - m_logRawLastSymbol = symbol; - if (m_logRawBuffer.tellp() > (symbol == SYN ? 0 : 64)) { // flush: direction+5 hdr+24 max data+crc+direction+ack+1 - if (symbol != SYN) { - m_logRawBuffer << "..."; - } - const string bufStr = m_logRawBuffer.str(); - const char* str = bufStr.c_str(); - if (m_logRawFile) { - m_logRawFile->write((const unsigned char*)str, strlen(str), received, false); - } else { - logNotice(lf_bus, str); - } - m_logRawBuffer.str(""); - } -} - -void MainLoop::notifyStatus(bool error, const char* message) { - if (error) { - logError(lf_bus, "device status: %s", message); - } else { - logNotice(lf_bus, "device status: %s", message); - } -} - result_t MainLoop::decodeRequest(Request* req, bool* connected, RequestMode* reqMode, string* user, bool* reload, ostringstream* ostream) { vector args; @@ -1886,15 +1772,7 @@ result_t MainLoop::executeRaw(const vector& args, ostringstream* ostream " Toggle logging of messages or each byte."; return RESULT_OK; } - bool enabled; - m_logRawBytes = bytes; - if (m_logRawFile) { - enabled = !m_logRawFile->isEnabled(); - m_logRawFile->setEnabled(enabled); - } else { - enabled = !m_logRawEnabled; - m_logRawEnabled = enabled; - } + bool enabled = m_protocol->toggleLogRaw(bytes); *ostream << (enabled ? "raw logging enabled" : "raw logging disabled"); return RESULT_OK; } @@ -1905,12 +1783,11 @@ result_t MainLoop::executeDump(const vector& args, ostringstream* ostrea " Toggle binary dump of received bytes."; return RESULT_OK; } - if (!m_dumpFile) { + if (!m_protocol->hasDumpFile()) { *ostream << "dump not configured"; return RESULT_OK; } - bool enabled = !m_dumpFile->isEnabled(); - m_dumpFile->setEnabled(enabled); + bool enabled = m_protocol->toggleDump(); *ostream << (enabled ? "dump enabled" : "dump disabled"); return RESULT_OK; } diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 4ef5ed08..43255752 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -30,7 +30,7 @@ #include "ebusd/scan.h" #include "lib/ebus/filereader.h" #include "lib/ebus/message.h" -#include "lib/utils/rotatefile.h" +#include "lib/ebus/protocol.h" #include "lib/utils/httpclient.h" namespace ebusd { @@ -99,18 +99,18 @@ class UserList : public UserInfo, public MappedFileReader { /** * The main loop handling requests from connected clients. */ -class MainLoop : public Thread, DeviceListener { +class MainLoop : public Thread { public: /** * Construct the main loop and create bus handling components. * @param opt the program options. - * @param device the @a Device instance. + * @param busHandler @a BusHandler instance. * @param messages the @a MessageMap instance. * @param scanHelper the @a ScanHelper instance. * @param requestQueue the reference to the @a Request @a Queue. */ - MainLoop(const struct options& opt, Device *device, MessageMap* messages, ScanHelper* scanHelper, - Queue* requestQueue); + MainLoop(const struct options& opt, BusHandler* busHandler, + MessageMap* messages, ScanHelper* scanHelper, Queue* requestQueue); /** * Destructor. @@ -122,18 +122,6 @@ class MainLoop : public Thread, DeviceListener { */ void shutdown(); - /** - * Get the @a BusHandler instance. - * @return the created @a BusHandler instance. - */ - BusHandler* getBusHandler() { return m_busHandler; } - - // @copydoc - void notifyDeviceData(symbol_t symbol, bool received) override; - - // @copydoc - void notifyStatus(bool error, const char* message) override; - protected: // @copydoc @@ -370,33 +358,15 @@ class MainLoop : public Thread, DeviceListener { */ result_t formatHttpResult(result_t ret, int type, ostringstream* ostream); - /** the @a Device instance. */ - Device* m_device; + /** the @a BusHandler instance. */ + BusHandler* m_busHandler; + + /** the @a ProtocolHandler instance. */ + ProtocolHandler* m_protocol; /** the number of reconnects requested from the @a Device. */ unsigned int m_reconnectCount; - /** the @a RotateFile for writing sent/received bytes in log format, or nullptr. */ - RotateFile* m_logRawFile; - - /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is nullptr). */ - 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 nullptr. */ - RotateFile* m_dumpFile; - /** the @a UserList instance. */ UserList m_userList; @@ -440,12 +410,6 @@ class MainLoop : public Thread, DeviceListener { /** the @a HttpClient for performing the update check. */ HttpClient m_httpClient; - /** the created @a BusHandler instance. */ - BusHandler* m_busHandler; - - /** the created @a ProtocolHandler instance. */ - ProtocolHandler* m_protocol; - /** the reference to the @a Request @a Queue. */ Queue* m_requestQueue; diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp index 459c99d1..c490b55d 100755 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -775,7 +775,7 @@ void MqttHandler::run() { publishDefinition(m_replacers, "def_global_uptime-", uptimeTopic, "global", "uptime", "def_global-"); publishDefinition(m_replacers, "def_global_updatecheck-", m_globalTopic.get("", "updatecheck"), "global", "updatecheck", "def_global-"); - if (m_busHandler->getDevice()->supportsUpdateCheck()) { + if (m_busHandler->getProtocol()->supportsUpdateCheck()) { publishDefinition(m_replacers, "def_global_updatecheck_device-", m_globalTopic.get("", "updatecheck"), "global", "updatecheck_device", ""); } diff --git a/src/lib/ebus/protocol.cpp b/src/lib/ebus/protocol.cpp index 9707916a..e87e594e 100644 --- a/src/lib/ebus/protocol.cpp +++ b/src/lib/ebus/protocol.cpp @@ -20,13 +20,18 @@ # include #endif +#include +#include #include "lib/ebus/protocol.h" #include "lib/ebus/protocol_direct.h" -#include #include "lib/utils/log.h" namespace ebusd { +using std::hex; +using std::setfill; +using std::setw; + bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) { if (result == RESULT_OK) { string str = slave.getStr(); @@ -37,12 +42,25 @@ bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) { return false; } - ProtocolHandler* ProtocolHandler::create(const ebus_protocol_config_t config, - Device* device, ProtocolListener* listener) { + ProtocolListener* listener) { + Device *device = Device::create(config.device, config.extraLatency, !config.noDeviceCheck); + if (device == nullptr) { + return nullptr; + } return new DirectProtocolHandler(config, device, listener); } +result_t ProtocolHandler::open() { + result_t result = m_device->open(); + if (result != RESULT_OK) { + logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); + } else if (!m_device->isValid()) { + logError(lf_bus, "device %s not available", m_device->getName()); + } + return result; +} + void ProtocolHandler::formatInfo(ostringstream* ostream, bool verbose, bool noWait) { m_device->formatInfo(ostream, verbose, true); if (isReadOnly()) { @@ -58,6 +76,63 @@ void ProtocolHandler::formatInfoJson(ostringstream* ostream) { m_device->formatInfoJson(ostream); } +void ProtocolHandler::notifyDeviceData(symbol_t symbol, bool received) { + if (received && m_dumpFile) { + m_dumpFile->write(&symbol, 1); + } + if (!m_logRawFile && !m_logRawEnabled) { + return; + } + if (m_logRawBytes) { + if (m_logRawFile) { + m_logRawFile->write(&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; + if (m_logRawBuffer.tellp() == 0 && m_logRawLastSymbol != SYN) { + m_logRawBuffer << "..."; + } + m_logRawBuffer << (received ? "<" : ">"); + } + m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast(symbol); + } + m_logRawLastSymbol = symbol; + if (m_logRawBuffer.tellp() > (symbol == SYN ? 0 : 64)) { // flush: direction+5 hdr+24 max data+crc+direction+ack+1 + if (symbol != SYN) { + m_logRawBuffer << "..."; + } + const string bufStr = m_logRawBuffer.str(); + const char* str = bufStr.c_str(); + if (m_logRawFile) { + m_logRawFile->write((const unsigned char*)str, strlen(str), received, false); + } else { + logNotice(lf_bus, str); + } + m_logRawBuffer.str(""); + } +} + +void ProtocolHandler::notifyStatus(bool error, const char* message) { + if (error) { + logError(lf_bus, "device status: %s", message); + } else { + logNotice(lf_bus, "device status: %s", message); + } +} + + void ProtocolHandler::clear() { memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); m_masterCount = 1; @@ -161,4 +236,45 @@ bool ProtocolHandler::addSeenAddress(symbol_t address) { return ret; } +void ProtocolHandler::setDumpFile(const char* dumpFile, unsigned int dumpSize, bool dumpFlush) { + if (m_dumpFile) { + delete m_dumpFile; + m_dumpFile = nullptr; + } + if (dumpFile && dumpFile[0]) { + m_dumpFile = new RotateFile(dumpFile, dumpSize, false, dumpFlush ? 1 : 16); + } +} + +bool ProtocolHandler::toggleDump() { + if (!m_dumpFile) { + return false; + } + bool enabled = !m_dumpFile->isEnabled(); + m_dumpFile->setEnabled(enabled); + return enabled; +} + +void ProtocolHandler::setLogRawFile(const char* logRawFile, unsigned int logRawSize) { + if (logRawFile[0]) { + m_logRawFile = new RotateFile(logRawFile, logRawSize, true); + m_logRawFile->setEnabled(m_logRawEnabled); + } else { + m_logRawFile = nullptr; + } +} + +bool ProtocolHandler::toggleLogRaw(bool bytes) { + bool enabled; + m_logRawBytes = bytes; + if (m_logRawFile) { + enabled = !m_logRawFile->isEnabled(); + m_logRawFile->setEnabled(enabled); + } else { + enabled = !m_logRawEnabled; + m_logRawEnabled = enabled; + } + return enabled; +} + } // namespace ebusd diff --git a/src/lib/ebus/protocol.h b/src/lib/ebus/protocol.h index 2424ad07..e5c244df 100755 --- a/src/lib/ebus/protocol.h +++ b/src/lib/ebus/protocol.h @@ -23,6 +23,7 @@ #include "lib/ebus/result.h" #include "lib/ebus/device.h" #include "lib/utils/queue.h" +#include "lib/utils/rotatefile.h" #include "lib/utils/thread.h" namespace ebusd { @@ -52,8 +53,14 @@ namespace ebusd { /** settings for the eBUS protocol handler. */ typedef struct ebus_protocol_config { + /** eBUS device string (serial device or [udp:]ip:port) with optional protocol prefix (enh: or ens:). */ + const char* device; + /** whether to skip serial eBUS device test. */ + bool noDeviceCheck; /** whether to allow read access to the device only. */ bool readOnly; + /** extra transfer latency in ms. */ + unsigned int extraLatency; /** the own master address. */ symbol_t ownAddress; /** whether to answer queries for the own master/slave address. */ @@ -232,7 +239,7 @@ class ProtocolListener { /** * Handles input from and output to eBUS with respect to the eBUS protocol. */ -class ProtocolHandler : public WaitThread { +class ProtocolHandler : public WaitThread, DeviceListener { public: /** * Construct a new instance. @@ -249,8 +256,13 @@ class ProtocolHandler : public WaitThread { m_masterCount(config.readOnly ? 0 : 1), m_symbolLatencyMin(-1), m_symbolLatencyMax(-1), m_arbitrationDelayMin(-1), m_arbitrationDelayMax(-1), m_lastReceive(0), - m_symPerSec(0), m_maxSymPerSec(0) { + m_symPerSec(0), m_maxSymPerSec(0), + m_logRawFile(nullptr), m_logRawEnabled(false), m_logRawBytes(false), + m_logRawLastSymbol(SYN), m_dumpFile(nullptr) { memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); + m_device->setListener(this); + m_logRawLastReceived = true; + m_logRawLastSymbol = SYN; } /** @@ -267,16 +279,33 @@ class ProtocolHandler : public WaitThread { delete req; } } + if (m_dumpFile) { + delete m_dumpFile; + m_dumpFile = nullptr; + } + if (m_logRawFile) { + delete m_logRawFile; + m_logRawFile = nullptr; + } + if (m_device != nullptr) { + delete m_device; + m_device = nullptr; + } } /** * Create a new instance. * @param config the configuration to use. - * @param device the @a Device instance for accessing the bus. * @param listener the @a ProtocolListener. * @return the new ProtocolHandler, or @a nullptr on error. */ - static ProtocolHandler* create(const ebus_protocol_config_t config, Device* device, ProtocolListener* listener); + static ProtocolHandler* create(const ebus_protocol_config_t config, ProtocolListener* listener); + + /** + * Open the device. + * @return the @a result_t code. + */ + virtual result_t open(); /** * Format device/protocol infos in plain text. @@ -334,9 +363,15 @@ class ProtocolHandler : public WaitThread { unsigned int getMaxSymPerSec() const { return m_maxSymPerSec; } /** - * @return the @a Device instance for accessing the bus. + * @return whether the device supports checking for version updates. */ - const Device* getDevice() const { return m_device; } + virtual bool supportsUpdateCheck() const { return m_device->supportsUpdateCheck(); } + + // @copydoc + void notifyDeviceData(symbol_t symbol, bool received) override; + + // @copydoc + void notifyStatus(bool error, const char* message) override; /** * Clear stored values (e.g. scan results). @@ -425,6 +460,37 @@ class ProtocolHandler : public WaitThread { */ unsigned int getMasterCount() const { return m_masterCount; } + /** + * Set the dump file to use. + * @param dumpFile the dump file to use, or nullptr. + * @param dumpSize the maximum file size. + * @param dumpFlush true to early flush the file. + */ + void setDumpFile(const char* dumpFile, unsigned int dumpSize, bool dumpFlush); + + /** + * @return whether a dump file is set. + */ + bool hasDumpFile() const { return m_dumpFile; } + + /** + * Toggle dumping to file. + * @return true if dumping is now enabled. + */ + bool toggleDump(); + + /** + * Set the log raw data file to use. + * @param logRawFile the log raw file to use, or nullptr. + * @param logRawSize the maximum file size. + */ + void setLogRawFile(const char* logRawFile, unsigned int logRawSize); + + /** + * Toggle logging raw data. + * @return true if logging raw data is now enabled. + */ + bool toggleLogRaw(bool bytes); protected: /** @@ -500,6 +566,27 @@ class ProtocolHandler : public WaitThread { /** the participating bus addresses seen so far. */ bool m_seenAddresses[256]; + + /** the @a RotateFile for writing sent/received bytes in log format, or nullptr. */ + RotateFile* m_logRawFile; + + /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is nullptr). */ + 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 nullptr. */ + RotateFile* m_dumpFile; }; } // namespace ebusd