diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 913b2047..a73cd78f 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -1019,13 +1019,14 @@ int main(int argc, char* argv[], char* envp[]) { signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); - logNotice(lf_main, PACKAGE_STRING "." REVISION " started%s%s on%s device %s", - device->isReadOnly() ? " read only" : "", + ostringstream ostream; + device->formatInfo(&ostream, false, 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" : s_opt.initialScan == BROADCAST ? " with broadcast scan" : s_opt.initialScan == SYN ? " with full scan" : " with single scan" : "", - device->isEnhancedProto() ? " enhanced" : "", - device->getName()); + deviceInfoStr.c_str()); // load configuration files s_scanHelper->loadConfigFiles(!s_opt.scanConfig); diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index ee5be00f..5d242fb9 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -372,12 +372,7 @@ void MainLoop::run() { << ",\"a\":\"other\"" #endif << ",\"u\":" << (now-start); - if (m_device->isEnhancedProto()) { - string ver = m_device->getEnhancedVersion(); - if (!ver.empty()) { - ostr << ",\"dv\":\"" << ver << "\""; - } - } + m_device->formatInfo(&ostr, false, true, true); if (m_reconnectCount) { ostr << ",\"rc\":" << m_reconnectCount; } @@ -1944,31 +1939,8 @@ result_t MainLoop::executeInfo(const vector& args, const string& user, o if (!m_updateCheck.empty()) { *ostream << "update check: " << m_updateCheck << "\n"; } - *ostream << "device: " << m_device->getName(); - if (m_device->isEnhancedProto()) { - *ostream << ", enhanced"; - } - if (m_device->isReadOnly()) { - *ostream << ", readonly"; - } - if (!m_device->isValid()) { - *ostream << ", invalid"; - } - bool infoAdded = false; - if (verbose) { - string info = m_device->getEnhancedInfos(); - if (!info.empty()) { - *ostream << ", " << info; - infoAdded = true; - } - } - if (!infoAdded) { - string info = m_device->getEnhancedVersion(); - if (!info.empty()) { - *ostream << ", firmware " << info; - } - } - + *ostream << "device: "; + m_device->formatInfo(ostream, verbose); *ostream << "\n"; if (!user.empty()) { *ostream << "user: " << user << "\n"; diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp index a4575062..b88c510b 100755 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -952,7 +952,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()->supportsEnhancedInfos()) { + if (m_busHandler->getDevice()->supportsUpdateCheck()) { publishDefinition(m_replacers, "def_global_updatecheck_device-", m_globalTopic.get("", "updatecheck"), "global", "updatecheck_device", ""); } diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 6a946390..f96a4ac9 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -75,25 +75,8 @@ namespace ebusd { #define ENH_BYTE2 ((uint8_t)0x80) #define makeEnhancedSequence(cmd, data) {(uint8_t)(ENH_BYTE1 | ((cmd)<<2) | (((data)&0xc0)>>6)), (uint8_t)(ENH_BYTE2 | ((data)&0x3f))} -Device::Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, - bool enhancedProto) - : m_name(name), m_checkDevice(checkDevice), - m_latency(HOST_LATENCY_MS+(enhancedProto?ENHANCED_LATENCY_MS:0)+latency), m_readOnly(readOnly), - m_initialSend(initialSend), m_enhancedProto(enhancedProto), m_fd(-1), m_resetRequested(false), - m_listener(nullptr), m_arbitrationMaster(SYN), - m_arbitrationCheck(0), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(0), - m_extraFatures(0), m_infoId(0xff), m_infoReqTime(0), m_infoLen(0), m_infoPos(0) { - m_buffer = reinterpret_cast(malloc(m_bufSize)); - if (!m_buffer) { - m_bufSize = 0; - } -} - -Device::~Device() { - close(); - if (m_buffer) { - free(m_buffer); - } +Device::Device(const char* name, bool readOnly, bool initialSend) + : m_name(name), m_readOnly(readOnly), m_initialSend(initialSend), m_listener(nullptr) { } Device* Device::create(const char* name, unsigned int extraLatency, bool checkDevice, bool readOnly, bool initialSend) { @@ -138,14 +121,82 @@ Device* Device::create(const char* name, unsigned int extraLatency, bool checkDe return new SerialDevice(name, checkDevice, extraLatency, readOnly, initialSend, enhanced, highSpeed); } -result_t Device::open() { +result_t Device::afterOpen() { + if (m_initialSend && !send(ESC)) { + return RESULT_ERR_SEND; + } + return RESULT_OK; +} + + +FileDevice::FileDevice(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, + bool enhancedProto) + : Device(name, readOnly, initialSend), + m_checkDevice(checkDevice), + m_latency(HOST_LATENCY_MS+(enhancedProto?ENHANCED_LATENCY_MS:0)+latency), + m_enhancedProto(enhancedProto), m_fd(-1), m_resetRequested(false), + m_arbitrationMaster(SYN), + m_arbitrationCheck(0), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(0), + m_extraFatures(0), m_infoId(0xff), m_infoReqTime(0), m_infoLen(0), m_infoPos(0) { + m_buffer = reinterpret_cast(malloc(m_bufSize)); + if (!m_buffer) { + m_bufSize = 0; + } +} + +FileDevice::~FileDevice() { + close(); + if (m_buffer) { + free(m_buffer); + } +} + +void FileDevice::formatInfo(ostringstream* ostream, bool verbose, bool asJson, bool noWait) { + if (asJson) { + if (m_enhancedProto) { + string ver = getEnhancedVersion(); + if (!ver.empty()) { + *ostream << ",\"dv\":\"" << ver << "\""; + } + } + return; + } + *ostream << m_name; + string info = getEnhancedProtoInfo(); + if (!info.empty()) { + *ostream << ", " << info; + } + if (isReadOnly()) { + *ostream << ", readonly"; + } + if (noWait) { + return; + } + if (!isValid()) { + *ostream << ", invalid"; + } + bool infoAdded = false; + if (verbose) { + info = getEnhancedInfos(); + if (!info.empty()) { + *ostream << ", " << info; + infoAdded = true; + } + } + if (!infoAdded) { + string ver = getEnhancedVersion(); + if (!ver.empty()) { + *ostream << ", firmware " << ver; + } + } +} + +result_t FileDevice::open() { close(); return m_bufSize == 0 ? RESULT_ERR_DEVICE : RESULT_OK; } -result_t Device::afterOpen() { - m_bufLen = 0; - m_extraFatures = 0; +result_t FileDevice::afterOpen() { if (m_enhancedProto) { symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INIT, 0x01); // extra feature: info #ifdef DEBUG_RAW_TRAFFIC @@ -165,15 +216,16 @@ result_t Device::afterOpen() { return RESULT_OK; } -void Device::close() { +void FileDevice::close() { if (m_fd != -1) { ::close(m_fd); m_fd = -1; } m_bufLen = 0; // flush read buffer + m_extraFatures = 0; // reset state } -bool Device::isValid() { +bool FileDevice::isValid() { if (m_fd == -1) { return false; } @@ -183,7 +235,7 @@ bool Device::isValid() { return m_fd != -1; } -result_t Device::requestEnhancedInfo(symbol_t infoId) { +result_t FileDevice::requestEnhancedInfo(symbol_t infoId) { if (!m_enhancedProto || m_extraFatures == 0) { return RESULT_ERR_INVALID_ARG; } @@ -212,7 +264,7 @@ result_t Device::requestEnhancedInfo(symbol_t infoId) { return sendEnhancedInfoRequest(infoId); } -result_t Device::sendEnhancedInfoRequest(symbol_t infoId) { +result_t FileDevice::sendEnhancedInfoRequest(symbol_t infoId) { symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INFO, infoId); #ifdef DEBUG_RAW_TRAFFIC fprintf(stdout, "raw enhanced > %2.2x %2.2x\n", buf[0], buf[1]); @@ -227,7 +279,7 @@ result_t Device::sendEnhancedInfoRequest(symbol_t infoId) { return RESULT_OK; } -string Device::getEnhancedInfos() { +string FileDevice::getEnhancedInfos() { if (!m_enhancedProto || m_extraFatures == 0) { return ""; } @@ -276,7 +328,7 @@ string Device::getEnhancedInfos() { + m_enhInfoBusVoltage; } -result_t Device::send(symbol_t value) { +result_t FileDevice::send(symbol_t value) { if (!isValid()) { return RESULT_ERR_DEVICE; } @@ -296,7 +348,7 @@ result_t Device::send(symbol_t value) { #define ENHANCED_COMPLETE_WAIT_DURATION 10 -bool Device::cancelRunningArbitration(ArbitrationState* arbitrationState) { +bool FileDevice::cancelRunningArbitration(ArbitrationState* arbitrationState) { if (m_enhancedProto && m_arbitrationMaster != SYN) { *arbitrationState = as_error; m_arbitrationMaster = SYN; @@ -313,7 +365,7 @@ bool Device::cancelRunningArbitration(ArbitrationState* arbitrationState) { return true; } -result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { +result_t FileDevice::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { if (m_arbitrationMaster != SYN) { *arbitrationState = as_running; } @@ -424,7 +476,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a return RESULT_OK; } -result_t Device::startArbitration(symbol_t masterAddress) { +result_t FileDevice::startArbitration(symbol_t masterAddress) { if (m_arbitrationCheck) { if (masterAddress != SYN) { return RESULT_ERR_ARB_RUNNING; // should not occur @@ -453,7 +505,7 @@ result_t Device::startArbitration(symbol_t masterAddress) { return RESULT_OK; } -bool Device::write(symbol_t value, bool startArbitration) { +bool FileDevice::write(symbol_t value, bool startArbitration) { if (m_enhancedProto) { symbol_t buf[2] = makeEnhancedSequence(startArbitration ? ENH_REQ_START : ENH_REQ_SEND, value); #ifdef DEBUG_RAW_TRAFFIC @@ -473,7 +525,7 @@ bool Device::write(symbol_t value, bool startArbitration) { #endif } -bool Device::available() { +bool FileDevice::available() { if (m_bufLen <= 0) { return false; } @@ -542,7 +594,7 @@ bool Device::available() { return false; } -bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState, bool* incomplete) { +bool FileDevice::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState, bool* incomplete) { if (!isAvailable) { if (m_bufLen > 0 && m_bufPos != 0) { if (m_bufLen > m_bufSize / 2) { @@ -609,7 +661,7 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati return handleEnhancedBufferedData(value, arbitrationState); } -bool Device::handleEnhancedBufferedData(symbol_t* value, ArbitrationState* arbitrationState) { +bool FileDevice::handleEnhancedBufferedData(symbol_t* value, ArbitrationState* arbitrationState) { while (m_bufLen > 0) { symbol_t ch = m_buffer[m_bufPos]; if (!(ch&ENH_BYTE_FLAG)) { @@ -840,7 +892,7 @@ bool Device::handleEnhancedBufferedData(symbol_t* value, ArbitrationState* arbit result_t SerialDevice::open() { - result_t result = Device::open(); + result_t result = FileDevice::open(); if (result != RESULT_OK) { return result; } @@ -857,7 +909,7 @@ result_t SerialDevice::open() { return RESULT_ERR_NOTFOUND; } - if (flock(m_fd, LOCK_EX|LOCK_NB)) { + if (flock(m_fd, LOCK_EX|LOCK_NB) != 0) { close(); return RESULT_ERR_DEVICE; } @@ -925,7 +977,7 @@ void SerialDevice::close() { // restore previous settings of the device tcsetattr(m_fd, TCSANOW, &m_oldSettings); } - Device::close(); + FileDevice::close(); } void SerialDevice::checkDevice() { @@ -936,7 +988,7 @@ void SerialDevice::checkDevice() { } result_t NetworkDevice::open() { - result_t result = Device::open(); + result_t result = FileDevice::open(); if (result != RESULT_OK) { return result; } diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index a5c31615..8605286e 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -99,20 +99,16 @@ class Device { /** * Construct a new instance. * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - * @param checkDevice whether to regularly check the device availability. - * @param latency the bus transfer latency in milliseconds. * @param readOnly whether to allow read access to the device only. * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @param enhancedProto whether to use the ebusd enhanced protocol. */ - Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, - bool enhancedProto = false); + Device(const char* name, bool readOnly, bool initialSend); public: /** * Destructor. */ - virtual ~Device(); + virtual ~Device() { } /** * Factory method for creating a new instance. @@ -128,34 +124,61 @@ class Device { bool readOnly = false, bool initialSend = false); /** - * Get the transfer latency of this device. - * @return the transfer latency in milliseconds. + * Get the device name. + * @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ - virtual unsigned int getLatency() const { return m_latency; } + const char* getName() const { return m_name; } + + /** + * Return whether to allow read access to the device only. + * @return whether to allow read access to the device only. + */ + bool isReadOnly() const { return m_readOnly; } + + /** + * Set the @a DeviceListener. + * @param listener the @a DeviceListener. + */ + void setListener(DeviceListener* listener) { m_listener = listener; } + + /** + * Format device infos in plain text or JSON format. + * @param output the @a ostringstream to append the infos to. + * @param verbose whether to add verbose infos. + * @param asJson whether to format as JSON rather than plain text. + * @param noWait true to not wait for any response asynchronously and return immediately. + */ + virtual void formatInfo(ostringstream* output, bool verbose, bool asJson = false, bool noWait = false) = 0; /** * Open the file descriptor. * @return the @a result_t code. */ - virtual result_t open(); + virtual result_t open() = 0; /** * Has to be called by subclasses upon successful opening the device as last action in open(). * @return the @a result_t code. */ - result_t afterOpen(); + virtual result_t afterOpen(); /** * Close the file descriptor if opened. */ - virtual void close(); + virtual void close() = 0; + + /** + * Return whether the device is opened and available. + * @return whether the device is opened and available. + */ + virtual bool isValid() = 0; /** * Write a single byte to the device. * @param value the byte value to write. * @return the @a result_t code. */ - result_t send(symbol_t value); + virtual result_t send(symbol_t value) = 0; /** * Read a single byte from the device. @@ -165,7 +188,7 @@ class Device { * @a as_won, the received byte is the master address that was successfully arbitrated with. * @return the result_t code. */ - result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState); + virtual result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) = 0; /** * Start the arbitration with the specified master address. A subsequent request while an arbitration is currently in @@ -173,31 +196,89 @@ class Device { * @param masterAddress the master address, or @a SYN to cancel a previous arbitration request. * @return the result_t code. */ - result_t startArbitration(symbol_t masterAddress); + virtual result_t startArbitration(symbol_t masterAddress) = 0; /** * Return whether the device is currently in arbitration. * @return true when the device is currently in arbitration. */ - bool isArbitrating() const { return m_arbitrationMaster != SYN; } + virtual bool isArbitrating() const = 0; /** - * Return the device name. - * @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @return whether the device supports checking for version updates. */ - const char* getName() { return m_name; } + virtual bool supportsUpdateCheck() const = 0; + + protected: + /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ + const char* m_name; + + /** whether to allow read access to the device only. */ + const bool m_readOnly; + + /** whether to send an initial @a ESC symbol in @a open(). */ + const bool m_initialSend; + + /** the @a DeviceListener, or nullptr. */ + DeviceListener* m_listener; +}; + + +/** + * The common base class for devices using a file descriptor. + */ +class FileDevice : public Device { + protected: + /** + * Construct a new instance. + * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param checkDevice whether to regularly check the device availability. + * @param latency the bus transfer latency in milliseconds. + * @param readOnly whether to allow read access to the device only. + * @param initialSend whether to send an initial @a ESC symbol in @a open(). + * @param enhancedProto whether to use the ebusd enhanced protocol. + */ + FileDevice(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, + bool enhancedProto = false); + + public: + /** + * Destructor. + */ + virtual ~FileDevice(); + + // @copydoc + void formatInfo(ostringstream* output, bool verbose, bool asJson = false, bool noWait = false) override; + + // @copydoc + result_t open() override; + + // @copydoc + result_t afterOpen() override; + + // @copydoc + void close() override; + + // @copydoc + bool isValid() override; + + // @copydoc + result_t send(symbol_t value) override; + + // @copydoc + result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) override; + + // @copydoc + result_t startArbitration(symbol_t masterAddress) override; + + // @copydoc + bool isArbitrating() const override { return m_arbitrationMaster != SYN; } /** - * Return whether the device is opened and available. - * @return whether the device is opened and available. + * Get the transfer latency of this device. + * @return the transfer latency in milliseconds. */ - bool isValid(); - - /** - * Return whether to allow read access to the device only. - * @return whether to allow read access to the device only. - */ - bool isReadOnly() const { return m_readOnly; } + virtual unsigned int getLatency() const { return m_latency; } /** * Return whether the device supports the ebusd enhanced protocol. @@ -205,17 +286,20 @@ class Device { */ bool isEnhancedProto() const { return m_enhancedProto; } + /** + * Get info about enhanced protocol support as string. + * @return a @a string describing level of enhanced protocol support, or the empty string. + */ + virtual string getEnhancedProtoInfo() const { return m_enhancedProto ? "enhanced" : ""; } + + // @copydoc + bool supportsUpdateCheck() const override { return m_enhancedProto && m_extraFatures & 0x01; } + /** * @return whether the device supports the ebusd enhanced protocol and supports querying extra infos. */ bool supportsEnhancedInfos() const { return m_enhancedProto && m_extraFatures & 0x01; } - /** - * Set the @a DeviceListener. - * @param listener the @a DeviceListener. - */ - void setListener(DeviceListener* listener) { m_listener = listener; } - /** * Check for a running extra infos request, wait for it to complete, * and then send a new request for extra infos to enhanced device. @@ -281,21 +365,12 @@ class Device { virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState = nullptr, bool* incomplete = nullptr); - /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ - const char* m_name; - /** whether to regularly check the device availability. */ const bool m_checkDevice; /** the bus transfer latency in milliseconds. */ const unsigned int m_latency; - /** whether to allow read access to the device only. */ - const bool m_readOnly; - - /** whether to send an initial @a ESC symbol in @a open(). */ - const bool m_initialSend; - /** whether the device supports the ebusd enhanced protocol. */ const bool m_enhancedProto; @@ -314,9 +389,6 @@ class Device { */ bool handleEnhancedBufferedData(symbol_t* value, ArbitrationState* arbitrationState); - /** the @a DeviceListener, or nullptr. */ - DeviceListener* m_listener; - /** the arbitration master address to send when in arbitration, or @a SYN. */ symbol_t m_arbitrationMaster; @@ -371,7 +443,7 @@ class Device { /** * The @a Device for directly connected serial interfaces (tty). */ -class SerialDevice : public Device { +class SerialDevice : public FileDevice { public: /** * Construct a new instance. @@ -385,10 +457,15 @@ class SerialDevice : public Device { */ SerialDevice(const char* name, bool checkDevice, unsigned int extraLatency, bool readOnly, bool initialSend, bool enhancedProto = false, bool enhancedHighSpeed = false) - : Device(name, checkDevice, extraLatency, readOnly, initialSend, enhancedProto), + : FileDevice(name, checkDevice, extraLatency, readOnly, initialSend, enhancedProto), m_enhancedHighSpeed(enhancedHighSpeed) { } + // @copydoc + string getEnhancedProtoInfo() const override { + return m_enhancedProto ? (m_enhancedHighSpeed ? "enhanced high speed" : "enhanced") : ""; + } + // @copydoc result_t open() override; @@ -412,7 +489,7 @@ class SerialDevice : public Device { /** * The @a Device for remote network interfaces. */ -class NetworkDevice : public Device { +class NetworkDevice : public FileDevice { public: /** * Construct a new instance. @@ -428,7 +505,7 @@ class NetworkDevice : public Device { */ NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, unsigned int extraLatency, bool readOnly, bool initialSend, bool udp, bool enhancedProto = false) - : Device(name, true, NETWORK_LATENCY_MS+extraLatency, readOnly, initialSend, enhancedProto), + : FileDevice(name, true, NETWORK_LATENCY_MS+extraLatency, readOnly, initialSend, enhancedProto), m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {} /**