From 18ba48e86feabe4facd594fd5e9a70619b176dc8 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 13 May 2018 18:18:04 +0200 Subject: [PATCH 01/48] use type for predefined bus symbols --- src/lib/ebus/symbol.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/ebus/symbol.h b/src/lib/ebus/symbol.h index e128e0f5..e5ad4b04 100755 --- a/src/lib/ebus/symbol.h +++ b/src/lib/ebus/symbol.h @@ -72,19 +72,19 @@ using std::vector; typedef unsigned char symbol_t; /** escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA. */ -#define ESC 0xA9 +#define ESC ((symbol_t)0xA9) /** synchronization symbol. */ -#define SYN 0xAA +#define SYN ((symbol_t)0xAA) /** positive acknowledge symbol. */ -#define ACK 0x00 +#define ACK ((symbol_t)0x00) /** negative acknowledge symbol. */ -#define NAK 0xFF +#define NAK ((symbol_t)0xFF) /** the broadcast destination address. */ -#define BROADCAST 0xFE +#define BROADCAST ((symbol_t)0xFE) /** * Parse an unsigned int value. From f3d12a321c5e7df577b0c06cfad19bf767f6e6f9 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 13 May 2018 18:18:31 +0200 Subject: [PATCH 02/48] moved arbitration logic to device --- src/ebusd/bushandler.cpp | 133 ++++++++++++++++++++++----------------- src/lib/ebus/device.cpp | 29 ++++++++- src/lib/ebus/device.h | 42 ++++++++++++- 3 files changed, 145 insertions(+), 59 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 45c664f2..565c2ead 100755 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -276,7 +276,7 @@ bool GrabbedMessage::dump(bool unknown, MessageMap* messages, bool first, bool d if (remain == 0) { return true; } - for (const auto it : *types) { + for (const auto& it : *types) { const DataType* baseType = it.second; if ((baseType->getBitCount() % 8) != 0 || baseType->isIgnored()) { // skip bit and ignored types continue; @@ -422,7 +422,6 @@ result_t BusHandler::handleSymbol() { unsigned int timeout = SYN_TIMEOUT; symbol_t sendSymbol = ESC; bool sending = false; - BusRequest* startRequest = nullptr; // check if another symbol has to be sent and determine timeout for receive switch (m_state) { @@ -432,13 +431,8 @@ result_t BusHandler::handleSymbol() { case bs_skip: timeout = SYN_TIMEOUT; - break; - - case bs_ready: - if (m_currentRequest != nullptr) { - setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up - } else if (m_remainLockCount == 0) { - startRequest = m_nextRequests.peek(); + if (!m_device->isArbitrating() && m_currentRequest == nullptr && m_remainLockCount == 0) { + BusRequest* startRequest = m_nextRequests.peek(); if (startRequest == nullptr && m_pollInterval > 0) { // check for poll/scan time_t now; time(&now); @@ -446,7 +440,7 @@ result_t BusHandler::handleSymbol() { Message* message = m_messages->getNextPoll(); if (message != nullptr) { m_lastPoll = now; - PollRequest* request = new PollRequest(message); + auto request = new PollRequest(message); result_t ret = request->prepare(m_ownMasterAddress); if (ret != RESULT_OK) { logError(lf_bus, "prepare poll message: %s", getResultCode(ret)); @@ -459,12 +453,23 @@ result_t BusHandler::handleSymbol() { } } if (startRequest != nullptr) { // initiate arbitration - sendSymbol = startRequest->m_master[0]; - sending = true; + result_t ret = m_device->startArbitration(startRequest->m_master[0]); + if (ret != RESULT_OK) { + logError(lf_bus, "arbitration start: %s", getResultCode(ret)); + m_currentRequest = startRequest; + setState(bs_ready, ret); // force the failed request to be notified + startRequest = nullptr; + } } } break; + case bs_ready: + if (m_currentRequest != nullptr) { + setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up + } + break; + case bs_recvCmd: case bs_recvCmdCrc: timeout = m_slaveRecvTimeout; @@ -537,11 +542,11 @@ result_t BusHandler::handleSymbol() { // send symbol if necessary result_t result; - struct timespec sentTime, recvTime; + struct timespec sentTime = {}, recvTime = {}; if (sending) { if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) { if (m_escape) { - sendSymbol = sendSymbol == ESC ? 0x00 : 0x01; + sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01); } else { m_escape = sendSymbol; sendSymbol = ESC; @@ -558,55 +563,76 @@ result_t BusHandler::handleSymbol() { } else { sending = false; timeout = SYN_TIMEOUT; - if (startRequest != nullptr && m_nextRequests.remove(startRequest)) { - m_currentRequest = startRequest; // force the failed request to be notified - } setState(bs_skip, result); } } // receive next symbol (optionally check reception of sent symbol) symbol_t recvSymbol; - result = m_device->recv(timeout+m_transferLatency, &recvSymbol); + ArbitrationState arbitrationState = as_none; + result = m_device->recv(timeout+m_transferLatency, &recvSymbol, &arbitrationState); if (sending) { clockGettime(&recvTime); } + bool sentAutoSyn = false; if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 - && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { + && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { // check if acting as AUTO-SYN generator is required result = m_device->send(SYN); - if (result == RESULT_OK) { - clockGettime(&sentTime); - recvSymbol = ESC; - result = m_device->recv(SEND_TIMEOUT+m_transferLatency, &recvSymbol); - clockGettime(&recvTime); - if (result == RESULT_ERR_TIMEOUT) { - return setState(bs_noSignal, result); - } - if (result != RESULT_OK) { - logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result)); - } else if (recvSymbol != SYN) { - logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol); - } else { - measureLatency(&sentTime, &recvTime); - if (m_generateSynInterval != SYN_TIMEOUT) { - // received own AUTO-SYN symbol back again: act as AUTO-SYN generator now - m_generateSynInterval = SYN_TIMEOUT; - logNotice(lf_bus, "acting as AUTO-SYN generator"); - } - m_remainLockCount = 0; - m_lastSynReceiveTime = recvTime; - return setState(bs_ready, result); + if (result != RESULT_OK) { + return setState(bs_skip, result); + } + clockGettime(&sentTime); + recvSymbol = ESC; + result = m_device->recv(SEND_TIMEOUT+m_transferLatency, &recvSymbol, &arbitrationState); + clockGettime(&recvTime); + if (result != RESULT_OK) { + logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result)); + return setState(bs_noSignal, result); + } + if (recvSymbol != SYN) { + logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol); + return setState(bs_noSignal, result); + } + measureLatency(&sentTime, &recvTime); + if (m_generateSynInterval != SYN_TIMEOUT) { + // received own AUTO-SYN symbol back again: act as AUTO-SYN generator now + m_generateSynInterval = SYN_TIMEOUT; + logNotice(lf_bus, "acting as AUTO-SYN generator"); + } + m_remainLockCount = 0; + m_lastSynReceiveTime = recvTime; + sentAutoSyn = true; + } + if (arbitrationState == as_lost) { + if (m_currentRequest == nullptr) { + BusRequest *startRequest = m_nextRequests.peek(); + if (startRequest != nullptr && m_nextRequests.remove(startRequest)) { + m_currentRequest = startRequest; // force the failed request to be notified } } - return setState(bs_skip, result); + setState(m_state, RESULT_ERR_BUS_LOST); + } else if (arbitrationState == as_won) { // implies RESULT_OK + if (m_currentRequest == nullptr) { + m_currentRequest = m_nextRequests.peek(); + } + if (m_currentRequest == nullptr) { + logDebug(lf_bus, "arbitration won without request"); + } else if (m_state == bs_ready) { + if (!m_nextRequests.remove(m_currentRequest)) { + // request already removed (e.g. due to timeout) + return setState(bs_skip, RESULT_ERR_TIMEOUT); + } + sendSymbol = m_currentRequest->m_master[0]; + sending = true; + } + } + if (sentAutoSyn) { + return setState(bs_ready, RESULT_OK); } time_t now; time(&now); if (result != RESULT_OK) { - if (sending && startRequest != nullptr && m_nextRequests.remove(startRequest)) { - m_currentRequest = startRequest; // force the failed request to be notified - } if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1) // at least one full second has passed since last received symbol || m_state == bs_noSignal) { @@ -672,19 +698,14 @@ result_t BusHandler::handleSymbol() { return RESULT_OK; case bs_ready: - if (startRequest != nullptr && sending) { - if (!m_nextRequests.remove(startRequest)) { - // request already removed (e.g. due to timeout) - return setState(bs_skip, RESULT_ERR_TIMEOUT); - } - m_currentRequest = startRequest; + if (m_currentRequest != nullptr && sending) { // check arbitration if (recvSymbol == sendSymbol) { // arbitration successful // measure arbitration delay long long latencyLong = (sentTime.tv_sec*1000000000 + sentTime.tv_nsec - m_lastSynReceiveTime.tv_sec*1000000000 - m_lastSynReceiveTime.tv_nsec)/1000; if (latencyLong >= 0 && latencyLong <= 10000) { // skip clock skew or out of reasonable range - int latency = static_cast(latencyLong); + auto latency = static_cast(latencyLong); logDebug(lf_bus, "arbitration delay %d micros", latency); if (m_arbitrationDelayMin < 0 || (latency < m_arbitrationDelayMin || latency > m_arbitrationDelayMax)) { if (m_arbitrationDelayMin == -1 || latency < m_arbitrationDelayMin) { @@ -1012,7 +1033,7 @@ void BusHandler::measureLatency(struct timespec* sentTime, struct timespec* recv if (latencyLong < 0 || latencyLong > 1000) { return; // clock skew or out of reasonable range } - int latency = static_cast(latencyLong); + auto latency = static_cast(latencyLong); logDebug(lf_bus, "send/receive symbol latency %d ms", latency); if (m_symbolLatencyMin >= 0 && (latency >= m_symbolLatencyMin && latency <= m_symbolLatencyMax)) { return; @@ -1294,7 +1315,7 @@ bool BusHandler::formatScanResult(symbol_t slave, bool leadingNewline, ostringst *output << endl; } *output << hex << setw(2) << setfill('0') << static_cast(slave); - for (const auto result : it->second) { + for (const auto &result : it->second) { *output << result; } return true; @@ -1423,7 +1444,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const { const auto it = m_scanResults.find(address); if (it != m_scanResults.end()) { *output << ",\"s\":\""; - for (const auto result : it->second) { + for (const auto& result : it->second) { *output << result; } *output << "\""; @@ -1439,7 +1460,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const { if (!loadedFiles.empty()) { *output << ",\"f\":["; bool first = true; - for (const auto loadedFile : loadedFiles) { + for (const auto& loadedFile : loadedFiles) { if (first) { first = false; } else { diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index b9f541d9..825b1960 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -120,7 +120,7 @@ result_t Device::send(symbol_t value) { return RESULT_OK; } -result_t Device::recv(unsigned int timeout, symbol_t* value) { +result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { if (!isValid()) { return RESULT_ERR_DEVICE; } @@ -178,9 +178,36 @@ result_t Device::recv(unsigned int timeout, symbol_t* value) { close(); return RESULT_ERR_DEVICE; } + if (*value != SYN || m_arbitrationMaster == SYN) { + if (m_listener != nullptr) { + m_listener->notifyDeviceData(*value, true); + } + if (m_arbitrationMaster != SYN) { + if (m_arbitrationCheck) { + *arbitrationState = *value == m_arbitrationMaster ? as_won : as_lost; + m_arbitrationMaster = SYN; + m_arbitrationCheck = false; + } else { + *arbitrationState = m_arbitrationMaster == SYN ? as_none : as_start; + } + } + return RESULT_OK; + } + ssize_t wcnt = write(m_arbitrationMaster); // send as fast as possible if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); } + if (wcnt != 1) { + *arbitrationState = as_error; + m_arbitrationMaster = SYN; + m_arbitrationCheck = false; + return RESULT_OK; + } + if (m_listener != NULL) { + m_listener->notifyDeviceData(m_arbitrationMaster, false); + } + m_arbitrationCheck = true; + *arbitrationState = as_running; return RESULT_OK; } diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 7c555f8f..c8b5ffbc 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -39,6 +39,16 @@ namespace ebusd { * to a file and/or forwarding it to a logging function. */ +/** the arbitration state handled by @a Device. */ +enum ArbitrationState { + as_none, //!< no arbitration in process + as_start, //!< arbitration start requested + as_error, //!< error while sending master address + as_running, //!< arbitration currently running (master address sent, waiting for reception) + as_lost, //!< arbitration lost + as_won, //!< arbitration won +}; + /** * Interface for listening to data received on/sent to a device. */ @@ -72,7 +82,7 @@ class Device { */ Device(const char* name, bool checkDevice, bool readOnly, bool initialSend) : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1), - m_listener(nullptr) {} + m_listener(nullptr), m_arbitrationMaster(SYN), m_arbitrationCheck(false) {} /** * Destructor. @@ -119,9 +129,31 @@ class Device { * Read a single byte from the device. * @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite. * @param value the reference in which the received byte value is stored. + * @param arbitrationState the reference in which the current @a ArbitrationState is stored on success. When set to + * @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); + result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState); + + /** + * Start the arbitration with the specified master address. A subsequent request while an arbitration is currently in + * checking state will always result in @a RESULT_ERR_DUPLICATE. + * @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) { + if (m_arbitrationCheck) { + return RESULT_ERR_DUPLICATE; + } + if (m_readOnly) { + return RESULT_ERR_SEND; + } + m_arbitrationCheck = false; + m_arbitrationMaster = masterAddress; + return RESULT_OK; + } + + bool isArbitrating() const { return m_arbitrationMaster != SYN; }; /** * Return the device name. @@ -193,6 +225,12 @@ class Device { private: /** the @a DeviceListener, or nullptr. */ DeviceListener* m_listener; + + /** the arbitration master address to send when in arbitration, or @a SYN. */ + symbol_t m_arbitrationMaster; + + /** true when in arbitration and the next received symbol needs to be checked against the sent master address. */ + bool m_arbitrationCheck; }; /** From 12d51406052fa7b92dbd16c9da68b1a3777a3f8f Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 13 May 2018 18:28:56 +0200 Subject: [PATCH 03/48] removed unused test --- .gitignore | 1 - src/lib/ebus/test/CMakeLists.txt | 4 ---- src/lib/ebus/test/Makefile.am | 5 ----- 3 files changed, 10 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 src/lib/ebus/test/CMakeLists.txt mode change 100644 => 100755 src/lib/ebus/test/Makefile.am diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 5ed1c7db..02eb9709 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ app.info /src/lib/utils/libutils.a /src/lib/ebus/libebus.a /src/lib/ebus/contrib/test/test_tem -/src/lib/ebus/test/test_device /src/lib/ebus/test/test_symbol /src/lib/ebus/test/test_data /src/lib/ebus/test/test_message diff --git a/src/lib/ebus/test/CMakeLists.txt b/src/lib/ebus/test/CMakeLists.txt old mode 100644 new mode 100755 index 002d779d..128a2a0b --- a/src/lib/ebus/test/CMakeLists.txt +++ b/src/lib/ebus/test/CMakeLists.txt @@ -11,10 +11,6 @@ add_executable(test_filereader test_filereader.cpp) target_link_libraries(test_filereader ebus pthread) add_test(filereader test_filereader) -add_executable(test_device test_device.cpp) -target_link_libraries(test_device ebus pthread ${test_LIBS}) -add_test(device test_device) - add_executable(test_symbol test_symbol.cpp) target_link_libraries(test_symbol ebus pthread) add_test(symbol test_symbol) diff --git a/src/lib/ebus/test/Makefile.am b/src/lib/ebus/test/Makefile.am old mode 100644 new mode 100755 index 2b7aa4f8..da3acee1 --- a/src/lib/ebus/test/Makefile.am +++ b/src/lib/ebus/test/Makefile.am @@ -3,7 +3,6 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \ -Wno-unused-parameter noinst_PROGRAMS = test_filereader \ - test_device \ test_symbol \ test_data \ test_message @@ -11,9 +10,6 @@ noinst_PROGRAMS = test_filereader \ test_filereader_SOURCES = test_filereader.cpp test_filereader_LDADD = ../libebus.a -lpthread -test_device_SOURCES = test_device.cpp -test_device_LDADD = ../libebus.a -lpthread - test_symbol_SOURCES = test_symbol.cpp test_symbol_LDADD = ../libebus.a -lpthread @@ -24,7 +20,6 @@ test_message_SOURCES = test_message.cpp test_message_LDADD = ../libebus.a -lpthread if CONTRIB -test_device_LDADD += ../contrib/libebuscontrib.a test_data_LDADD += ../contrib/libebuscontrib.a test_message_LDADD += ../contrib/libebuscontrib.a endif From d3120760bc83d6cd19e59fca0a1814d386c80080 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 13 May 2018 20:26:39 +0200 Subject: [PATCH 04/48] added support for enhanced protocol --- src/lib/ebus/device.cpp | 126 ++++++++++++++++++++++++++++++++++------ src/lib/ebus/device.h | 40 ++++++------- 2 files changed, 125 insertions(+), 41 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 825b1960..41b14410 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -47,6 +47,17 @@ namespace ebusd { #define POLLRDHUP 0 #endif +// ebusd enhanced protocol IDs: +//TODO use this: +#define ENH_INIT ((symbol_t)0x00) +#define ENH_RESETTED ((symbol_t)0x00) +#define ENH_SEND ((symbol_t)0x01) +#define ENH_RECEIVED ((symbol_t)0x01) +#define ENH_START ((symbol_t)0x02) +#define ENH_STARTED ((symbol_t)0x02) +#define ENH_FAILED ((symbol_t)0x82) + + Device::~Device() { close(); } @@ -55,8 +66,13 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i if (strchr(name, '/') == nullptr && strchr(name, ':') != nullptr) { char* in = strdup(name); bool udp = false; + bool enhanced = false; char* addrpos = in; char* portpos = strchr(addrpos, ':'); + if (portpos >= addrpos+3 && strncmp(addrpos, "enh", 3) == 0) { + enhanced = true; + addrpos += 3; + } if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { addrpos += 4; portpos = strchr(addrpos, ':'); @@ -85,7 +101,7 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i free(in); address.sin_family = AF_INET; address.sin_port = (in_port_t)htons((uint16_t)port); - return new NetworkDevice(name, address, readOnly, initialSend, udp); + return new NetworkDevice(name, address, readOnly, initialSend, udp, enhanced); } return new SerialDevice(name, checkDevice, readOnly, initialSend); } @@ -111,7 +127,7 @@ result_t Device::send(symbol_t value) { if (!isValid()) { return RESULT_ERR_DEVICE; } - if (m_readOnly || write(value) != 1) { + if (m_readOnly || !write(value)) { return RESULT_ERR_SEND; } if (m_listener != nullptr) { @@ -170,19 +186,21 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a } // directly read byte from device - ssize_t nbytes = read(value); - if (nbytes == 0) { - return RESULT_ERR_EOF; - } - if (nbytes < 0) { + if (!read(value, m_enhancedProto ? arbitrationState : nullptr)) { close(); return RESULT_ERR_DEVICE; } - if (*value != SYN || m_arbitrationMaster == SYN) { + ArbitrationState prevState = m_enhancedProto && arbitrationState ? *arbitrationState : as_none; + if (*value != SYN || m_arbitrationMaster == SYN || m_enhancedProto) { if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); } - if (m_arbitrationMaster != SYN) { + if (m_enhancedProto) { + if (arbitrationState && *arbitrationState != prevState) { + m_arbitrationMaster = SYN; + m_arbitrationCheck = false; + } + } else if (m_arbitrationMaster != SYN) { if (m_arbitrationCheck) { *arbitrationState = *value == m_arbitrationMaster ? as_won : as_lost; m_arbitrationMaster = SYN; @@ -193,11 +211,11 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a } return RESULT_OK; } - ssize_t wcnt = write(m_arbitrationMaster); // send as fast as possible + bool wrote = write(m_arbitrationMaster); // send as fast as possible if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); } - if (wcnt != 1) { + if (!wrote) { *arbitrationState = as_error; m_arbitrationMaster = SYN; m_arbitrationCheck = false; @@ -211,6 +229,35 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a return RESULT_OK; } +result_t Device::startArbitration(symbol_t masterAddress) { + if (m_arbitrationCheck) { + return RESULT_ERR_DUPLICATE; + } + if (m_readOnly) { + return RESULT_ERR_SEND; + } + m_arbitrationCheck = false; + m_arbitrationMaster = masterAddress; + if (m_enhancedProto) { + if (!write(masterAddress, true)) { + m_arbitrationMaster = SYN; + m_arbitrationCheck = false; + return RESULT_ERR_SEND; + } + m_arbitrationCheck = true; + return RESULT_OK; + } + return RESULT_OK; +} + +bool Device::write(symbol_t value, bool startArbitration) { + return ::write(m_fd, &value, 1) == 1; +} + +bool Device::read(symbol_t* value, ArbitrationState* arbitrationState) { + return ::read(m_fd, value, 1) == 1; +} + result_t SerialDevice::open() { if (m_fd != -1) { @@ -266,7 +313,7 @@ result_t SerialDevice::open() { // set serial device into blocking mode fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); - if (m_initialSend && write(ESC) != 1) { + if (m_initialSend && !write(ESC)) { return RESULT_ERR_SEND; } return RESULT_OK; @@ -337,7 +384,7 @@ result_t NetworkDevice::open() { } } m_bufLen = 0; - if (m_initialSend && write(ESC) != 1) { + if (m_initialSend && !write(ESC)) { return RESULT_ERR_SEND; } return RESULT_OK; @@ -359,29 +406,70 @@ bool NetworkDevice::available() { return m_buffer && m_bufLen > 0; } -ssize_t NetworkDevice::write(symbol_t value) { +bool NetworkDevice::write(symbol_t value, bool startArbitration) { m_bufLen = 0; // flush read buffer + if (m_bufSize > 0 && m_enhancedProto) { + m_buffer[0] = startArbitration ? ENH_START : ENH_SEND; + m_buffer[1] = value; + return ::write(m_fd, m_buffer, 2) == 2; + } return Device::write(value); } -ssize_t NetworkDevice::read(symbol_t* value) { +bool NetworkDevice::read(symbol_t* value, ArbitrationState* arbitrationState) { if (available()) { *value = m_buffer[m_bufPos]; m_bufPos = (m_bufPos+1)%m_bufSize; m_bufLen--; - return 1; + return true; } if (m_bufSize > 0) { ssize_t size = ::read(m_fd, m_buffer, m_bufSize); if (size <= 0) { - return size; + return false; + } + if (m_enhancedProto) { + symbol_t* buf = m_buffer; + m_bufPos = 0; + m_bufLen = 0; + while (size > 0) { + buf++; + size--; + switch (*buf) { + case ENH_STARTED: + if (arbitrationState) { + *arbitrationState = as_won; + } + break; + case ENH_FAILED: + if (arbitrationState) { + *arbitrationState = as_error; + } + break; + case ENH_RECEIVED: + m_buffer[m_bufPos++] = *buf; + m_bufLen++; + break; + case ENH_RESETTED: // TODO + break; + default: + return false; + } + } + if (m_bufLen > 0) { + *value = m_buffer[0]; + m_bufPos = 1; + m_bufLen--; + return true; + } + return false; } *value = m_buffer[0]; m_bufPos = 1; m_bufLen = size-1; - return size; + return true; } - return Device::read(value); + return Device::read(value, arbitrationState); } } // namespace ebusd diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index c8b5ffbc..92768e33 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -79,10 +79,12 @@ class Device { * @param checkDevice whether to regularly check the device availability (only for serial devices). * @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 the device supports the ebusd enhanced protocol. */ - Device(const char* name, bool checkDevice, bool readOnly, bool initialSend) - : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1), - m_listener(nullptr), m_arbitrationMaster(SYN), m_arbitrationCheck(false) {} + Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false) + : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), + m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN), + m_arbitrationCheck(false) {} /** * Destructor. @@ -141,17 +143,7 @@ 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) { - if (m_arbitrationCheck) { - return RESULT_ERR_DUPLICATE; - } - if (m_readOnly) { - return RESULT_ERR_SEND; - } - m_arbitrationCheck = false; - m_arbitrationMaster = masterAddress; - return RESULT_OK; - } + result_t startArbitration(symbol_t masterAddress); bool isArbitrating() const { return m_arbitrationMaster != SYN; }; @@ -195,16 +187,16 @@ class Device { /** * Write a single byte. * @param value the byte value to write. - * @return the number of bytes written, or -1 on error. + * @return true on success, false on error. */ - virtual ssize_t write(symbol_t value) { return ::write(m_fd, &value, 1); } + virtual bool write(symbol_t value, bool startArbitration=false); /** * Read a single byte. * @param value the reference in which the read byte value is stored. - * @return the number of bytes read, or -1 on error. + * @return true on success, false on error. */ - virtual ssize_t read(symbol_t* value) { return ::read(m_fd, value, 1); } + virtual bool read(symbol_t* value, ArbitrationState* arbitrationState=nullptr); /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ const char* m_name; @@ -218,6 +210,9 @@ class Device { /** 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; + /** the opened file descriptor, or -1. */ int m_fd; @@ -277,10 +272,11 @@ class NetworkDevice : public Device { * @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 udp true for UDP, false to TCP. + * @param enhancedProto whether the device supports the ebusd enhanced protocol. */ NetworkDevice(const char* name, const struct sockaddr_in& address, bool readOnly, bool initialSend, - bool udp) - : Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp), + bool udp, bool enhancedProto=false) + : Device(name, true, readOnly, initialSend, enhancedProto), m_address(address), m_udp(udp), m_buffer(nullptr), m_bufSize(0), m_bufLen(0), m_bufPos(0) {} /** @@ -309,10 +305,10 @@ class NetworkDevice : public Device { bool available() override; // @copydoc - ssize_t write(symbol_t value) override; + bool write(symbol_t value, bool startArbitration=false) override; // @copydoc - ssize_t read(symbol_t* value) override; + bool read(symbol_t* value, ArbitrationState* arbitrationState=nullptr) override; private: From 76013fbc277bf7e74b18eaa49f9fb410d12e6901 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Sep 2018 12:50:40 +0200 Subject: [PATCH 05/48] moved buffering to common Device, completed support for enhanced proto --- src/lib/ebus/device.cpp | 229 +++++++++++++++++++++++----------------- src/lib/ebus/device.h | 70 +++++------- 2 files changed, 159 insertions(+), 140 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 41b14410..ce324c75 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -57,9 +57,30 @@ namespace ebusd { #define ENH_STARTED ((symbol_t)0x02) #define ENH_FAILED ((symbol_t)0x82) +/** + * Construct a new instance. + * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param address the socket address of the device. + * @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 udp true for UDP, false to TCP. + * @param enhancedProto whether the device supports the ebusd enhanced protocol. + */ +Device::Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto) + : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), + m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN), + m_arbitrationCheck(false), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(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::create(const char* name, bool checkDevice, bool readOnly, bool initialSend) { @@ -72,6 +93,10 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i if (portpos >= addrpos+3 && strncmp(addrpos, "enh", 3) == 0) { enhanced = true; addrpos += 3; + if (portpos == addrpos) { + addrpos++; + portpos = strchr(addrpos, ':'); + } } if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { addrpos += 4; @@ -106,11 +131,17 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i return new SerialDevice(name, checkDevice, readOnly, initialSend); } +result_t Device::open() { + close(); + return m_bufSize == 0 ? RESULT_ERR_DEVICE : RESULT_OK; +} + void Device::close() { if (m_fd != -1) { ::close(m_fd); m_fd = -1; } + m_bufLen = 0; // flush read buffer } bool Device::isValid() { @@ -140,7 +171,8 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a if (!isValid()) { return RESULT_ERR_DEVICE; } - if (!available() && timeout > 0) { + bool isAvailable = available(); + if (!isAvailable && timeout > 0) { int ret; struct timespec tdiff; @@ -185,18 +217,18 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a } } + ArbitrationState prevState = *arbitrationState; // directly read byte from device - if (!read(value, m_enhancedProto ? arbitrationState : nullptr)) { + if (!read(value, isAvailable, arbitrationState)) { close(); return RESULT_ERR_DEVICE; } - ArbitrationState prevState = m_enhancedProto && arbitrationState ? *arbitrationState : as_none; - if (*value != SYN || m_arbitrationMaster == SYN || m_enhancedProto) { + if (m_enhancedProto || *value != SYN || m_arbitrationMaster == SYN) { if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); } if (m_enhancedProto) { - if (arbitrationState && *arbitrationState != prevState) { + if (*arbitrationState != prevState) { m_arbitrationMaster = SYN; m_arbitrationCheck = false; } @@ -236,32 +268,116 @@ result_t Device::startArbitration(symbol_t masterAddress) { if (m_readOnly) { return RESULT_ERR_SEND; } - m_arbitrationCheck = false; m_arbitrationMaster = masterAddress; - if (m_enhancedProto) { + m_arbitrationCheck = false; + if (m_enhancedProto && masterAddress != SYN) { if (!write(masterAddress, true)) { m_arbitrationMaster = SYN; - m_arbitrationCheck = false; return RESULT_ERR_SEND; } m_arbitrationCheck = true; - return RESULT_OK; } return RESULT_OK; } bool Device::write(symbol_t value, bool startArbitration) { + if (m_enhancedProto) { + symbol_t buf[2] = {startArbitration ? ENH_START : ENH_SEND, value}; + return ::write(m_fd, buf, 2) == 2; + } return ::write(m_fd, &value, 1) == 1; } -bool Device::read(symbol_t* value, ArbitrationState* arbitrationState) { - return ::read(m_fd, value, 1) == 1; +bool Device::available() { + if (m_bufLen <= 0) { + return false; + } + if (!m_enhancedProto) { + return true; + } + // peek into the received enhanced proto bytes to determine symbol availability + for (size_t pos = 0; pos < m_bufLen; pos++) { + symbol_t ch = m_buffer[(pos+m_bufPos)%m_bufSize]; + if (ch == ENH_RECEIVED) { + return pos+1 < m_bufLen; + } + } + return false; +} + +bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState) { + if (!isAvailable) { + if (m_bufLen > 0 && m_bufPos != 0) { + if (m_bufLen > m_bufSize / 2) { + m_bufLen = 0; // TODO report error + } else { + size_t tail; + if (m_bufPos+m_bufLen > m_bufSize) { + // move wrapped tail away + tail = (m_bufPos+m_bufLen) % m_bufSize; + size_t head = m_bufLen-tail; + memmove(m_buffer+head, m_buffer, tail); + } else { + tail = 0; + } + // move head to first position + memmove(m_buffer, m_buffer + m_bufPos, m_bufLen - tail); + } + } + m_bufPos = 0; + // fill up the buffer + ssize_t size = ::read(m_fd, m_buffer + m_bufLen, m_bufSize - m_bufLen); + if (size <= 0) { + return false; + } + m_bufLen += size; + } + if (!available()) { + return false; + } + if (!m_enhancedProto) { + *value = m_buffer[m_bufPos]; + m_bufPos = (m_bufPos+1)%m_bufSize; + m_bufLen--; + return true; + } + while (m_bufLen > 0) { + symbol_t ch = m_buffer[m_bufPos]; + m_bufPos = (m_bufPos+1)%m_bufSize; + m_bufLen--; + switch (ch) { + case ENH_STARTED: + *arbitrationState = as_won; + m_arbitrationMaster = SYN; + break; + case ENH_FAILED: + *arbitrationState = as_error; + m_arbitrationMaster = SYN; + break; + case ENH_RECEIVED: + if (m_bufLen <= 0) { + return false; + } + *value = m_buffer[m_bufPos]; + m_bufPos = (m_bufPos+1)%m_bufSize; + m_bufLen--; + return true; + case ENH_RESETTED: // TODO + *arbitrationState = as_error; + break; + default: + // TODO proto error + return false; + } + } + return false; } result_t SerialDevice::open() { - if (m_fd != -1) { - close(); + result_t result = Device::open(); + if (result != RESULT_OK) { + return result; } struct termios newSettings; @@ -339,8 +455,9 @@ void SerialDevice::checkDevice() { result_t NetworkDevice::open() { - if (m_fd != -1) { - close(); + result_t result = Device::open(); + if (result != RESULT_OK) { + return result; } m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0); if (m_fd < 0) { @@ -376,13 +493,6 @@ result_t NetworkDevice::open() { break; } } - if (m_bufSize == 0) { - m_bufSize = MAX_LEN+1; - m_buffer = reinterpret_cast(malloc(m_bufSize)); - if (!m_buffer) { - m_bufSize = 0; - } - } m_bufLen = 0; if (m_initialSend && !write(ESC)) { return RESULT_ERR_SEND; @@ -390,11 +500,6 @@ result_t NetworkDevice::open() { return RESULT_OK; } -void NetworkDevice::close() { - m_bufLen = 0; // flush read buffer - Device::close(); -} - void NetworkDevice::checkDevice() { int cnt; if (ioctl(m_fd, FIONREAD, &cnt) < 0) { @@ -402,74 +507,4 @@ void NetworkDevice::checkDevice() { } } -bool NetworkDevice::available() { - return m_buffer && m_bufLen > 0; -} - -bool NetworkDevice::write(symbol_t value, bool startArbitration) { - m_bufLen = 0; // flush read buffer - if (m_bufSize > 0 && m_enhancedProto) { - m_buffer[0] = startArbitration ? ENH_START : ENH_SEND; - m_buffer[1] = value; - return ::write(m_fd, m_buffer, 2) == 2; - } - return Device::write(value); -} - -bool NetworkDevice::read(symbol_t* value, ArbitrationState* arbitrationState) { - if (available()) { - *value = m_buffer[m_bufPos]; - m_bufPos = (m_bufPos+1)%m_bufSize; - m_bufLen--; - return true; - } - if (m_bufSize > 0) { - ssize_t size = ::read(m_fd, m_buffer, m_bufSize); - if (size <= 0) { - return false; - } - if (m_enhancedProto) { - symbol_t* buf = m_buffer; - m_bufPos = 0; - m_bufLen = 0; - while (size > 0) { - buf++; - size--; - switch (*buf) { - case ENH_STARTED: - if (arbitrationState) { - *arbitrationState = as_won; - } - break; - case ENH_FAILED: - if (arbitrationState) { - *arbitrationState = as_error; - } - break; - case ENH_RECEIVED: - m_buffer[m_bufPos++] = *buf; - m_bufLen++; - break; - case ENH_RESETTED: // TODO - break; - default: - return false; - } - } - if (m_bufLen > 0) { - *value = m_buffer[0]; - m_bufPos = 1; - m_bufLen--; - return true; - } - return false; - } - *value = m_buffer[0]; - m_bufPos = 1; - m_bufLen = size-1; - return true; - } - return Device::read(value, arbitrationState); -} - } // namespace ebusd diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 92768e33..84576291 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -81,10 +81,7 @@ class Device { * @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param enhancedProto whether the device supports the ebusd enhanced protocol. */ - Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false) - : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), - m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN), - m_arbitrationCheck(false) {} + Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false); /** * Destructor. @@ -113,7 +110,7 @@ class Device { * Open the file descriptor. * @return the @a result_t code. */ - virtual result_t open() = 0; // abstract + virtual result_t open(); /** * Close the file descriptor if opened. @@ -178,12 +175,6 @@ class Device { */ virtual void checkDevice() = 0; // abstract - /** - * Check whether a byte is available immediately (without waiting). - * @return true when a a byte is available immediately. - */ - virtual bool available() { return false; } - /** * Write a single byte. * @param value the byte value to write. @@ -191,12 +182,20 @@ class Device { */ virtual bool write(symbol_t value, bool startArbitration=false); + /** + * Check whether a symbol is available for reading immediately (without waiting). + * @return true when a symbol is available for reading immediately. + */ + virtual bool available(); + /** * Read a single byte. * @param value the reference in which the read byte value is stored. + * @param isAvailable the result of the immediately preceding call to @a available(). + * @param arbitrationState the variable in which to store the received arbitration state (mandatory for enhanced proto). * @return true on success, false on error. */ - virtual bool read(symbol_t* value, ArbitrationState* arbitrationState=nullptr); + virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState=nullptr); /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ const char* m_name; @@ -219,15 +218,28 @@ class Device { private: /** the @a DeviceListener, or nullptr. */ - DeviceListener* m_listener; +public: DeviceListener* m_listener; /** the arbitration master address to send when in arbitration, or @a SYN. */ symbol_t m_arbitrationMaster; /** true when in arbitration and the next received symbol needs to be checked against the sent master address. */ bool m_arbitrationCheck; + + /** the read buffer. */ + symbol_t* m_buffer; + + /** the read buffer size (multiple of 4). */ + size_t m_bufSize; + + /** the read buffer fill length. */ + size_t m_bufLen; + + /** the read buffer read position. */ + size_t m_bufPos; }; + /** * The @a Device for directly connected serial interfaces (tty). */ @@ -276,17 +288,12 @@ class NetworkDevice : public Device { */ NetworkDevice(const char* name, const struct sockaddr_in& address, bool readOnly, bool initialSend, bool udp, bool enhancedProto=false) - : Device(name, true, readOnly, initialSend, enhancedProto), m_address(address), m_udp(udp), - m_buffer(nullptr), m_bufSize(0), m_bufLen(0), m_bufPos(0) {} + : Device(name, true, readOnly, initialSend, enhancedProto), m_address(address), m_udp(udp) {} /** * Destructor. */ - virtual ~NetworkDevice() { - if (m_buffer) { - free(m_buffer); - } - } + ~NetworkDevice() override {} // @copydoc unsigned int getLatency() const override { return 10000; } @@ -294,22 +301,11 @@ class NetworkDevice : public Device { // @copydoc result_t open() override; - // @copydoc - void close() override; protected: // @copydoc void checkDevice() override; - // @copydoc - bool available() override; - - // @copydoc - bool write(symbol_t value, bool startArbitration=false) override; - - // @copydoc - bool read(symbol_t* value, ArbitrationState* arbitrationState=nullptr) override; - private: /** the socket address of the device. */ @@ -317,18 +313,6 @@ class NetworkDevice : public Device { /** true for UDP, false to TCP. */ const bool m_udp; - - /** the buffer memory, or nullptr. */ - symbol_t* m_buffer; - - /** the buffer size. */ - size_t m_bufSize; - - /** the buffer fill length. */ - size_t m_bufLen; - - /** the buffer read position. */ - size_t m_bufPos; }; } // namespace ebusd From fb70e32ee584d7382cfd57e27ebfa6ba792f69b0 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Sep 2018 12:58:24 +0200 Subject: [PATCH 06/48] completed support for enhanced proto --- src/ebusd/bushandler.cpp | 70 ++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 565c2ead..91d5750f 100755 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -454,11 +454,13 @@ result_t BusHandler::handleSymbol() { } if (startRequest != nullptr) { // initiate arbitration result_t ret = m_device->startArbitration(startRequest->m_master[0]); - if (ret != RESULT_OK) { + if (ret == RESULT_OK) { + logDebug(lf_bus, "arbitration start with %2.2x", startRequest->m_master[0]); + } else { logError(lf_bus, "arbitration start: %s", getResultCode(ret)); + m_nextRequests.remove(startRequest); m_currentRequest = startRequest; setState(bs_ready, ret); // force the failed request to be notified - startRequest = nullptr; } } } @@ -476,7 +478,7 @@ result_t BusHandler::handleSymbol() { break; case bs_recvCmdAck: - timeout = m_slaveRecvTimeout+(m_currentRequest ? m_transferLatency:0); + timeout = m_slaveRecvTimeout+(m_currentRequest ? m_transferLatency : 0); break; case bs_recvRes: @@ -603,32 +605,46 @@ result_t BusHandler::handleSymbol() { m_remainLockCount = 0; m_lastSynReceiveTime = recvTime; sentAutoSyn = true; + setState(bs_ready, RESULT_OK); } - if (arbitrationState == as_lost) { - if (m_currentRequest == nullptr) { - BusRequest *startRequest = m_nextRequests.peek(); - if (startRequest != nullptr && m_nextRequests.remove(startRequest)) { - m_currentRequest = startRequest; // force the failed request to be notified + switch (arbitrationState) { + case as_lost: + logDebug(lf_bus, "arbitration lost"); + if (m_currentRequest == nullptr) { + BusRequest *startRequest = m_nextRequests.peek(); + if (startRequest != nullptr && m_nextRequests.remove(startRequest)) { + m_currentRequest = startRequest; // force the failed request to be notified + } } - } - setState(m_state, RESULT_ERR_BUS_LOST); - } else if (arbitrationState == as_won) { // implies RESULT_OK - if (m_currentRequest == nullptr) { - m_currentRequest = m_nextRequests.peek(); - } - if (m_currentRequest == nullptr) { - logDebug(lf_bus, "arbitration won without request"); - } else if (m_state == bs_ready) { - if (!m_nextRequests.remove(m_currentRequest)) { - // request already removed (e.g. due to timeout) - return setState(bs_skip, RESULT_ERR_TIMEOUT); + setState(m_state, RESULT_ERR_BUS_LOST); + break; + case as_won: // implies RESULT_OK + if (m_currentRequest != nullptr) { + logNotice(lf_bus, "arbitration won while handling another request"); + setState(bs_ready, RESULT_OK); // force the current request to be notified + } else { + BusRequest *startRequest = m_nextRequests.peek(); + if (m_state != bs_ready || startRequest == nullptr || !m_nextRequests.remove(startRequest)) { + logNotice(lf_bus, "arbitration won in invalid state %s", getStateCode(m_state)); + setState(bs_ready, RESULT_ERR_TIMEOUT); + } else { + logDebug(lf_bus, "arbitration won"); + m_currentRequest = startRequest; + sendSymbol = m_currentRequest->m_master[0]; + sending = true; + } } - sendSymbol = m_currentRequest->m_master[0]; - sending = true; - } + break; + case as_running: + break; + case as_error: + logError(lf_bus, "arbitration start error"); // TODO cancel all requests? + break; + default: // only as_none + break; } - if (sentAutoSyn) { - return setState(bs_ready, RESULT_OK); + if (sentAutoSyn && !sending) { + return RESULT_OK; } time_t now; time(&now); @@ -978,6 +994,9 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit } m_currentRequest = nullptr; } + if (state == bs_skip) { + m_device->startArbitration(SYN); // reset arbitration state + } } if (state == bs_noSignal) { // notify all requests @@ -1012,6 +1031,7 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit } else if (m_state == bs_noSignal) { logNotice(lf_bus, "signal acquired"); } + // logDebug(lf_bus, "state: from %s to %s with %s", getStateCode(m_state), getStateCode(state), getResultCode(result)); m_state = state; if (state == bs_ready || state == bs_skip) { From 13ee2a1fd7ed41497874402184d0d8eb594566d3 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Sep 2018 14:06:52 +0200 Subject: [PATCH 07/48] notify sent master address on arbitration success/failure --- src/lib/ebus/device.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index ce324c75..ff3559a5 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -348,10 +348,16 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati switch (ch) { case ENH_STARTED: *arbitrationState = as_won; + if (m_listener != NULL) { + m_listener->notifyDeviceData(m_arbitrationMaster, false); + } m_arbitrationMaster = SYN; break; case ENH_FAILED: *arbitrationState = as_error; + if (m_listener != NULL) { + m_listener->notifyDeviceData(m_arbitrationMaster, false); + } m_arbitrationMaster = SYN; break; case ENH_RECEIVED: From 58082c65d862f8b3287d0550dc0ce3ec41ee0eaf Mon Sep 17 00:00:00 2001 From: John Date: Sat, 15 Sep 2018 15:59:16 +0200 Subject: [PATCH 08/48] Update ChangeLog.md --- ChangeLog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 5b7549d6..0d8ae104 100755 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,11 @@ +# next version + +## Bug Fixes +* fix for missing MQTT subscription after broker reconnect +* fix for answering to first scan only in answer mode +* fix don't add transfer latency to receive timeout when acting as SYN generator + + # 3.2 (2018-05-10) ## Breaking Changes From f65d194e65c8c3ce7d0047c8dd3980208d8994a0 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Sep 2018 16:05:37 +0200 Subject: [PATCH 09/48] updated --- ChangeLog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 0d8ae104..13aff986 100755 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,9 @@ * fix for answering to first scan only in answer mode * fix don't add transfer latency to receive timeout when acting as SYN generator +## Breaking Changes +* added support for enhanced network protocol mode for recent [ebusd-esp firmware](https://github.com/john30/ebusd-esp/) that allows the arbitration to be done directly by the Wemos + # 3.2 (2018-05-10) From 888e26263bf374bafd80d6a153f2a466d940c159 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Sep 2018 16:08:50 +0200 Subject: [PATCH 10/48] added enhanced protocol hint --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 README.md diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 122286b9..e512678e --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Features The main features of the daemon are: - * use USB serial, TCP connected, or UDP device + * use USB serial, TCP connected or UDP device, or enhanced ebusd protocol for recent [ebusd-esp firmware](https://github.com/john30/ebusd-esp/) (allows arbitration to be done directly by the Wemos) * actively send messages to and receive answers from the eBUS * passively listen to messages sent on the eBUS * regularly poll for messages From 7696cb822b325d202d3e2ee235f90c67b5107ae4 Mon Sep 17 00:00:00 2001 From: john Date: Sun, 16 Sep 2018 16:32:04 +0200 Subject: [PATCH 11/48] allow enh: prefix for serial device as well --- src/lib/ebus/device.cpp | 16 ++++++++++------ src/lib/ebus/device.h | 9 +++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index ff3559a5..35f88931 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -64,7 +64,7 @@ namespace ebusd { * @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 udp true for UDP, false to TCP. - * @param enhancedProto whether the device supports the ebusd enhanced protocol. + * @param enhancedProto whether to use the ebusd enhanced protocol. */ Device::Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto) : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), @@ -84,20 +84,23 @@ Device::~Device() { } Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool initialSend) { + bool enhanced = strncmp(name, "enh:", 4) == 0; + if (enhanced) { + name += 4; + } if (strchr(name, '/') == nullptr && strchr(name, ':') != nullptr) { char* in = strdup(name); bool udp = false; - bool enhanced = false; char* addrpos = in; char* portpos = strchr(addrpos, ':'); - if (portpos >= addrpos+3 && strncmp(addrpos, "enh", 3) == 0) { - enhanced = true; + if (!enhanced && portpos >= addrpos+3 && strncmp(addrpos, "enh", 3) == 0) { + enhanced = true; // support enhtcp:: and enhudp:: addrpos += 3; if (portpos == addrpos) { addrpos++; portpos = strchr(addrpos, ':'); } - } + } // else: support enh:: defaulting to TCP if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { addrpos += 4; portpos = strchr(addrpos, ':'); @@ -128,7 +131,8 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i address.sin_port = (in_port_t)htons((uint16_t)port); return new NetworkDevice(name, address, readOnly, initialSend, udp, enhanced); } - return new SerialDevice(name, checkDevice, readOnly, initialSend); + // support enh:/dev/ + return new SerialDevice(name, checkDevice, readOnly, initialSend, enhanced); } result_t Device::open() { diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 84576291..8a2ede7e 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -79,7 +79,7 @@ class Device { * @param checkDevice whether to regularly check the device availability (only for serial devices). * @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 the device supports the ebusd enhanced protocol. + * @param enhancedProto whether to use the ebusd enhanced protocol. */ Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false); @@ -251,9 +251,10 @@ class SerialDevice : public Device { * @param checkDevice whether to regularly check the device availability (only for serial devices). * @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. */ - SerialDevice(const char* name, bool checkDevice, bool readOnly, bool initialSend) - : Device(name, checkDevice, readOnly, initialSend) {} + SerialDevice(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false) + : Device(name, checkDevice, readOnly, initialSend, enhancedProto) {} // @copydoc result_t open() override; @@ -284,7 +285,7 @@ class NetworkDevice : public Device { * @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 udp true for UDP, false to TCP. - * @param enhancedProto whether the device supports the ebusd enhanced protocol. + * @param enhancedProto whether to use the ebusd enhanced protocol. */ NetworkDevice(const char* name, const struct sockaddr_in& address, bool readOnly, bool initialSend, bool udp, bool enhancedProto=false) From 8b04eb3d28c7797a7ca3bc6088b129b5fac7e443 Mon Sep 17 00:00:00 2001 From: john Date: Sun, 16 Sep 2018 19:54:28 +0200 Subject: [PATCH 12/48] better documentation --- src/lib/ebus/device.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 8a2ede7e..050c1e1d 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -76,7 +76,7 @@ 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 (only for serial devices). + * @param checkDevice whether to regularly check the device availability. * @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. @@ -200,7 +200,7 @@ class Device { /** 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 (only for serial devices). */ + /** whether to regularly check the device availability. */ const bool m_checkDevice; /** whether to allow read access to the device only. */ @@ -248,7 +248,7 @@ class SerialDevice : public 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 (only for serial devices). + * @param checkDevice whether to regularly check the device availability. * @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. From ce4f500198108d7caafee68e18535b07d8e127b0 Mon Sep 17 00:00:00 2001 From: john Date: Sun, 23 Sep 2018 12:21:51 +0200 Subject: [PATCH 13/48] set baudrate to 115200 for enhanced proto over serial connection --- src/lib/ebus/device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 35f88931..d3f29ce9 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -421,7 +421,7 @@ result_t SerialDevice::open() { // create new settings memset(&newSettings, 0, sizeof(newSettings)); - newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD); + newSettings.c_cflag |= ((m_enhancedProto ? B115200 : B2400) | CS8 | CLOCAL | CREAD); newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode newSettings.c_iflag |= IGNPAR; // ignore parity errors newSettings.c_oflag &= ~OPOST; From da3e674dd26496f3f89a3c53e346bb5efab77c2d Mon Sep 17 00:00:00 2001 From: John Date: Fri, 30 Nov 2018 08:07:28 +0100 Subject: [PATCH 14/48] added enhanced protocol description --- docs/enhanced_proto.md | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/enhanced_proto.md diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md new file mode 100644 index 00000000..cf4d7f53 --- /dev/null +++ b/docs/enhanced_proto.md @@ -0,0 +1,50 @@ +## Protocol + +Every communication in either direction is prefixed with at most one byte indicating the kind of action/result. + +### from ebusd to device + * initialization (after connect) + `` + This is not yet implemented in ebusd and supposed to be enhanced by another byte requesting certain features. + * send request + ` ` + Requests the specified symbol to be sent to the eBUS. + * arbitration start request + ` ` + Requests the start of the arbitration process with the specified master address after the next received `` symbol. + +### from device to ebusd + * initialization (after re-connect) + `` + Indicates a reboot or an initial ebusd connection on the device. + This is actually desired to be enhanced by another byte in future indicating availability of certain features (like full message sending instead of arbitration onnly). + * symbol received from eBUS + ` ` + Indicates that the specified symbol was received from the eBUS. + * arbitration start succeeded + `` + Indicates that the last arbitration request was successful (arbitration was won). + * arbitration start failed + `` + Indicates that the last arbitration request failed (arbitration was lost or sending failed). + + +## Symbols + +These are the predefined symbols as used above. + +### Generic + * SYN 0xAA + +### From ebusd to device + * INIT 0x00 + * SEND 0x01 + * START 0x02 + +### From device to ebusd + * RESETTED 0x00 + * RECEIVED 0x01 + * STARTED 0x02 + * FAILED 0x82 + + From ede35485d2c25ab58bac407f1340055dbc4f7a24 Mon Sep 17 00:00:00 2001 From: john Date: Sun, 6 Oct 2019 17:06:55 +0200 Subject: [PATCH 15/48] documentation --- src/lib/ebus/device.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 6d8e0b15..f959e3a8 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -143,6 +143,10 @@ class Device { */ result_t startArbitration(symbol_t masterAddress); + /** + * Return whether the device is currently in arbitration. + * @return true when the device is currently in arbitration. + */ bool isArbitrating() const { return m_arbitrationMaster != SYN; }; /** @@ -179,6 +183,7 @@ class Device { /** * Write a single byte. * @param value the byte value to write. + * @param startArbitration true to start arbitration. * @return true on success, false on error. */ virtual bool write(symbol_t value, bool startArbitration=false); From 5d8db10fbf0822dc79facb25cd9b09d2d0bf60ae Mon Sep 17 00:00:00 2001 From: john30 Date: Tue, 26 Nov 2019 20:57:20 +0100 Subject: [PATCH 16/48] updated to next generation of enhanced protocol --- docs/enhanced_proto.md | 90 ++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md index cf4d7f53..cc9503de 100644 --- a/docs/enhanced_proto.md +++ b/docs/enhanced_proto.md @@ -1,50 +1,70 @@ +## Transfer speed + +In order to compensate potential overhead of transfer encoding, the transfer speed is set to 9600 Baud with 8 bits, no parity, and 1 stop bit. + + ## Protocol +Data bytes with a value below 0x80 can be transferred as is. -Every communication in either direction is prefixed with at most one byte indicating the kind of action/result. +Data bytes with value above or equal to 0x80 are split up into two bytes, each one with the highest bit set to 1. +The second bit indicates whether it is the first or second byte of a split transfer. This way protocol errors can easily be detected. +The bits in the two bytes look like this: -### from ebusd to device - * initialization (after connect) - `` - This is not yet implemented in ebusd and supposed to be enhanced by another byte requesting certain features. - * send request - ` ` - Requests the specified symbol to be sent to the eBUS. +``` +first second +76543210 76543210 +11ccccdd 10dddddd +``` +4 bits in `c` are used for indicating a special purpose and is set to one of the command request/response symbols as stated below. +8 bits in `d` are the data byte to be transferred (might also be unused). + +### Command request/response symbols + +#### from ebusd to device + * initialization request + ` ` + Requests an initialization of the device and requests special features in the data byte (tbd). + * send data request + ` ` + Requests the specified data byte in `d` to be sent to the eBUS. + For data byte values <0x80, the short form without the `` prefix is allowed as well. * arbitration start request - ` ` - Requests the start of the arbitration process with the specified master address after the next received `` symbol. + ` ` + Requests the start of the arbitration process after the next received `` symbol with the specified master address in `d`. -### from device to ebusd - * initialization (after re-connect) - `` - Indicates a reboot or an initial ebusd connection on the device. - This is actually desired to be enhanced by another byte in future indicating availability of certain features (like full message sending instead of arbitration onnly). - * symbol received from eBUS - ` ` - Indicates that the specified symbol was received from the eBUS. - * arbitration start succeeded - `` - Indicates that the last arbitration request was successful (arbitration was won). +#### from device to ebusd + * initialization response + ` ` + Indicates a reboot or an initial ebusd connection on the device and is expected to be returned after an ` request. + The data byte `d` indicates availability of certain features (like full message sending instead of arbitration only, tbd). + * receive data notification + ` ` + Indicates that the specified data byte in `d` was received from the eBUS. + For data byte values <0x80, the short form without the `` prefix is allowed as well. + * arbitration start succeeded + ` ` + Indicates the the last arbitration request succeeded (arbitration was won). + The data byte in `d` may contain additional information (tbd). * arbitration start failed - `` - Indicates that the last arbitration request failed (arbitration was lost or sending failed). + ` ` + Indicates that the last arbitration request failed (arbitration was lost or sending failed). + The data byte in `d` contains the address of the master that won the arbitration. ## Symbols These are the predefined symbols as used above. -### Generic +### Bus symbols * SYN 0xAA -### From ebusd to device - * INIT 0x00 - * SEND 0x01 - * START 0x02 - -### From device to ebusd - * RESETTED 0x00 - * RECEIVED 0x01 - * STARTED 0x02 - * FAILED 0x82 - +### Command request symbols (from ebusd to device) + * INIT 0x0 + * SEND 0x1 + * START 0x2 +### Command response symbols (from device to ebusd) + * RESETTED 0x0 + * RECEIVED 0x1 + * STARTED 0x2 + * FAILED 0xa From 8e41736056b5cbcbede18584de6107833e69ab54 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 30 Nov 2019 00:00:24 +0100 Subject: [PATCH 17/48] changed STARTED to report used master address --- docs/enhanced_proto.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md index cc9503de..ddffa02e 100644 --- a/docs/enhanced_proto.md +++ b/docs/enhanced_proto.md @@ -42,9 +42,9 @@ first second Indicates that the specified data byte in `d` was received from the eBUS. For data byte values <0x80, the short form without the `` prefix is allowed as well. * arbitration start succeeded - ` ` + ` ` Indicates the the last arbitration request succeeded (arbitration was won). - The data byte in `d` may contain additional information (tbd). + The data byte in `d` contains the master address that was successfully used during arbitration. * arbitration start failed ` ` Indicates that the last arbitration request failed (arbitration was lost or sending failed). From f84b5bb4ce8eb2fdbd6d8e1f7db8d79e4f856936 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 30 Nov 2019 00:13:36 +0100 Subject: [PATCH 18/48] implemented new enhanced proto --- src/lib/ebus/device.cpp | 87 +++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 9732e05d..c26c8b02 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -51,14 +51,19 @@ namespace ebusd { #endif // ebusd enhanced protocol IDs: -//TODO use this: -#define ENH_INIT ((symbol_t)0x00) -#define ENH_RESETTED ((symbol_t)0x00) -#define ENH_SEND ((symbol_t)0x01) -#define ENH_RECEIVED ((symbol_t)0x01) -#define ENH_START ((symbol_t)0x02) -#define ENH_STARTED ((symbol_t)0x02) -#define ENH_FAILED ((symbol_t)0x82) +#define ENH_REQ_INIT ((uint8_t)0x0) +#define ENH_RES_RESETTED ((uint8_t)0x0) +#define ENH_REQ_SEND ((uint8_t)0x1) +#define ENH_RES_RECEIVED ((uint8_t)0x1) +#define ENH_REQ_START ((uint8_t)0x2) +#define ENH_RES_STARTED ((uint8_t)0x2) +#define ENH_RES_FAILED ((uint8_t)0xa) + +#define ENH_BYTE_FLAG ((uint8_t)0x80) +#define ENH_BYTE_MASK ((uint8_t)0xc0) +#define ENH_BYTE1 ((uint8_t)0xc0) +#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))} /** * Construct a new instance. @@ -289,7 +294,7 @@ result_t Device::startArbitration(symbol_t masterAddress) { bool Device::write(symbol_t value, bool startArbitration) { if (m_enhancedProto) { - symbol_t buf[2] = {startArbitration ? ENH_START : ENH_SEND, value}; + symbol_t buf[2] = makeEnhancedSequence(startArbitration ? ENH_REQ_START : ENH_REQ_SEND, value); return ::write(m_fd, buf, 2) == 2; } return ::write(m_fd, &value, 1) == 1; @@ -305,9 +310,13 @@ bool Device::available() { // peek into the received enhanced proto bytes to determine symbol availability for (size_t pos = 0; pos < m_bufLen; pos++) { symbol_t ch = m_buffer[(pos+m_bufPos)%m_bufSize]; - if (ch == ENH_RECEIVED) { + if (!(ch&ENH_BYTE_FLAG)) { + return true; + } + if ((ch&ENH_BYTE_MASK) == ENH_BYTE1) { return pos+1 < m_bufLen; } + // TODO check protocol error } return false; } @@ -316,6 +325,7 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati if (!isAvailable) { if (m_bufLen > 0 && m_bufPos != 0) { if (m_bufLen > m_bufSize / 2) { + // more than half of input buffer consumed is taken as signal that ebusd is too slow m_bufLen = 0; // TODO report error } else { size_t tail; @@ -350,36 +360,56 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } while (m_bufLen > 0) { symbol_t ch = m_buffer[m_bufPos]; + if (!(ch&ENH_BYTE_FLAG)) { + *value = ch; + m_bufPos = (m_bufPos+1)%m_bufSize; + m_bufLen--; + return true; + } + uint8_t kind = ch&ENH_BYTE_MASK; + if (kind == ENH_BYTE1 && m_bufLen<2) { + return false; // transfer not complete yet + } m_bufPos = (m_bufPos+1)%m_bufSize; m_bufLen--; + if (kind == ENH_BYTE2) { + return false; // TODO protocol error + } + // kind is ENH_BYTE1 + symbol_t ch2 = m_buffer[m_bufPos]; + if ((ch2 & ENH_BYTE_MASK) != ENH_BYTE2) { + return false; // TODO protocol error + } + m_bufPos = (m_bufPos + 1) % m_bufSize; + m_bufLen--; + ch2 = (symbol_t)(((ch&0x03)<<6) | (ch&0x3f)); + ch = (ch>>2)&0xf; switch (ch) { - case ENH_STARTED: + case ENH_RES_STARTED: *arbitrationState = as_won; if (m_listener != NULL) { - m_listener->notifyDeviceData(m_arbitrationMaster, false); + m_listener->notifyDeviceData(ch2, false); } m_arbitrationMaster = SYN; break; - case ENH_FAILED: + case ENH_RES_FAILED: *arbitrationState = as_error; if (m_listener != NULL) { - m_listener->notifyDeviceData(m_arbitrationMaster, false); + m_listener->notifyDeviceData(ch2, false); } m_arbitrationMaster = SYN; break; - case ENH_RECEIVED: - if (m_bufLen <= 0) { - return false; - } - *value = m_buffer[m_bufPos]; - m_bufPos = (m_bufPos+1)%m_bufSize; - m_bufLen--; + case ENH_RES_RECEIVED: + *value = ch2; return true; - case ENH_RESETTED: // TODO - *arbitrationState = as_error; + case ENH_RES_RESETTED: // TODO + if (*arbitrationState != as_none) { + *arbitrationState = as_error; + } + // TODO define additional feature flags break; default: - // TODO proto error + // TODO protocol error return false; } } @@ -434,7 +464,7 @@ result_t SerialDevice::open() { // create new settings memset(&newSettings, 0, sizeof(newSettings)); - cfsetspeed(&newSettings, m_enhancedProto ? B115200 : B2400); + cfsetspeed(&newSettings, m_enhancedProto ? B9600 : B2400); newSettings.c_cflag |= (CS8 | CLOCAL | CREAD); newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode newSettings.c_iflag |= IGNPAR; // ignore parity errors @@ -456,7 +486,12 @@ result_t SerialDevice::open() { // set serial device into blocking mode fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); - if (m_initialSend && !write(ESC)) { + if (m_enhancedProto) { + symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INIT, 0); // TODO define additional feature flags + if (::write(m_fd, buf, 2) != 2) { + return RESULT_ERR_SEND; + } + } else if (m_initialSend && !write(ESC)) { return RESULT_ERR_SEND; } return RESULT_OK; From 2af779edf9be9663ce3f1832bca7096482d80657 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 30 Nov 2019 09:38:09 +0100 Subject: [PATCH 19/48] added traffic examples --- docs/enhanced_proto.md | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md index ddffa02e..4eddaba7 100644 --- a/docs/enhanced_proto.md +++ b/docs/enhanced_proto.md @@ -68,3 +68,62 @@ These are the predefined symbols as used above. * RECEIVED 0x1 * STARTED 0x2 * FAILED 0xa + + +## Examples + +### Passive receive +The data sequence (without SYN, ACK, and CRC) `1508951200 / 0164` when ebusd is only listening to traffic on the bus would usually be transferred as follows (with all extra symbols seen on the bus): + +|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| +|----:|-----|-----|-----|-----|-----| +|1|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| +|2|`QQ`|0x15|interface|`<0x15>`|0x15| +|3|`ZZ`|0x08|interface|`<0x08>`|0x08| +|4|`PB`|0x95|interface|` <0x95>`|0xC6 0x95| +|5|`SB`|0x12|interface|`<0x12>`|0x12| +|6|`NN`|0x00|interface|`<0x00>`|0x00| +|7|`CRC`|0xAF|interface|` <0xAF>`|0xC6 0xAF| +|8|`ACK`|0x00|interface|`<0x00>`|0x00| +|9|`NN`|0x01|interface|`<0x01>`|0x01| +|10|`DD`|0x64|interface|`<0x64>`|0x64| +|11|`CRC`|0xFF|interface|` <0xFF>`|0xC7 0xBF| +|12|`ACK`|0x00|interface|`<0x00>`|0x00| +|13|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| + +### Active successful send +The same data sequence (without SYN, ACK, and CRC) `1508951200 / 0164` when initiated by ebusd as master (with address 0x15) would usually be transferred as follows (with all extra symbols seen on the bus): + +|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| +|----:|-----|-----|-----|-----|-----| +|1| |0x15|ebusd|` <0x15>`|0xC8 0x95| +|2|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| +|3|`QQ`|0x15|interface|`<0x15>`|0x15| +|4| |0x15|ebusd|` <0x15>`|0xC8 0x95| +|5|`ZZ`|0x08|ebusd|`<0x08>`|0x08| +|6|`ZZ`|0x08|interface|`<0x08>`|0x08| +|7|`PB`|0x95|ebusd|` <0x95>`|0xC6 0x95| +|8|`PB`|0x95|interface|` <0x95>`|0xC6 0x95| +|9|`SB`|0x12|ebusd|`<0x12>`|0x12| +|10|`SB`|0x12|interface|`<0x12>`|0x12| +|11|`NN`|0x00|ebusd|`<0x00>`|0x00| +|12|`NN`|0x00|interface|`<0x00>`|0x00| +|13|`CRC`|0xAF|ebusd|` <0xAF>`|0xC6 0xAF| +|14|`CRC`|0xAF|interface|` <0xAF>`|0xC6 0xAF| +|15|`ACK`|0x00|interface|`<0x00>`|0x00| +|16|`NN`|0x01|interface|`<0x01>`|0x01| +|17|`DD`|0x64|interface|`<0x64>`|0x64| +|18|`CRC`|0xFF|interface|` <0xFF>`|0xC7 0xBF| +|19|`ACK`|0x00|ebusd|`<0x00>`|0x00| +|20|`ACK`|0x00|interface|`<0x00>`|0x00| +|21|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| + +### Active failed traffic +A failed arbitration when initiated by ebusd as master (with address 0x15) would usually be transferred as follows (with all extra symbols seen on the bus): + +|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| +|----:|-----|-----|-----|-----|-----| +|1| |0x15|ebusd|` <0x15>`|0xC8 0x95| +|2|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| +|3|`QQ`|0x03|interface|`<0x03>`|0x03| +|4| |0x15|ebusd|` <0x15>`|0xE8 0x95| From 05e629a7423502f2d7cd6408911a836980845252 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 30 Nov 2019 10:08:04 +0100 Subject: [PATCH 20/48] changed FAILED to include the sent master address instead of the received one, renamed device to interface, corrected traffic example, added SYN generator example --- docs/enhanced_proto.md | 62 ++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md index 4eddaba7..c40db8cb 100644 --- a/docs/enhanced_proto.md +++ b/docs/enhanced_proto.md @@ -20,10 +20,10 @@ first second ### Command request/response symbols -#### from ebusd to device +#### from ebusd to interface * initialization request ` ` - Requests an initialization of the device and requests special features in the data byte (tbd). + Requests an initialization of the interface and requests special features in the data byte (tbd). * send data request ` ` Requests the specified data byte in `d` to be sent to the eBUS. @@ -32,10 +32,10 @@ first second ` ` Requests the start of the arbitration process after the next received `` symbol with the specified master address in `d`. -#### from device to ebusd +#### from interface to ebusd * initialization response ` ` - Indicates a reboot or an initial ebusd connection on the device and is expected to be returned after an ` request. + Indicates a reboot or an initial ebusd connection on the interface and is expected to be returned after an ` request. The data byte `d` indicates availability of certain features (like full message sending instead of arbitration only, tbd). * receive data notification ` ` @@ -44,11 +44,11 @@ first second * arbitration start succeeded ` ` Indicates the the last arbitration request succeeded (arbitration was won). - The data byte in `d` contains the master address that was successfully used during arbitration. + The data byte in `d` contains the master address that was sent to eBUS during arbitration. * arbitration start failed ` ` Indicates that the last arbitration request failed (arbitration was lost or sending failed). - The data byte in `d` contains the address of the master that won the arbitration. + The data byte in `d` contains the master address that was sent to eBUS during arbitration. ## Symbols @@ -58,12 +58,12 @@ These are the predefined symbols as used above. ### Bus symbols * SYN 0xAA -### Command request symbols (from ebusd to device) +### Command request symbols (from ebusd to interface) * INIT 0x0 * SEND 0x1 * START 0x2 -### Command response symbols (from device to ebusd) +### Command response symbols (from interface to ebusd) * RESETTED 0x0 * RECEIVED 0x1 * STARTED 0x2 @@ -73,17 +73,17 @@ These are the predefined symbols as used above. ## Examples ### Passive receive -The data sequence (without SYN, ACK, and CRC) `1508951200 / 0164` when ebusd is only listening to traffic on the bus would usually be transferred as follows (with all extra symbols seen on the bus): +The master-slave data sequence (without SYN, ACK, and CRC) `1008951200 / 0164` when ebusd is only listening to traffic on the bus would usually be transferred as follows (with all extra symbols seen on the bus): -|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| +|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced bytes| |----:|-----|-----|-----|-----|-----| |1|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| -|2|`QQ`|0x15|interface|`<0x15>`|0x15| +|2|`QQ`|0x10|interface|`<0x10>`|0x10| |3|`ZZ`|0x08|interface|`<0x08>`|0x08| |4|`PB`|0x95|interface|` <0x95>`|0xC6 0x95| |5|`SB`|0x12|interface|`<0x12>`|0x12| |6|`NN`|0x00|interface|`<0x00>`|0x00| -|7|`CRC`|0xAF|interface|` <0xAF>`|0xC6 0xAF| +|7|`CRC`|0xB1|interface|` <0xB1>`|0xC6 0xB1| |8|`ACK`|0x00|interface|`<0x00>`|0x00| |9|`NN`|0x01|interface|`<0x01>`|0x01| |10|`DD`|0x64|interface|`<0x64>`|0x64| @@ -92,24 +92,24 @@ The data sequence (without SYN, ACK, and CRC) `1508951200 / 0164` when ebusd is |13|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| ### Active successful send -The same data sequence (without SYN, ACK, and CRC) `1508951200 / 0164` when initiated by ebusd as master (with address 0x15) would usually be transferred as follows (with all extra symbols seen on the bus): +The same data sequence `1008951200 / 0164` when initiated by ebusd as master (with address 0x10) would usually be transferred as follows (with all extra symbols seen on the bus): -|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| +|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced bytes| |----:|-----|-----|-----|-----|-----| -|1| |0x15|ebusd|` <0x15>`|0xC8 0x95| +|1| | |ebusd|` <0x10>`|0xC8 0x90| |2|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| -|3|`QQ`|0x15|interface|`<0x15>`|0x15| -|4| |0x15|ebusd|` <0x15>`|0xC8 0x95| +|3| | |interface|` <0x10>`|0xC8 0x90| +|4|`QQ`|0x10|interface|`<0x10>`|0x10| |5|`ZZ`|0x08|ebusd|`<0x08>`|0x08| |6|`ZZ`|0x08|interface|`<0x08>`|0x08| -|7|`PB`|0x95|ebusd|` <0x95>`|0xC6 0x95| +|7|`PB`|0x95|ebusd|` <0x95>`|0xC6 0x95| |8|`PB`|0x95|interface|` <0x95>`|0xC6 0x95| |9|`SB`|0x12|ebusd|`<0x12>`|0x12| |10|`SB`|0x12|interface|`<0x12>`|0x12| |11|`NN`|0x00|ebusd|`<0x00>`|0x00| |12|`NN`|0x00|interface|`<0x00>`|0x00| -|13|`CRC`|0xAF|ebusd|` <0xAF>`|0xC6 0xAF| -|14|`CRC`|0xAF|interface|` <0xAF>`|0xC6 0xAF| +|13|`CRC`|0xB1|ebusd|` <0xB1>`|0xC6 0xB1| +|14|`CRC`|0xB1|interface|` <0xB1>`|0xC6 0xB1| |15|`ACK`|0x00|interface|`<0x00>`|0x00| |16|`NN`|0x01|interface|`<0x01>`|0x01| |17|`DD`|0x64|interface|`<0x64>`|0x64| @@ -118,12 +118,28 @@ The same data sequence (without SYN, ACK, and CRC) `1508951200 / 0164` when init |20|`ACK`|0x00|interface|`<0x00>`|0x00| |21|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| + +### Active successful send as SYN generator +The same data sequence `1008951200 / 0164` when initiated by ebusd as master (with address 0x10) and acting as SYN generator would usually be transferred as follows (with all extra symbols seen on the bus): + +|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced bytes| +|----:|-----|-----|-----|-----|-----| +|1| | |ebusd|` <0x10>`|0xC8 0x90| +|2|`SYN`|0xAA|ebusd|` <0xAA>`|0xC6 0xAA| +|3|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| +|4| | |interface|` <0x10>`|0xC8 0x90| +|5|`QQ`|0x10|interface|`<0x10>`|0x10| +|...|see above| | | | | +The rest of the communcation is the same as before (from 5.) + + ### Active failed traffic A failed arbitration when initiated by ebusd as master (with address 0x15) would usually be transferred as follows (with all extra symbols seen on the bus): |order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| |----:|-----|-----|-----|-----|-----| -|1| |0x15|ebusd|` <0x15>`|0xC8 0x95| +|1| | |ebusd|` <0x10>`|0xC8 0x90| |2|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| -|3|`QQ`|0x03|interface|`<0x03>`|0x03| -|4| |0x15|ebusd|` <0x15>`|0xE8 0x95| +|3| |0x10|ebusd|` <0x10>`|0xE0 0x90| +|4|`QQ`|0x03|interface|`<0x03>`|0x03| + From a68adba23fd1aeabddd9595215edc8e8bec90001 Mon Sep 17 00:00:00 2001 From: john30 Date: Tue, 3 Dec 2019 08:01:06 +0100 Subject: [PATCH 21/48] fix for new enhanced proto --- src/lib/ebus/device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index c26c8b02..cde2d157 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -382,7 +382,7 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } m_bufPos = (m_bufPos + 1) % m_bufSize; m_bufLen--; - ch2 = (symbol_t)(((ch&0x03)<<6) | (ch&0x3f)); + ch2 = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f)); ch = (ch>>2)&0xf; switch (ch) { case ENH_RES_STARTED: From a82ef6631fc86fe76267704bdc1e06bddf821ab8 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 25 Jan 2020 19:44:41 +0100 Subject: [PATCH 22/48] fix for two-byte enhanced transfer polling, fix unnecessary buffer allocation, fix skip for invalid protocol bytes --- src/lib/ebus/device.cpp | 106 ++++++++++++++++++++++------------------ src/lib/ebus/device.h | 3 +- 2 files changed, 60 insertions(+), 49 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 8c586219..3ba25013 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -183,58 +183,68 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a if (!isValid()) { return RESULT_ERR_DEVICE; } - bool isAvailable = available(); - if (!isAvailable && timeout > 0) { - int ret; - struct timespec tdiff; + bool repeat = false; + bool repeated = false; + ArbitrationState prevState = *arbitrationState; + do { + repeat = false; + bool isAvailable = available(); + if (!isAvailable && timeout > 0) { + int ret; + struct timespec tdiff; - // set select timeout - tdiff.tv_sec = timeout/1000000; - tdiff.tv_nsec = (timeout%1000000)*1000; + // set select timeout + tdiff.tv_sec = timeout/1000000; + tdiff.tv_nsec = (timeout%1000000)*1000; #ifdef HAVE_PPOLL - nfds_t nfds = 1; - struct pollfd fds[nfds]; + nfds_t nfds = 1; + struct pollfd fds[nfds]; - memset(fds, 0, sizeof(fds)); + memset(fds, 0, sizeof(fds)); - fds[0].fd = m_fd; - fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; - ret = ppoll(fds, nfds, &tdiff, nullptr); - if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) { - ret = -1; - } + fds[0].fd = m_fd; + fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; + ret = ppoll(fds, nfds, &tdiff, nullptr); + if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) { + ret = -1; + } #else #ifdef HAVE_PSELECT - fd_set readfds, exceptfds; + fd_set readfds, exceptfds; - FD_ZERO(&readfds); - FD_ZERO(&exceptfds); - FD_SET(m_fd, &readfds); + FD_ZERO(&readfds); + FD_ZERO(&exceptfds); + FD_SET(m_fd, &readfds); - ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr); - if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) { - ret = -1; - } + ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr); + if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) { + ret = -1; + } #else - ret = 1; // ignore timeout if neither ppoll nor pselect are available + ret = 1; // ignore timeout if neither ppoll nor pselect are available #endif #endif - if (ret == -1) { - close(); - return RESULT_ERR_DEVICE; + if (ret == -1) { + close(); + return RESULT_ERR_DEVICE; + } + if (ret == 0) { + return RESULT_ERR_TIMEOUT; + } } - if (ret == 0) { + + // directly read byte from device + bool incomplete = false; + if (!read(value, isAvailable, arbitrationState, &incomplete)) { + if (!isAvailable && incomplete && !repeated) { + // for a two-byte transfer another poll is needed + repeat = true; + continue; + } return RESULT_ERR_TIMEOUT; } - } - - ArbitrationState prevState = *arbitrationState; - // directly read byte from device - if (!read(value, isAvailable, arbitrationState)) { - close(); - return RESULT_ERR_DEVICE; - } + } while (repeat); if (m_enhancedProto || *value != SYN || m_arbitrationMaster == SYN) { if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); @@ -316,12 +326,16 @@ bool Device::available() { if ((ch&ENH_BYTE_MASK) == ENH_BYTE1) { return pos+1 < m_bufLen; } - // TODO check protocol error + // TODO protocol error + // skip byte from erroneous protocol + m_bufPos = (m_bufPos+1)%m_bufSize; + m_bufLen--; + pos--; } return false; } -bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState) { +bool Device::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) { @@ -350,6 +364,9 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati m_bufLen += size; } if (!available()) { + if (incomplete) { + *incomplete = m_enhancedProto && m_bufLen > 0; + } return false; } if (!m_enhancedProto) { @@ -377,11 +394,11 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } // kind is ENH_BYTE1 symbol_t ch2 = m_buffer[m_bufPos]; + m_bufPos = (m_bufPos + 1) % m_bufSize; + m_bufLen--; if ((ch2 & ENH_BYTE_MASK) != ENH_BYTE2) { return false; // TODO protocol error } - m_bufPos = (m_bufPos + 1) % m_bufSize; - m_bufLen--; ch2 = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f)); ch = (ch>>2)&0xf; switch (ch) { @@ -577,13 +594,6 @@ result_t NetworkDevice::open() { close(); return RESULT_ERR_GENERIC_IO; } - if (m_bufSize == 0) { - m_bufSize = MAX_LEN+1; - m_buffer = reinterpret_cast(malloc(m_bufSize)); - if (!m_buffer) { - m_bufSize = 0; - } - } m_bufLen = 0; if (m_initialSend && !write(ESC)) { return RESULT_ERR_SEND; diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index f959e3a8..925f4aab 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -199,9 +199,10 @@ class Device { * @param value the reference in which the read byte value is stored. * @param isAvailable the result of the immediately preceding call to @a available(). * @param arbitrationState the variable in which to store the received arbitration state (mandatory for enhanced proto). + * @param incomplete the variable in which to store when a partial transfer needs another poll. * @return true on success, false on error. */ - virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState=nullptr); + 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; From 3a77957e28d508d37b955fae47c09d572c121795 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Feb 2020 09:00:53 +0100 Subject: [PATCH 23/48] make terminal change immediate (otherwise sometimes causes params not being set under cygwin) --- src/lib/ebus/device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 3ba25013..c46f9d2e 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -495,7 +495,7 @@ result_t SerialDevice::open() { tcflush(m_fd, TCIFLUSH); // activate new settings of serial device - if (tcsetattr(m_fd, TCSAFLUSH, &newSettings)) { + if (tcsetattr(m_fd, TCSANOW, &newSettings)) { close(); return RESULT_ERR_DEVICE; } From 6d35c90c9cd6234ddad3602113545849ad39582d Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Feb 2020 09:53:19 +0100 Subject: [PATCH 24/48] shorten timeout for receiving second part of enhanced proto --- src/lib/ebus/device.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index c46f9d2e..b7434e8b 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -179,6 +179,12 @@ result_t Device::send(symbol_t value) { return RESULT_OK; } +/** + * the maximum duration to wait for an enhanced sequence to complete after the first part was already retrieved: + * Start+8Bit+Stop+Extra @ 9600Bd. + */ +#define ENHANCED_COMPLETE_WAIT_DURATION 1150 + result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { if (!isValid()) { return RESULT_ERR_DEVICE; @@ -240,6 +246,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a if (!isAvailable && incomplete && !repeated) { // for a two-byte transfer another poll is needed repeat = true; + timeout = ENHANCED_COMPLETE_WAIT_DURATION; continue; } return RESULT_ERR_TIMEOUT; From 4174531a9d2cef4a781c28fa2b5bc672eb358222 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Feb 2020 15:57:22 +0100 Subject: [PATCH 25/48] added notifyStatus to DeviceListener and log errors/info from enhanced device, switch to send error instead of duplicate when starting arbitration if that is already in progress, fix enhanced arbitration, also send initial reset if enhanced device is on network --- src/ebusd/mainloop.cpp | 8 ++++ src/ebusd/mainloop.h | 3 ++ src/lib/ebus/device.cpp | 95 +++++++++++++++++++++++++++-------------- src/lib/ebus/device.h | 15 ++++++- 4 files changed, 89 insertions(+), 32 deletions(-) diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index a295e41f..f7d55bbe 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -474,6 +474,14 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) { } } +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::decodeMessage(const string &data, bool isHttp, bool* connected, ClientSettings* settings, string* user, bool* reload, ostringstream* ostream) { string token, previous; diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 742ce866..f4ba8208 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -132,6 +132,9 @@ class MainLoop : public Thread, DeviceListener { // @copydoc void notifyDeviceData(symbol_t symbol, bool received) override; + // @copydoc + void notifyStatus(bool error, const char* message) override; + protected: // @copydoc diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index b7434e8b..3dd787c6 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -148,6 +148,22 @@ result_t Device::open() { return m_bufSize == 0 ? RESULT_ERR_DEVICE : RESULT_OK; } +result_t Device::afterOpen() { + m_bufLen = 0; + if (m_enhancedProto) { + symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INIT, 0); // TODO define additional feature flags + if (::write(m_fd, buf, 2) != 2) { + return RESULT_ERR_SEND; + } + if (m_listener != nullptr) { + m_listener->notifyStatus(false, "resetting"); + } + } else if (m_initialSend && !write(ESC)) { + return RESULT_ERR_SEND; + } + return RESULT_OK; +} + void Device::close() { if (m_fd != -1) { ::close(m_fd); @@ -181,9 +197,10 @@ result_t Device::send(symbol_t value) { /** * the maximum duration to wait for an enhanced sequence to complete after the first part was already retrieved: - * Start+8Bit+Stop+Extra @ 9600Bd. + * 2* (Start+8Bit+Stop+Extra @ 9600Bd) */ -#define ENHANCED_COMPLETE_WAIT_DURATION 1150 +#define ENHANCED_COMPLETE_WAIT_DURATION (2*1150) + result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { if (!isValid()) { @@ -272,6 +289,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a } return RESULT_OK; } + // non-enhanced: arbitration executed by ebusd itself bool wrote = write(m_arbitrationMaster); // send as fast as possible if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); @@ -282,7 +300,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a m_arbitrationCheck = false; return RESULT_OK; } - if (m_listener != NULL) { + if (m_listener != nullptr) { m_listener->notifyDeviceData(m_arbitrationMaster, false); } m_arbitrationCheck = true; @@ -292,7 +310,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a result_t Device::startArbitration(symbol_t masterAddress) { if (m_arbitrationCheck) { - return RESULT_ERR_DUPLICATE; + return RESULT_ERR_SEND; // should not occur } if (m_readOnly) { return RESULT_ERR_SEND; @@ -331,9 +349,26 @@ bool Device::available() { return true; } if ((ch&ENH_BYTE_MASK) == ENH_BYTE1) { - return pos+1 < m_bufLen; + if (pos+1 >= m_bufLen) { + return false; + } + // peek into next byte to check if enhanced sequence is ok + ch = m_buffer[(pos+m_bufPos+1)%m_bufSize]; + if (!(ch&ENH_BYTE_FLAG) || (ch&ENH_BYTE_MASK) != ENH_BYTE2) { + if (m_listener != nullptr) { + m_listener->notifyStatus(true, "unexpected available enhanced following byte 1"); + } + // drop first byte of invalid sequence + m_bufPos = (m_bufPos + 1) % m_bufSize; + m_bufLen--; + pos--; + continue; + } + return true; + } + if (m_listener != nullptr) { + m_listener->notifyStatus(true, "unexpected available enhanced byte 2"); } - // TODO protocol error // skip byte from erroneous protocol m_bufPos = (m_bufPos+1)%m_bufSize; m_bufLen--; @@ -347,7 +382,10 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati if (m_bufLen > 0 && m_bufPos != 0) { if (m_bufLen > m_bufSize / 2) { // more than half of input buffer consumed is taken as signal that ebusd is too slow - m_bufLen = 0; // TODO report error + m_bufLen = 0; + if (m_listener != nullptr) { + m_listener->notifyStatus(true, "buffer overflow"); + } } else { size_t tail; if (m_bufPos+m_bufLen > m_bufSize) { @@ -397,31 +435,33 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati m_bufPos = (m_bufPos+1)%m_bufSize; m_bufLen--; if (kind == ENH_BYTE2) { - return false; // TODO protocol error + if (m_listener != nullptr) { + m_listener->notifyStatus(true, "unexpected enhanced byte 2"); + } + return false; } // kind is ENH_BYTE1 symbol_t ch2 = m_buffer[m_bufPos]; m_bufPos = (m_bufPos + 1) % m_bufSize; m_bufLen--; if ((ch2 & ENH_BYTE_MASK) != ENH_BYTE2) { - return false; // TODO protocol error + if (m_listener != nullptr) { + m_listener->notifyStatus(true, "missing enhanced byte 2"); + } + return false; } ch2 = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f)); ch = (ch>>2)&0xf; switch (ch) { case ENH_RES_STARTED: *arbitrationState = as_won; - if (m_listener != NULL) { - m_listener->notifyDeviceData(ch2, false); - } m_arbitrationMaster = SYN; + m_arbitrationCheck = false; break; case ENH_RES_FAILED: - *arbitrationState = as_error; - if (m_listener != NULL) { - m_listener->notifyDeviceData(ch2, false); - } + *arbitrationState = as_lost; m_arbitrationMaster = SYN; + m_arbitrationCheck = false; break; case ENH_RES_RECEIVED: *value = ch2; @@ -431,9 +471,14 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati *arbitrationState = as_error; } // TODO define additional feature flags + if (m_listener != nullptr) { + m_listener->notifyStatus(false, "reset"); + } break; default: - // TODO protocol error + if (m_listener != nullptr) { + m_listener->notifyStatus(true, "unexpected enhanced command"); + } return false; } } @@ -510,15 +555,7 @@ result_t SerialDevice::open() { // set serial device into blocking mode fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); - if (m_enhancedProto) { - symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INIT, 0); // TODO define additional feature flags - if (::write(m_fd, buf, 2) != 2) { - return RESULT_ERR_SEND; - } - } else if (m_initialSend && !write(ESC)) { - return RESULT_ERR_SEND; - } - return RESULT_OK; + return afterOpen(); } void SerialDevice::close() { @@ -601,11 +638,7 @@ result_t NetworkDevice::open() { close(); return RESULT_ERR_GENERIC_IO; } - m_bufLen = 0; - if (m_initialSend && !write(ESC)) { - return RESULT_ERR_SEND; - } - return RESULT_OK; + return afterOpen(); } void NetworkDevice::checkDevice() { diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 925f4aab..b4a8290c 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -66,6 +66,13 @@ class DeviceListener { * @param received @a true on reception, @a false on sending. */ virtual void notifyDeviceData(symbol_t symbol, bool received) = 0; // abstract + + /** + * Called to notify a status message from the device. + * @param error true for an error message, false for an info message. + * @param message the message string. + */ + virtual void notifyStatus(bool error, const char* message) = 0; // abstract }; @@ -113,6 +120,12 @@ class Device { */ virtual result_t open(); + /** + * 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(); + /** * Close the file descriptor if opened. */ @@ -225,7 +238,7 @@ class Device { private: /** the @a DeviceListener, or nullptr. */ -public: DeviceListener* m_listener; + DeviceListener* m_listener; /** the arbitration master address to send when in arbitration, or @a SYN. */ symbol_t m_arbitrationMaster; From f2aae4cb2bcff2ef0f553c08ec3e02d564131f6c Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 9 Feb 2020 12:05:26 +0100 Subject: [PATCH 26/48] avoid unnecessary host traffic --- docs/enhanced_proto.md | 47 ++++++++++++++++++++--------------------- src/lib/ebus/device.cpp | 6 ++++++ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md index c40db8cb..8621da99 100644 --- a/docs/enhanced_proto.md +++ b/docs/enhanced_proto.md @@ -41,6 +41,7 @@ first second ` ` Indicates that the specified data byte in `d` was received from the eBUS. For data byte values <0x80, the short form without the `` prefix is allowed as well. + Note that this message shall not be sent when the byte received was part of an arbitration request initiated by ebusd. * arbitration start succeeded ` ` Indicates the the last arbitration request succeeded (arbitration was won). @@ -48,7 +49,7 @@ first second * arbitration start failed ` ` Indicates that the last arbitration request failed (arbitration was lost or sending failed). - The data byte in `d` contains the master address that was sent to eBUS during arbitration. + The data byte in `d` contains the master address that has won the arbitration. ## Symbols @@ -98,25 +99,24 @@ The same data sequence `1008951200 / 0164` when initiated by ebusd as master (wi |----:|-----|-----|-----|-----|-----| |1| | |ebusd|` <0x10>`|0xC8 0x90| |2|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| -|3| | |interface|` <0x10>`|0xC8 0x90| -|4|`QQ`|0x10|interface|`<0x10>`|0x10| -|5|`ZZ`|0x08|ebusd|`<0x08>`|0x08| -|6|`ZZ`|0x08|interface|`<0x08>`|0x08| -|7|`PB`|0x95|ebusd|` <0x95>`|0xC6 0x95| -|8|`PB`|0x95|interface|` <0x95>`|0xC6 0x95| -|9|`SB`|0x12|ebusd|`<0x12>`|0x12| -|10|`SB`|0x12|interface|`<0x12>`|0x12| -|11|`NN`|0x00|ebusd|`<0x00>`|0x00| -|12|`NN`|0x00|interface|`<0x00>`|0x00| -|13|`CRC`|0xB1|ebusd|` <0xB1>`|0xC6 0xB1| -|14|`CRC`|0xB1|interface|` <0xB1>`|0xC6 0xB1| -|15|`ACK`|0x00|interface|`<0x00>`|0x00| -|16|`NN`|0x01|interface|`<0x01>`|0x01| -|17|`DD`|0x64|interface|`<0x64>`|0x64| -|18|`CRC`|0xFF|interface|` <0xFF>`|0xC7 0xBF| -|19|`ACK`|0x00|ebusd|`<0x00>`|0x00| -|20|`ACK`|0x00|interface|`<0x00>`|0x00| -|21|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| +|3|`QQ`|0x10|interface|` <0x10>`|0xC8 0x90| +|4|`ZZ`|0x08|ebusd|`<0x08>`|0x08| +|5|`ZZ`|0x08|interface|`<0x08>`|0x08| +|6|`PB`|0x95|ebusd|` <0x95>`|0xC6 0x95| +|7|`PB`|0x95|interface|` <0x95>`|0xC6 0x95| +|8|`SB`|0x12|ebusd|`<0x12>`|0x12| +|9|`SB`|0x12|interface|`<0x12>`|0x12| +|10|`NN`|0x00|ebusd|`<0x00>`|0x00| +|11|`NN`|0x00|interface|`<0x00>`|0x00| +|12|`CRC`|0xB1|ebusd|` <0xB1>`|0xC6 0xB1| +|13|`CRC`|0xB1|interface|` <0xB1>`|0xC6 0xB1| +|14|`ACK`|0x00|interface|`<0x00>`|0x00| +|15|`NN`|0x01|interface|`<0x01>`|0x01| +|16|`DD`|0x64|interface|`<0x64>`|0x64| +|17|`CRC`|0xFF|interface|` <0xFF>`|0xC7 0xBF| +|18|`ACK`|0x00|ebusd|`<0x00>`|0x00| +|19|`ACK`|0x00|interface|`<0x00>`|0x00| +|20|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| ### Active successful send as SYN generator @@ -127,14 +127,13 @@ The same data sequence `1008951200 / 0164` when initiated by ebusd as master (wi |1| | |ebusd|` <0x10>`|0xC8 0x90| |2|`SYN`|0xAA|ebusd|` <0xAA>`|0xC6 0xAA| |3|`SYN`|0xAA|interface|` <0xAA>`|0xC6 0xAA| -|4| | |interface|` <0x10>`|0xC8 0x90| -|5|`QQ`|0x10|interface|`<0x10>`|0x10| +|4|`QQ`|0x10|interface|` <0x10>`|0xC8 0x90| |...|see above| | | | | -The rest of the communcation is the same as before (from 5.) +The rest of the communcation is the same as before (from 4.) ### Active failed traffic -A failed arbitration when initiated by ebusd as master (with address 0x15) would usually be transferred as follows (with all extra symbols seen on the bus): +A failed arbitration when initiated by ebusd as master (with address 0x10) would usually be transferred as follows (with all extra symbols seen on the bus): |order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte| |----:|-----|-----|-----|-----|-----| diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 3dd787c6..c9517a61 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -455,11 +455,17 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati switch (ch) { case ENH_RES_STARTED: *arbitrationState = as_won; + if (m_listener != NULL) { + m_listener->notifyDeviceData(ch2, false); + } m_arbitrationMaster = SYN; m_arbitrationCheck = false; break; case ENH_RES_FAILED: *arbitrationState = as_lost; + if (m_listener != NULL) { + m_listener->notifyDeviceData(ch2, false); + } m_arbitrationMaster = SYN; m_arbitrationCheck = false; break; From 2ddd7df75863687c96cd51ea8abf73881b1fe89d Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Feb 2020 12:39:24 +0100 Subject: [PATCH 27/48] add enhanced error codes --- docs/enhanced_proto.md | 12 ++++++++++ src/lib/ebus/device.cpp | 50 ++++++++++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/docs/enhanced_proto.md b/docs/enhanced_proto.md index 8621da99..36ea9604 100644 --- a/docs/enhanced_proto.md +++ b/docs/enhanced_proto.md @@ -50,6 +50,14 @@ first second ` ` Indicates that the last arbitration request failed (arbitration was lost or sending failed). The data byte in `d` contains the master address that has won the arbitration. + * eBUS communication error + ` ` + Indicates an error in the eBUS UART. + The data byte in `d` contains the error message. + * host communication error + ` ` + Indicates an error in the host UART. + The data byte in `d` contains the error message. ## Symbols @@ -70,6 +78,10 @@ These are the predefined symbols as used above. * STARTED 0x2 * FAILED 0xa +### Error codes (from interface to ebusd) + * ERR_FRAMING 0x00: framing error + * ERR_OVERRUN 0x00: buffer overrun error + ## Examples diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index c9517a61..46ae6486 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "lib/ebus/data.h" namespace ebusd { @@ -58,6 +60,12 @@ namespace ebusd { #define ENH_REQ_START ((uint8_t)0x2) #define ENH_RES_STARTED ((uint8_t)0x2) #define ENH_RES_FAILED ((uint8_t)0xa) +#define ENH_RES_ERROR_EBUS ((uint8_t)0xb) +#define ENH_RES_ERROR_HOST ((uint8_t)0xc) + +// ebusd enhanced error codes for the ERROR_* responses +#define ENH_ERR_FRAMING ((uint8_t)0x00) +#define ENH_ERR_OVERRUN ((uint8_t)0x01) #define ENH_BYTE_FLAG ((uint8_t)0x80) #define ENH_BYTE_MASK ((uint8_t)0xc0) @@ -450,13 +458,13 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } return false; } - ch2 = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f)); - ch = (ch>>2)&0xf; - switch (ch) { + symbol_t data = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f)); + symbol_t cmd = (ch>>2)&0xf; + switch (cmd) { case ENH_RES_STARTED: *arbitrationState = as_won; if (m_listener != NULL) { - m_listener->notifyDeviceData(ch2, false); + m_listener->notifyDeviceData(data, false); } m_arbitrationMaster = SYN; m_arbitrationCheck = false; @@ -464,15 +472,15 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati case ENH_RES_FAILED: *arbitrationState = as_lost; if (m_listener != NULL) { - m_listener->notifyDeviceData(ch2, false); + m_listener->notifyDeviceData(data, false); } m_arbitrationMaster = SYN; m_arbitrationCheck = false; break; case ENH_RES_RECEIVED: - *value = ch2; + *value = data; return true; - case ENH_RES_RESETTED: // TODO + case ENH_RES_RESETTED: if (*arbitrationState != as_none) { *arbitrationState = as_error; } @@ -481,9 +489,35 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati m_listener->notifyStatus(false, "reset"); } break; + case ENH_RES_ERROR_EBUS: + case ENH_RES_ERROR_HOST: + if (m_listener != nullptr) { + ostringstream stream; + stream << (cmd==ENH_RES_ERROR_EBUS ? "eBUS comm error: " : "host comm error: "); + switch (data) { + case ENH_ERR_FRAMING: + stream << "framing"; + break; + case ENH_ERR_OVERRUN: + stream << "overrun"; + break; + default: + stream << "unknown 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(data); + break; + } + string str = stream.str(); + m_listener->notifyStatus(true, str.c_str()); + } + if (*arbitrationState != as_none) { + *arbitrationState = as_error; + } + break; default: if (m_listener != nullptr) { - m_listener->notifyStatus(true, "unexpected enhanced command"); + ostringstream stream; + stream << "unexpected enhanced command 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(cmd); + string str = stream.str(); + m_listener->notifyStatus(true, str.c_str()); } return false; } From b26bd2d9415b8ce63d9b9f9e2dac86d9468cf69f Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 15 Feb 2020 13:14:12 +0100 Subject: [PATCH 28/48] fix for handling arbitration result --- src/lib/ebus/device.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 46ae6486..2cb8dd06 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -468,7 +468,8 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } m_arbitrationMaster = SYN; m_arbitrationCheck = false; - break; + *value = data; + return true; case ENH_RES_FAILED: *arbitrationState = as_lost; if (m_listener != NULL) { @@ -476,7 +477,8 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } m_arbitrationMaster = SYN; m_arbitrationCheck = false; - break; + *value = data; + return true; case ENH_RES_RECEIVED: *value = data; return true; From 5a6920afd5570ee0f863f924ea4711236b806632 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 16 Feb 2020 10:49:17 +0100 Subject: [PATCH 29/48] simplified, fix for repeating read when first was incomplete --- src/lib/ebus/device.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 2cb8dd06..aa3394cb 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -216,7 +216,6 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a } bool repeat = false; bool repeated = false; - ArbitrationState prevState = *arbitrationState; do { repeat = false; bool isAvailable = available(); @@ -271,6 +270,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a if (!isAvailable && incomplete && !repeated) { // for a two-byte transfer another poll is needed repeat = true; + repeated = true; timeout = ENHANCED_COMPLETE_WAIT_DURATION; continue; } @@ -281,12 +281,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a if (m_listener != nullptr) { m_listener->notifyDeviceData(*value, true); } - if (m_enhancedProto) { - if (*arbitrationState != prevState) { - m_arbitrationMaster = SYN; - m_arbitrationCheck = false; - } - } else if (m_arbitrationMaster != SYN) { + if (!m_enhancedProto && m_arbitrationMaster != SYN) { if (m_arbitrationCheck) { *arbitrationState = *value == m_arbitrationMaster ? as_won : as_lost; m_arbitrationMaster = SYN; @@ -485,6 +480,8 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati case ENH_RES_RESETTED: if (*arbitrationState != as_none) { *arbitrationState = as_error; + m_arbitrationMaster = SYN; + m_arbitrationCheck = false; } // TODO define additional feature flags if (m_listener != nullptr) { @@ -512,6 +509,8 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati } if (*arbitrationState != as_none) { *arbitrationState = as_error; + m_arbitrationMaster = SYN; + m_arbitrationCheck = false; } break; default: From 78d8aa4d943eb734b95918f2caa34a14fec3ebce Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 29 Mar 2020 18:28:23 +0200 Subject: [PATCH 30/48] apply transfer latency also to auto SYN --- src/ebusd/bushandler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 04c09896..94847d57 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -576,8 +576,7 @@ result_t BusHandler::handleSymbol() { // receive next symbol (optionally check reception of sent symbol) symbol_t recvSymbol; ArbitrationState arbitrationState = as_none; - bool isAutoSyn = !sending && m_generateSynInterval == SYN_TIMEOUT && (m_state == bs_noSignal || m_state == bs_skip); - result = m_device->recv(timeout+(isAutoSyn ? 0 : m_transferLatency), &recvSymbol, &arbitrationState); + result = m_device->recv(timeout+m_transferLatency, &recvSymbol, &arbitrationState); if (sending) { clockGettime(&recvTime); } From dae039e563a656f51b79c614ce197c5cf7ce822d Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 11 Apr 2020 13:20:10 +0200 Subject: [PATCH 31/48] add FORWARD_RAW_TTY define to use raw output as option to forward data to a serial connection with same params as a usual bus interface --- src/lib/utils/rotatefile.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib/utils/rotatefile.cpp b/src/lib/utils/rotatefile.cpp index 8cf4cc68..19849a68 100755 --- a/src/lib/utils/rotatefile.cpp +++ b/src/lib/utils/rotatefile.cpp @@ -50,6 +50,22 @@ bool RotateFile::setEnabled(bool enabled) { if (enabled) { m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); m_fileSize = 0; +#ifdef FORWARD_RAW_TTY + if (!m_textMode && isatty(fileno(m_stream)) == 1) { + int fd = fileno(m_stream); + struct termios newSettings; + memset(&newSettings, 0, sizeof(newSettings)); + + cfsetspeed(&newSettings, B2400); + newSettings.c_cflag |= (CS8 | CLOCAL); + newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode + newSettings.c_iflag |= IGNPAR; // ignore parity errors + newSettings.c_oflag &= ~OPOST; + + // activate new settings of serial device + tcsetattr(fd, TCSANOW, &newSettings); + } +#endif } return true; } From a46292468af5da11a6a6a4573e97ff0948572ee1 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 11 Apr 2020 13:26:10 +0200 Subject: [PATCH 32/48] don't require logging if log file is not set --- src/lib/utils/log.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/utils/log.cpp b/src/lib/utils/log.cpp index 350cda61..940a2dbe 100755 --- a/src/lib/utils/log.cpp +++ b/src/lib/utils/log.cpp @@ -175,6 +175,9 @@ void closeLogFile() { } bool needsLog(const LogFacility facility, const LogLevel level) { + if (s_logFile == nullptr && !s_useSyslog) { + return false; + } return s_facilityLogLevel[facility] >= level; } From b2eed484b30ecade0b7a68093262c7415b0bc4f3 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 11 Apr 2020 13:26:40 +0200 Subject: [PATCH 33/48] fix potential illegal string usage --- src/ebusd/bushandler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 94847d57..5fccbc78 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -67,7 +67,8 @@ result_t PollRequest::prepare(symbol_t ownMasterAddress) { istringstream input; result_t result = m_message->prepareMaster(m_index, ownMasterAddress, SYN, UI_FIELD_SEPARATOR, &input, &m_master); if (result == RESULT_OK) { - logInfo(lf_bus, "poll cmd: %s", m_master.getStr().c_str()); + string str = m_master.getStr(); + logInfo(lf_bus, "poll cmd: %s", str.c_str()); } return result; } @@ -99,7 +100,8 @@ result_t ScanRequest::prepare(symbol_t ownMasterAddress) { istringstream input; m_result = m_message->prepareMaster(m_index, ownMasterAddress, dstAddress, UI_FIELD_SEPARATOR, &input, &m_master); if (m_result >= RESULT_OK) { - logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, m_master.getStr().c_str()); + string str = m_master.getStr(); + logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, str.c_str()); } return m_result; } @@ -181,7 +183,8 @@ bool ScanRequest::notify(result_t result, const SlaveSymbolString& slave) { bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) { if (result == RESULT_OK) { - logDebug(lf_bus, "read res: %s", slave.getStr().c_str()); + string str = m_master.getStr(); + logDebug(lf_bus, "read res: %s", str.c_str()); } m_result = result; *m_slave = slave; From 165efcb25fcd1383b943f15e9c4c83b516a2dacd Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 11 Apr 2020 13:27:51 +0200 Subject: [PATCH 34/48] add DEBUG_RAW_TRAFFIC define for debugging low level raw traffic --- src/lib/ebus/device.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index aa3394cb..f581a234 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -256,6 +256,9 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a #endif #endif if (ret == -1) { +#ifdef DEBUG_RAW_TRAFFIC + fprintf(stdout, "poll error %d\n", errno); +#endif close(); return RESULT_ERR_DEVICE; } @@ -349,6 +352,9 @@ bool Device::available() { for (size_t pos = 0; pos < m_bufLen; pos++) { symbol_t ch = m_buffer[(pos+m_bufPos)%m_bufSize]; if (!(ch&ENH_BYTE_FLAG)) { +#ifdef DEBUG_RAW_TRAFFIC + fprintf(stdout, "raw avail direct\n"); +#endif return true; } if ((ch&ENH_BYTE_MASK) == ENH_BYTE1) { @@ -358,6 +364,9 @@ bool Device::available() { // peek into next byte to check if enhanced sequence is ok ch = m_buffer[(pos+m_bufPos+1)%m_bufSize]; if (!(ch&ENH_BYTE_FLAG) || (ch&ENH_BYTE_MASK) != ENH_BYTE2) { +#ifdef DEBUG_RAW_TRAFFIC + fprintf(stdout, "raw avail enhanced following bad\n"); +#endif if (m_listener != nullptr) { m_listener->notifyStatus(true, "unexpected available enhanced following byte 1"); } @@ -367,8 +376,14 @@ bool Device::available() { pos--; continue; } +#ifdef DEBUG_RAW_TRAFFIC + fprintf(stdout, "raw avail enhanced\n"); +#endif return true; } +#ifdef DEBUG_RAW_TRAFFIC + fprintf(stdout, "raw avail enhanced bad\n"); +#endif if (m_listener != nullptr) { m_listener->notifyStatus(true, "unexpected available enhanced byte 2"); } @@ -409,6 +424,13 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati if (size <= 0) { return false; } +#ifdef DEBUG_RAW_TRAFFIC + fprintf(stdout, "raw <"); + for (int pos=0; pos Date: Sat, 11 Apr 2020 13:28:40 +0200 Subject: [PATCH 35/48] add dedicated error code for arbitration running --- src/lib/ebus/device.cpp | 2 +- src/lib/ebus/result.cpp | 1 + src/lib/ebus/result.h | 15 ++++++++------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index f581a234..319edf87 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -316,7 +316,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a result_t Device::startArbitration(symbol_t masterAddress) { if (m_arbitrationCheck) { - return RESULT_ERR_SEND; // should not occur + return RESULT_ERR_ARB_RUNNING; // should not occur } if (m_readOnly) { return RESULT_ERR_SEND; diff --git a/src/lib/ebus/result.cpp b/src/lib/ebus/result.cpp index 5616bfa8..b1c0f6fb 100755 --- a/src/lib/ebus/result.cpp +++ b/src/lib/ebus/result.cpp @@ -44,6 +44,7 @@ const char* getResultCode(result_t resultCode) { case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry"; case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name"; case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost"; + case RESULT_ERR_ARB_RUNNING: return "ERR: arbitration running"; case RESULT_ERR_CRC: return "ERR: CRC error"; case RESULT_ERR_ACK: return "ERR: ACK error"; case RESULT_ERR_NAK: return "ERR: NAK received"; diff --git a/src/lib/ebus/result.h b/src/lib/ebus/result.h index 53470901..2ab59870 100755 --- a/src/lib/ebus/result.h +++ b/src/lib/ebus/result.h @@ -56,15 +56,16 @@ enum result_t { RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name) RESULT_ERR_BUS_LOST = -18, //!< arbitration lost - RESULT_ERR_CRC = -19, //!< CRC error - RESULT_ERR_ACK = -20, //!< ACK error - RESULT_ERR_NAK = -21, //!< NAK received + RESULT_ERR_ARB_RUNNING = -19, //!< arbitration running + RESULT_ERR_CRC = -20, //!< CRC error + RESULT_ERR_ACK = -21, //!< ACK error + RESULT_ERR_NAK = -22, //!< NAK received - RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus - RESULT_ERR_SYN = -23, //!< SYN received instead of answer - RESULT_ERR_SYMBOL = -24, //!< wrong symbol received instead of sent symbol + RESULT_ERR_NO_SIGNAL = -23, //!< no signal found on the bus + RESULT_ERR_SYN = -24, //!< SYN received instead of answer + RESULT_ERR_SYMBOL = -25, //!< wrong symbol received instead of sent symbol - RESULT_ERR_NOTAUTHORIZED = -25 //!< not authorized for this action + RESULT_ERR_NOTAUTHORIZED = -26 //!< not authorized for this action }; From 6cea44947833385fdbb0539e886e40dc78c5f599 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 11 Apr 2020 15:30:20 +0200 Subject: [PATCH 36/48] allow trailing "*" as wildcard for circuit and name in /list topic --- src/ebusd/mqtthandler.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp index 78df67af..ebce4f4c 100644 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -636,9 +636,21 @@ void MqttHandler::notifyTopic(const string& topic, const string& data) { if (isList) { logOtherInfo("mqtt", "received list topic for %s %s", circuit.c_str(), name.c_str()); deque messages; - m_messages->findAll(circuit, name, m_levels, true, true, true, true, true, true, 0, 0, false, &messages); + bool circuitPrefix = circuit.length()>0 && circuit.find_last_of('*')==circuit.length()-1; + if (circuitPrefix) { + circuit = circuit.substr(0, circuit.length()-1); + } + bool namePrefix = name.length()>0 && name.find_last_of('*')==name.length()-1; + if (namePrefix) { + name = name.substr(0, name.length()-1); + } + m_messages->findAll(circuit, name, m_levels, !(circuitPrefix || namePrefix), true, true, true, true, true, 0, 0, false, &messages); bool onlyWithData = !data.empty(); for (const auto message : messages) { + if (circuitPrefix && (message->getCircuit().substr(0, circuit.length())!=circuit || !namePrefix && name.length()>0 && message->getName()!=name) + || namePrefix && (message->getName().substr(0, name.length())!=name || !circuitPrefix && circuit.length()>0 && message->getCircuit()!=circuit)) { + continue; + } time_t lastup = message->getLastUpdateTime(); if (onlyWithData && lastup == 0) { continue; From f313658f90e11f1999343d8232fa28a8c3a81a2b Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 11 Apr 2020 15:36:24 +0200 Subject: [PATCH 37/48] add global/scan topic revealing status of --scanconfig option initiated scan (fixes #304) --- src/ebusd/datahandler.h | 6 ++++++ src/ebusd/mainloop.cpp | 16 ++++++++++++++++ src/ebusd/mqtthandler.cpp | 11 ++++++++++- src/ebusd/mqtthandler.h | 8 +++++++- 4 files changed, 39 insertions(+), 2 deletions(-) mode change 100644 => 100755 src/ebusd/mqtthandler.cpp diff --git a/src/ebusd/datahandler.h b/src/ebusd/datahandler.h index 253386c6..64af17bb 100755 --- a/src/ebusd/datahandler.h +++ b/src/ebusd/datahandler.h @@ -160,6 +160,12 @@ class DataSink : virtual public DataHandler { */ virtual void notifyUpdateCheckResult(const string& checkResult) {} + /** + * Notify the sink of the latest scan status. + * @param scanStatus a string describing the scan status. + */ + virtual void notifyScanStatus(const string& scanStatus) {} + protected: /** the allowed access levels. */ string m_levels; diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index f7d55bbe..a1fca732 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -217,6 +217,7 @@ void MainLoop::run() { time_t lastTaskRun, now, start, lastSignal = 0, since, sinkSince = 1, nextCheckRun; int taskDelay = 5; symbol_t lastScanAddress = 0; // 0 is known to be a master + string lastScanStatus = "."; time(&now); start = now; lastTaskRun = now; @@ -252,12 +253,16 @@ void MainLoop::run() { } if (m_scanConfig) { bool loadDelay = false; + string scanStatus = lastScanStatus; if (m_initialScan != ESC && reload && m_busHandler->hasSignal()) { loadDelay = true; result_t result; if (m_initialScan == SYN) { logNotice(lf_main, "starting initial full scan"); result = m_busHandler->startScan(true, "*"); + if (result == RESULT_OK) { + scanStatus = "running"; + } } else if (m_initialScan == BROADCAST) { logNotice(lf_main, "starting initial broadcast scan"); Message* message = m_messages->getScanMessage(BROADCAST); @@ -280,6 +285,7 @@ void MainLoop::run() { if (m_busHandler->formatScanResult(m_initialScan, false, &ret)) { logNotice(lf_main, "initial scan result: %s", ret.str().c_str()); } + scanStatus = "running"; } } if (result != RESULT_OK) { @@ -294,7 +300,11 @@ void MainLoop::run() { if (lastScanAddress == SYN) { taskDelay = 5; lastScanAddress = 0; + scanStatus = "finished"; } else { + if (scanStatus!="running") { + scanStatus = "running"; + } nextCheckRun = now + CHECK_INITIAL_DELAY; result_t result = m_busHandler->scanAndWait(lastScanAddress, true); taskDelay = (result == RESULT_ERR_NO_SIGNAL) ? 10 : 1; @@ -305,6 +315,12 @@ void MainLoop::run() { } } } + if (scanStatus != lastScanStatus && !dataSinks.empty()) { + lastScanStatus = scanStatus; + for (const auto dataSink : dataSinks) { + dataSink->notifyScanStatus(scanStatus); + } + } } else if (reload && m_busHandler->hasSignal()) { reload = false; // execute initial instructions diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp old mode 100644 new mode 100755 index ebce4f4c..a2de6626 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -435,7 +435,7 @@ void on_message( MqttHandler::MqttHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages) : DataSink(userInfo, "mqtt"), DataSource(busHandler), WaitThread(), m_messages(messages), m_connected(false), - m_initialConnectFailed(false), m_lastUpdateCheckResult("."), m_lastErrorLogTime(0) { + m_initialConnectFailed(false), m_lastUpdateCheckResult("."), m_lastScanStatus("."), m_lastErrorLogTime(0) { m_publishByField = false; m_mosquitto = nullptr; if (g_topicFields.empty()) { @@ -711,6 +711,14 @@ void MqttHandler::notifyUpdateCheckResult(const string& checkResult) { } } +void MqttHandler::notifyScanStatus(const string& scanStatus) { + if (scanStatus != m_lastScanStatus) { + m_lastScanStatus = scanStatus; + const string sep = (g_publishFormat & OF_JSON) ? "\"" : ""; + publishTopic(m_globalTopic+"scan", sep + (scanStatus.empty() ? "OK" : scanStatus) + sep, true); + } +} + void MqttHandler::run() { time_t lastTaskRun, now, start, lastSignal = 0, lastUpdates = 0; bool signal = false; @@ -787,6 +795,7 @@ void MqttHandler::run() { } } publishTopic(signalTopic, "false", true); + publishTopic(m_globalTopic+"scan", "", true); // clear retain of scan status } bool MqttHandler::handleTraffic(bool allowReconnect) { diff --git a/src/ebusd/mqtthandler.h b/src/ebusd/mqtthandler.h index 12f70cf5..b432d30b 100644 --- a/src/ebusd/mqtthandler.h +++ b/src/ebusd/mqtthandler.h @@ -91,7 +91,10 @@ class MqttHandler : public DataSink, public DataSource, public WaitThread { // @copydoc void notifyUpdateCheckResult(const string& checkResult) override; - protected: + // @copydoc + void notifyScanStatus(const string& scanStatus) override; + +protected: // @copydoc void run() override; @@ -159,6 +162,9 @@ class MqttHandler : public DataSink, public DataSource, public WaitThread { /** the last update check result. */ string m_lastUpdateCheckResult; + /** the last scan status. */ + string m_lastScanStatus; + /** the last system time when a communication error was logged. */ time_t m_lastErrorLogTime; }; From e5191937978b4f4f3874cda55ec35e8e7b60d6a4 Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sat, 9 May 2020 09:23:14 +0200 Subject: [PATCH 38/48] compiler warning --- src/lib/ebus/device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 0037d95d..b95e0d0b 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -126,7 +126,7 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i return nullptr; // invalid protocol or missing port } result_t result = RESULT_OK; - unsigned int port = parseInt(portpos+1, 10, 1, 65535, &result); + uint16_t port = (uint16_t)parseInt(portpos+1, 10, 1, 65535, &result); if (result != RESULT_OK) { free(in); return nullptr; // invalid port From d02b2bd3317cc6cfb080ef52f4f7026150ffbff1 Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 10:25:57 +0100 Subject: [PATCH 39/48] move transfer latency to device --- src/ebusd/bushandler.cpp | 14 +++++++------- src/ebusd/bushandler.h | 7 ++----- src/ebusd/main.cpp | 6 +++++- src/ebusd/mainloop.cpp | 8 +------- src/lib/ebus/device.cpp | 23 ++++++++--------------- src/lib/ebus/device.h | 37 ++++++++++++++++++++++++------------- 6 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 5fccbc78..e34201bb 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -433,7 +433,7 @@ result_t BusHandler::handleSymbol() { // check if another symbol has to be sent and determine timeout for receive switch (m_state) { case bs_noSignal: - timeout = m_generateSynInterval > 0 ? m_generateSynInterval+m_transferLatency : SIGNAL_TIMEOUT; + timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT; break; case bs_skip: @@ -485,7 +485,7 @@ result_t BusHandler::handleSymbol() { break; case bs_recvCmdAck: - timeout = m_slaveRecvTimeout+(m_currentRequest ? m_transferLatency : 0); + timeout = m_slaveRecvTimeout; break; case bs_recvRes: @@ -498,7 +498,7 @@ result_t BusHandler::handleSymbol() { break; case bs_recvResAck: - timeout = m_slaveRecvTimeout+m_transferLatency; + timeout = m_slaveRecvTimeout; break; case bs_sendCmd: @@ -565,9 +565,9 @@ result_t BusHandler::handleSymbol() { clockGettime(&sentTime); if (result == RESULT_OK) { if (m_state == bs_ready) { - timeout = m_transferLatency+m_busAcquireTimeout; + timeout = m_busAcquireTimeout; } else { - timeout = m_transferLatency+SEND_TIMEOUT; + timeout = SEND_TIMEOUT; } } else { sending = false; @@ -579,7 +579,7 @@ result_t BusHandler::handleSymbol() { // receive next symbol (optionally check reception of sent symbol) symbol_t recvSymbol; ArbitrationState arbitrationState = as_none; - result = m_device->recv(timeout+m_transferLatency, &recvSymbol, &arbitrationState); + result = m_device->recv(timeout, &recvSymbol, &arbitrationState); if (sending) { clockGettime(&recvTime); } @@ -593,7 +593,7 @@ result_t BusHandler::handleSymbol() { } clockGettime(&sentTime); recvSymbol = ESC; - result = m_device->recv(SEND_TIMEOUT+m_transferLatency, &recvSymbol, &arbitrationState); + result = m_device->recv(SEND_TIMEOUT, &recvSymbol, &arbitrationState); clockGettime(&recvTime); if (result != RESULT_OK) { logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result)); diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index da93e88c..e9190fe8 100755 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -378,14 +378,14 @@ class BusHandler : public WaitThread { BusHandler(Device* device, MessageMap* messages, symbol_t ownAddress, bool answer, unsigned int busLostRetries, unsigned int failedSendRetries, - unsigned int transferLatency, unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout, + unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout, unsigned int lockCount, bool generateSyn, unsigned int pollInterval) : WaitThread(), m_device(device), m_reconnect(false), m_messages(messages), m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)), m_answer(answer), m_addressConflict(false), m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), - m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), + m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0), m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount ? 1 : 0), m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0), @@ -686,9 +686,6 @@ class BusHandler : public WaitThread { /** the number of times a failed send is repeated (other than lost arbitration). */ const unsigned int m_failedSendRetries; - /** the bus transfer latency in microseconds. */ - const unsigned int m_transferLatency; - /** the maximum time in microseconds for bus acquisition. */ const unsigned int m_busAcquireTimeout; diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index a1924f35..4f07eec6 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -1306,7 +1306,11 @@ int main(int argc, char* argv[]) { } // open the device - Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); + unsigned int latency = 0; + if (opt.latency >= 0) { + latency = (unsigned int)opt.latency; + } + Device *device = Device::create(opt.device, latency, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); if (device == nullptr) { logError(lf_main, "unable to create device %s", opt.device); return EINVAL; diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index a1fca732..581894fc 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -141,16 +141,10 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag } } // create BusHandler - unsigned int latency; - if (opt.latency < 0) { - latency = device->getLatency(); - } else { - latency = (unsigned int)opt.latency; - } m_busHandler = new BusHandler(m_device, m_messages, m_address, opt.answer, opt.acquireRetries, opt.sendRetries, - latency, opt.acquireTimeout, opt.receiveTimeout, + opt.acquireTimeout, opt.receiveTimeout, opt.masterCount, opt.generateSyn, opt.pollInterval); m_busHandler->start("bushandler"); diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index b95e0d0b..a8d471dd 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -73,17 +73,9 @@ 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))} -/** - * Construct a new instance. - * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - * @param address the socket address of the device. - * @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 udp true for UDP, false to TCP. - * @param enhancedProto whether to use the ebusd enhanced protocol. - */ -Device::Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto) - : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), +Device::Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, + bool enhancedProto) + : m_name(name), m_checkDevice(checkDevice), m_latency(latency), m_readOnly(readOnly), m_initialSend(initialSend), m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN), m_arbitrationCheck(false), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(0) { m_buffer = reinterpret_cast(malloc(m_bufSize)); @@ -99,7 +91,7 @@ Device::~Device() { } } -Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool initialSend) { +Device* Device::create(const char* name, unsigned int extraLatency, bool checkDevice, bool readOnly, bool initialSend) { bool enhanced = strncmp(name, "enh:", 4) == 0; if (enhanced) { name += 4; @@ -134,10 +126,10 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i *portpos = 0; char* hostOrIp = strdup(addrpos); free(in); - return new NetworkDevice(name, hostOrIp, port, readOnly, initialSend, udp, enhanced); + return new NetworkDevice(name, hostOrIp, port, extraLatency, readOnly, initialSend, udp, enhanced); } // support enh:/dev/ - return new SerialDevice(name, checkDevice, readOnly, initialSend, enhanced); + return new SerialDevice(name, checkDevice, extraLatency, readOnly, initialSend, enhanced); } result_t Device::open() { @@ -205,6 +197,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a } bool repeat = false; bool repeated = false; + timeout += m_latency; do { repeat = false; bool isAvailable = available(); @@ -263,7 +256,7 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a // for a two-byte transfer another poll is needed repeat = true; repeated = true; - timeout = ENHANCED_COMPLETE_WAIT_DURATION; + timeout = m_latency+ENHANCED_COMPLETE_WAIT_DURATION; continue; } return RESULT_ERR_TIMEOUT; diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index 84772a90..b995afa4 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -40,6 +40,9 @@ namespace ebusd { * to a file and/or forwarding it to a logging function. */ +/** the transfer latency of the network device [ms]. */ +#define NETWORK_LATENCY_MS 10 + /** the arbitration state handled by @a Device. */ enum ArbitrationState { as_none, //!< no arbitration in process @@ -80,17 +83,20 @@ class DeviceListener { * The base class for accessing an eBUS. */ class Device { - public: + 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. */ - Device(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false); + Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, + bool enhancedProto=false); + public: /** * Destructor. */ @@ -99,20 +105,21 @@ class Device { /** * Factory method for creating a new instance. * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param extraLatency the extra bus transfer latency in milliseconds. * @param checkDevice whether to regularly check the device availability (only for serial devices). * @param readOnly whether to allow read access to the device only. * @param initialSend whether to send an initial @a ESC symbol in @a open(). * @return the new @a Device, or nullptr on error. * Note: the caller needs to free the created instance. */ - static Device* create(const char* name, bool checkDevice = true, bool readOnly = false, - bool initialSend = false); + static Device* create(const char* name, unsigned int extraLatency = 0, bool checkDevice = true, + bool readOnly = false, bool initialSend = false); /** * Get the transfer latency of this device. * @return the transfer latency in microseconds. */ - virtual unsigned int getLatency() const { return 0; } + virtual unsigned int getLatency() const { return m_latency; } /** * Open the file descriptor. @@ -223,6 +230,9 @@ class Device { /** 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; @@ -269,12 +279,14 @@ class SerialDevice : public 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 extraLatency the extra 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. */ - SerialDevice(const char* name, bool checkDevice, bool readOnly, bool initialSend, bool enhancedProto=false) - : Device(name, checkDevice, readOnly, initialSend, enhancedProto) {} + SerialDevice(const char* name, bool checkDevice, unsigned int extraLatency, bool readOnly, bool initialSend, + bool enhancedProto=false) + : Device(name, checkDevice, extraLatency, readOnly, initialSend, enhancedProto) {} // @copydoc result_t open() override; @@ -304,14 +316,16 @@ class NetworkDevice : public Device { * @param address the socket address of the device. * @param hostOrIp the host name or IP address of the device. * @param port the TCP or UDP port of the device. + * @param extraLatency the extra 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 udp true for UDP, false to TCP. * @param enhancedProto whether to use the ebusd enhanced protocol. */ - NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, bool readOnly, bool initialSend, - bool udp, bool enhancedProto=false) - : Device(name, true, readOnly, initialSend, enhancedProto), m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {} + 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), + m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {} /** * Destructor. @@ -322,9 +336,6 @@ class NetworkDevice : public Device { } } - // @copydoc - unsigned int getLatency() const override { return 10000; } - // @copydoc result_t open() override; From 7342fd09849180ddd6719f9dfb5c104d6d00f867 Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 10:43:36 +0100 Subject: [PATCH 40/48] switch to milliseconds instead of micros for better readability --- contrib/docker/README.md | 2 +- src/ebusd/bushandler.h | 32 +++++++++++++++++--------------- src/ebusd/main.cpp | 38 +++++++++++++++++++------------------- src/ebusd/main.h | 6 +++--- src/lib/ebus/device.cpp | 10 +++++----- src/lib/ebus/device.h | 4 ++-- test_coverage.sh | 2 +- 7 files changed, 48 insertions(+), 46 deletions(-) diff --git a/contrib/docker/README.md b/contrib/docker/README.md index 2d3bf5b7..75cdf5e6 100644 --- a/contrib/docker/README.md +++ b/contrib/docker/README.md @@ -41,7 +41,7 @@ Using a network device ---------------------- When using a network device, the "--device" argument to docker can be omitted, but the device information has to be passed on to ebusd: -> docker run --rm -it -p 8888 john30/ebusd -f --scanconfig -d udp:192.168.178.123:10000 --latency=80000 +> docker run --rm -it -p 8888 john30/ebusd -f --scanconfig -d udp:192.168.178.123:10000 --latency=80 Note: the "-f" and "--scanconfig" arguments are only passed to ebusd if it is called without any additional arguments. So when passing further arguments, these two usually need to be added as well. diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index e9190fe8..515ea266 100755 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -45,20 +45,23 @@ namespace ebusd { using std::string; -/** the default time [us] for retrieving a symbol from an addressed slave. */ -#define SLAVE_RECV_TIMEOUT 15000 +/** the default time [ms] for retrieving a symbol from an addressed slave. */ +#define SLAVE_RECV_TIMEOUT 15 -/** the maximum allowed time [us] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */ -#define SYN_TIMEOUT 50800 +/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */ +#define SYN_TIMEOUT 51 -/** the time [us] for determining bus signal availability (AUTO-SYN timeout * 5). */ -#define SIGNAL_TIMEOUT 250000 +/** the time [ms] for determining bus signal availability (AUTO-SYN timeout * 5). */ +#define SIGNAL_TIMEOUT 250 /** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */ -#define SYMBOL_DURATION 4700 +#define SYMBOL_DURATION_MICROS 4700 -/** the maximum allowed time [us] for retrieving back a sent symbol (2x symbol duration). */ -#define SEND_TIMEOUT (2*SYMBOL_DURATION) +/** the maximum duration [ms] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */ +#define SYMBOL_DURATION 5 + +/** the maximum allowed time [ms] for retrieving back a sent symbol (2x symbol duration). */ +#define SEND_TIMEOUT ((int)((2*SYMBOL_DURATION_MICROS+999)/1000)) /** the possible bus states. */ enum BusState { @@ -368,9 +371,8 @@ class BusHandler : public WaitThread { * @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 transferLatency the bus transfer latency in microseconds. - * @param busAcquireTimeout the maximum time in microseconds for bus acquisition. - * @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge. + * @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. @@ -686,10 +688,10 @@ class BusHandler : public WaitThread { /** the number of times a failed send is repeated (other than lost arbitration). */ const unsigned int m_failedSendRetries; - /** the maximum time in microseconds for bus acquisition. */ + /** the maximum time in milliseconds for bus acquisition. */ const unsigned int m_busAcquireTimeout; - /** the maximum time in microseconds an addressed slave is expected to acknowledge. */ + /** the maximum time in milliseconds an addressed slave is expected to acknowledge. */ const unsigned int m_slaveRecvTimeout; /** the number of masters already seen. */ @@ -704,7 +706,7 @@ class BusHandler : public WaitThread { /** the remaining number of AUTO-SYN symbols before sending is allowed again. */ unsigned int m_remainLockCount; - /** the interval in microseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */ + /** the interval in milliseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */ unsigned int m_generateSynInterval; /** the interval in seconds in which poll messages are cycled, or 0 if disabled. */ diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 4f07eec6..ce568dbf 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -79,7 +79,7 @@ static struct options opt = { false, // noDeviceCheck false, // readOnly false, // initialSend - -1, // latency + 0, // extraLatency CONFIG_PATH, // configPath false, // scanConfig @@ -92,7 +92,7 @@ static struct options opt = { 0x31, // address false, // answer - 9400, // acquireTimeout + 10, // acquireTimeout 3, // acquireRetries 2, // sendRetries SLAVE_RECV_TIMEOUT*5/3, // receiveTimeout @@ -184,7 +184,7 @@ static const struct argp_option argpoptions[] = { {"nodevicecheck", 'n', nullptr, 0, "Skip serial eBUS device test", 0 }, {"readonly", 'r', nullptr, 0, "Only read from device, never write to it", 0 }, {"initsend", O_INISND, nullptr, 0, "Send an initial escape symbol after connecting device", 0 }, - {"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 }, + {"latency", O_DEVLAT, "MSEC", 0, "Extra transfer latency in ms [0]", 0 }, {nullptr, 0, nullptr, 0, "Message configuration options:", 2 }, {"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTP URL) [" CONFIG_PATH @@ -204,10 +204,10 @@ static const struct argp_option argpoptions[] = { {nullptr, 0, nullptr, 0, "eBUS options:", 3 }, {"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 }, {"answer", O_ANSWER, nullptr, 0, "Actively answer to requests from other masters", 0 }, - {"acquiretimeout", O_ACQTIM, "USEC", 0, "Stop bus acquisition after USEC us [9400]", 0 }, + {"acquiretimeout", O_ACQTIM, "MSEC", 0, "Stop bus acquisition after MSEC ms [10]", 0 }, {"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 }, {"sendretries", O_SNDRET, "COUNT", 0, "Repeat failed sends COUNT times [2]", 0 }, - {"receivetimeout", O_RCVTIM, "USEC", 0, "Expect a slave to answer within USEC us [25000]", 0 }, + {"receivetimeout", O_RCVTIM, "MSEC", 0, "Expect a slave to answer within MSEC us [25]", 0 }, {"numbermasters", O_MASCNT, "COUNT", 0, "Expect COUNT masters on the bus, 0 for auto detection [0]", 0 }, {"generatesyn", O_GENSYN, nullptr, 0, "Enable AUTO-SYN symbol generation", 0 }, @@ -267,6 +267,7 @@ static map s_templatesByPath; error_t parse_opt(int key, char *arg, struct argp_state *state) { struct options *opt = (struct options*)state->input; result_t result = RESULT_OK; + unsigned int value; switch (key) { // Device options: @@ -295,12 +296,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) { } opt->initialSend = true; break; - case O_DEVLAT: // --latency=10000 - opt->latency = parseInt(arg, 10, 0, 200000, &result); - if (result != RESULT_OK) { + case O_DEVLAT: // --latency=10 + value = parseInt(arg, 10, 0, 200000, &result); // backwards compatible (micros) + if (result != RESULT_OK || (value<=1000 && value>200)) { // backwards compatible (micros) argp_error(state, "invalid latency"); return EINVAL; } + opt->extraLatency = value > 1000 ? value/1000 : value; // backwards compatible (micros) break; // Message configuration options: @@ -376,12 +378,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) { } opt->answer = true; break; - case O_ACQTIM: // --acquiretimeout=9400 - opt->acquireTimeout = parseInt(arg, 10, 1000, 100000, &result); - if (result != RESULT_OK) { + case O_ACQTIM: // --acquiretimeout=10 + value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros) + if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros) argp_error(state, "invalid acquiretimeout"); return EINVAL; } + opt->acquireTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros) break; case O_ACQRET: // --acquireretries=3 opt->acquireRetries = parseInt(arg, 10, 0, 10, &result); @@ -397,12 +400,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) { return EINVAL; } break; - case O_RCVTIM: // --receivetimeout=25000 - opt->receiveTimeout = parseInt(arg, 10, 1000, 100000, &result); - if (result != RESULT_OK) { + case O_RCVTIM: // --receivetimeout=25 + value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros) + if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros) argp_error(state, "invalid receivetimeout"); return EINVAL; } + opt->receiveTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros) break; case O_MASCNT: // --numbermasters=0 opt->masterCount = parseInt(arg, 10, 0, 25, &result); @@ -1306,11 +1310,7 @@ int main(int argc, char* argv[]) { } // open the device - unsigned int latency = 0; - if (opt.latency >= 0) { - latency = (unsigned int)opt.latency; - } - Device *device = Device::create(opt.device, latency, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); + Device *device = Device::create(opt.device, opt.extraLatency, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); if (device == nullptr) { logError(lf_main, "unable to create device %s", opt.device); return EINVAL; diff --git a/src/ebusd/main.h b/src/ebusd/main.h index e2721365..ba63ce95 100644 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -39,7 +39,7 @@ struct options { bool noDeviceCheck; //!< skip serial eBUS device test bool readOnly; //!< read-only access to the device bool initialSend; //!< send an initial escape symbol after connecting device - int latency; //!< transfer latency in us [0 for USB, 10000 for IP] + unsigned int extraLatency; //!< extra transfer latency in ms [0 for USB, 10 for IP] const char* configPath; //!< path to CSV configuration files [http://ebusd.eu/config/] bool scanConfig; //!< pick configuration files matching initial scan @@ -54,10 +54,10 @@ struct options { symbol_t address; //!< own bus address [31] bool answer; //!< answer to requests from other masters - unsigned int acquireTimeout; //!< bus acquisition timeout in us [9400] + unsigned int acquireTimeout; //!< bus acquisition timeout in ms [10] unsigned int acquireRetries; //!< number of retries for bus acquisition [3] unsigned int sendRetries; //!< number of retries for failed sends [2] - unsigned int receiveTimeout; //!< timeout for receiving answer from slave in us [25000] + unsigned int receiveTimeout; //!< timeout for receiving answer from slave in ms [25] unsigned int masterCount; //!< expected number of masters for arbitration [0] bool generateSyn; //!< enable AUTO-SYN symbol generation diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index a8d471dd..af3805e1 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -185,10 +185,10 @@ result_t Device::send(symbol_t value) { } /** - * the maximum duration to wait for an enhanced sequence to complete after the first part was already retrieved: - * 2* (Start+8Bit+Stop+Extra @ 9600Bd) + * the maximum duration in milliseconds to wait for an enhanced sequence to complete after the first part was already + * retrieved: 2* (Start+8Bit+Stop+Extra @ 9600Bd) */ -#define ENHANCED_COMPLETE_WAIT_DURATION (2*1150) +#define ENHANCED_COMPLETE_WAIT_DURATION 3 result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { @@ -206,8 +206,8 @@ result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* a struct timespec tdiff; // set select timeout - tdiff.tv_sec = timeout/1000000; - tdiff.tv_nsec = (timeout%1000000)*1000; + tdiff.tv_sec = timeout/1000; + tdiff.tv_nsec = (timeout%1000)*1000000; #ifdef HAVE_PPOLL nfds_t nfds = 1; diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index b995afa4..f2248571 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -117,7 +117,7 @@ class Device { /** * Get the transfer latency of this device. - * @return the transfer latency in microseconds. + * @return the transfer latency in milliseconds. */ virtual unsigned int getLatency() const { return m_latency; } @@ -147,7 +147,7 @@ class Device { /** * Read a single byte from the device. - * @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite. + * @param timeout maximum time to wait for the byte in milliseconds, or 0 for infinite. * @param value the reference in which the received byte value is stored. * @param arbitrationState the reference in which the current @a ArbitrationState is stored on success. When set to * @a as_won, the received byte is the master address that was successfully arbitrated with. diff --git a/test_coverage.sh b/test_coverage.sh index f5650060..74a13521 100755 --- a/test_coverage.sh +++ b/test_coverage.sh @@ -207,7 +207,7 @@ r,,SoftwareVersion,,,,,"0000",,,HEX:4,,, EOF echo "test,testpass,installer" > ./passwd #ebusd: -./src/ebusd/ebusd -d tcp:127.0.0.1:8876 --initsend --latency 10000 -n -c "$PWD/contrib/etc/ebusd" --pollinterval=10 -s -a 31 --acquireretries 3 --answer --generatesyn --receivetimeout 40000 --sendretries 1 --enablehex --htmlpath "$PWD/contrib/html" --httpport 8878 --pidfile "$PWD/ebusd.pid" --localhost -p 8877 -l "$PWD/ebusd.log" --logareas all --loglevel debug --lograwdata=bytes --lograwdatafile "$PWD/ebusd.raw" --lograwdatasize 1 --dumpfile "$PWD/ebusd.dump" --dumpsize 100 -D --scanconfig --aclfile=./passwd --mqttport=1883 +./src/ebusd/ebusd -d tcp:127.0.0.1:8876 --initsend --latency 10 -n -c "$PWD/contrib/etc/ebusd" --pollinterval=10 -s -a 31 --acquireretries 3 --answer --generatesyn --receivetimeout 40000 --sendretries 1 --enablehex --htmlpath "$PWD/contrib/html" --httpport 8878 --pidfile "$PWD/ebusd.pid" --localhost -p 8877 -l "$PWD/ebusd.log" --logareas all --loglevel debug --lograwdata=bytes --lograwdatafile "$PWD/ebusd.raw" --lograwdatasize 1 --dumpfile "$PWD/ebusd.dump" --dumpsize 100 -D --scanconfig --aclfile=./passwd --mqttport=1883 sleep 3 pid=`head -n 1 "$PWD/ebusd.pid"` if [ -z "$pid" ]; then From 91745fe847228fad1c972316d9b0298bee939634 Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 10:44:15 +0100 Subject: [PATCH 41/48] add host latency --- src/lib/ebus/device.cpp | 3 ++- src/lib/ebus/device.h | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index af3805e1..df1478e6 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -75,7 +75,8 @@ namespace ebusd { Device::Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, bool enhancedProto) - : m_name(name), m_checkDevice(checkDevice), m_latency(latency), m_readOnly(readOnly), m_initialSend(initialSend), + : m_name(name), m_checkDevice(checkDevice), + m_latency(HOST_LATENCY_MS+latency), m_readOnly(readOnly), m_initialSend(initialSend), m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN), m_arbitrationCheck(false), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(0) { m_buffer = reinterpret_cast(malloc(m_bufSize)); diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index f2248571..213f6780 100755 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -43,6 +43,13 @@ namespace ebusd { /** the transfer latency of the network device [ms]. */ #define NETWORK_LATENCY_MS 10 +/** the latency of the host [ms]. */ +#ifdef __CYGWIN__ +#define HOST_LATENCY_MS 20 +#else +#define HOST_LATENCY_MS 0 +#endif + /** the arbitration state handled by @a Device. */ enum ArbitrationState { as_none, //!< no arbitration in process From dc8de61042dc0dd6717647f8755af24c68a4439f Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 11:08:31 +0100 Subject: [PATCH 42/48] fix logging sent byte when arbitration was lost --- src/lib/ebus/device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index df1478e6..8b7fc79f 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -473,7 +473,7 @@ bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrati case ENH_RES_FAILED: *arbitrationState = as_lost; if (m_listener != NULL) { - m_listener->notifyDeviceData(data, false); + m_listener->notifyDeviceData(m_arbitrationMaster, false); } m_arbitrationMaster = SYN; m_arbitrationCheck = false; From 1b0752f2bf174244e28b3805491eed6243cf8dae Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 13:18:52 +0100 Subject: [PATCH 43/48] flush raw log after maximum allowed master length --- src/ebusd/mainloop.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 581894fc..c862fb9b 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -472,7 +472,10 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) { m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast(symbol); m_logRawLastSymbol = symbol; } - if (symbol == SYN && m_logRawBuffer.tellp() > 0) { // flush + if (m_logRawBuffer.tellp() > (symbol == SYN ? 0 : 61)) { // flush: (direction+5 hdr+24 max data+crc)*2 + if (symbol != SYN) { + m_logRawBuffer << "..."; + } const string bufStr = m_logRawBuffer.str(); const char* str = bufStr.c_str(); if (m_logRawFile) { @@ -481,6 +484,9 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) { logNotice(lf_bus, str); } m_logRawBuffer.str(""); + if (symbol != SYN) { + m_logRawBuffer << "..."; + } } } From df510b84758214ce2aa6e0b89bcbe1491a1c656c Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 13:32:14 +0100 Subject: [PATCH 44/48] better raw log flushing after maximum allowed master length --- src/ebusd/mainloop.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index c862fb9b..53449956 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -467,12 +467,15 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) { } 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 : 61)) { // flush: (direction+5 hdr+24 max data+crc)*2 + 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 << "..."; } @@ -484,9 +487,6 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) { logNotice(lf_bus, str); } m_logRawBuffer.str(""); - if (symbol != SYN) { - m_logRawBuffer << "..."; - } } } From 512b9d94567d2ceb8624920c8142af0fc180fa5b Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 15:35:39 +0100 Subject: [PATCH 45/48] set entry arbitration state for state machine to work properly --- src/lib/ebus/device.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 8b7fc79f..8f941858 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -193,6 +193,9 @@ result_t Device::send(symbol_t value) { result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) { + if (m_arbitrationMaster!=SYN) { + *arbitrationState = as_running; + } if (!isValid()) { return RESULT_ERR_DEVICE; } From 0092320c0facc5d400f3915fb8e55fcde1c67c5f Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 16:09:58 +0100 Subject: [PATCH 46/48] measure arbitration delay in enhanced proto as well --- src/ebusd/bushandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index e34201bb..8e130b3a 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -574,6 +574,8 @@ result_t BusHandler::handleSymbol() { timeout = SYN_TIMEOUT; setState(bs_skip, result); } + } else { + clockGettime(&sentTime); // for measuring arbitration delay in enhanced protocol } // receive next symbol (optionally check reception of sent symbol) From db8fc965dfa83c8a270e1be34ccb28393ac24f63 Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 16:10:48 +0100 Subject: [PATCH 47/48] cancel running request on arbitration error --- src/ebusd/bushandler.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 8e130b3a..6f885b65 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -647,7 +647,17 @@ result_t BusHandler::handleSymbol() { case as_running: break; case as_error: - logError(lf_bus, "arbitration start error"); // TODO cancel all requests? + logError(lf_bus, "arbitration start error"); + // cancel request + if (!m_currentRequest) { + BusRequest *startRequest = m_nextRequests.peek(); + if (startRequest && m_nextRequests.remove(startRequest)) { + m_currentRequest = startRequest; + } + } + if (m_currentRequest) { + setState(m_state, RESULT_ERR_BUS_LOST); + } break; default: // only as_none break; From 7b4e2eb2618555529bd81b78f56cf936378b29af Mon Sep 17 00:00:00 2001 From: John-Michael Baier Date: Sun, 1 Nov 2020 16:11:17 +0100 Subject: [PATCH 48/48] log request start --- src/ebusd/bushandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 6f885b65..87e031c5 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -460,6 +460,7 @@ result_t BusHandler::handleSymbol() { } } if (startRequest != nullptr) { // initiate arbitration + logDebug(lf_bus, "start request %2.2x", startRequest->m_master[0]); result_t ret = m_device->startArbitration(startRequest->m_master[0]); if (ret == RESULT_OK) { logDebug(lf_bus, "arbitration start with %2.2x", startRequest->m_master[0]);