From 730d92d06cbab848586e626609d3f0e66511a8fe Mon Sep 17 00:00:00 2001 From: John Date: Fri, 13 Oct 2023 08:08:58 +0200 Subject: [PATCH] abstract bus protocol handling --- src/ebusd/bushandler.cpp | 906 ++--------------------------- src/ebusd/bushandler.h | 409 +------------ src/ebusd/knxhandler.cpp | 4 +- src/ebusd/main.cpp | 2 +- src/ebusd/mainloop.cpp | 90 +-- src/ebusd/mainloop.h | 3 + src/ebusd/mqtthandler.cpp | 2 +- src/lib/ebus/CMakeLists.txt | 2 + src/lib/ebus/Makefile.am | 2 + src/lib/ebus/protocol.cpp | 140 +++++ src/lib/ebus/protocol.h | 477 +++++++++++++++ src/lib/ebus/protocol_direct.cpp | 762 ++++++++++++++++++++++++ src/lib/ebus/protocol_direct.h | 180 ++++++ src/{ebusd => lib/ebus}/states.png | Bin 14 files changed, 1695 insertions(+), 1284 deletions(-) create mode 100644 src/lib/ebus/protocol.cpp create mode 100755 src/lib/ebus/protocol.h create mode 100644 src/lib/ebus/protocol_direct.cpp create mode 100755 src/lib/ebus/protocol_direct.h rename src/{ebusd => lib/ebus}/states.png (100%) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index f880dc75..31a0ebbe 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -35,32 +35,6 @@ using std::endl; // the string used for answering to a scan request (07h 04h) #define SCAN_ANSWER ("ebusd.eu;" PACKAGE_NAME ";" SCAN_VERSION ";100") -/** - * Return the string corresponding to the @a BusState. - * @param state the @a BusState. - * @return the string corresponding to the @a BusState. - */ -const char* getStateCode(BusState state) { - switch (state) { - case bs_noSignal: return "no signal"; - case bs_skip: return "skip"; - case bs_ready: return "ready"; - case bs_sendCmd: return "send command"; - case bs_recvCmdCrc: return "receive command CRC"; - case bs_recvCmdAck: return "receive command ACK"; - case bs_recvRes: return "receive response"; - case bs_recvResCrc: return "receive response CRC"; - case bs_sendResAck: return "send response ACK"; - case bs_recvCmd: return "receive command"; - case bs_recvResAck: return "receive response ACK"; - case bs_sendCmdCrc: return "send command CRC"; - case bs_sendCmdAck: return "send command ACK"; - case bs_sendRes: return "send response"; - case bs_sendResCrc: return "send response CRC"; - case bs_sendSyn: return "send SYN"; - default: return "unknown"; - } -} result_t PollRequest::prepare(symbol_t ownMasterAddress) { istringstream input; @@ -181,17 +155,6 @@ bool ScanRequest::notify(result_t result, const SlaveSymbolString& slave) { } -bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) { - if (result == RESULT_OK) { - string str = slave.getStr(); - logDebug(lf_bus, "read res: %s", str.c_str()); - } - m_result = result; - *m_slave = slave; - return false; -} - - void GrabbedMessage::setLastData(const MasterSymbolString& master, const SlaveSymbolString& slave) { time(&m_lastTime); m_lastMaster = master; @@ -355,40 +318,13 @@ bool GrabbedMessage::dump(bool unknown, MessageMap* messages, bool first, Output void BusHandler::clear() { - memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); - m_masterCount = 1; + m_protocol->clear(); m_scanResults.clear(); } -result_t BusHandler::sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave) { - if (m_state == bs_noSignal) { - return RESULT_ERR_NO_SIGNAL; // don't wait when there is no signal - } - result_t result = RESULT_ERR_NO_SIGNAL; - slave->clear(); - ActiveBusRequest request(master, slave); - logInfo(lf_bus, "send message: %s", master.getStr().c_str()); - - for (int sendRetries = m_failedSendRetries + 1; sendRetries > 0; sendRetries--) { - m_nextRequests.push(&request); - bool success = m_finishedRequests.remove(&request, true); - result = success ? request.m_result : RESULT_ERR_TIMEOUT; - if (result == RESULT_OK) { - break; - } - if (!success || result == RESULT_ERR_NO_SIGNAL || result == RESULT_ERR_SEND || result == RESULT_ERR_DEVICE) { - logError(lf_bus, "send to %2.2x: %s, give up", master[1], getResultCode(result)); - break; - } - logError(lf_bus, "send to %2.2x: %s%s", master[1], getResultCode(result), sendRetries > 1 ? ", retry" : ""); - request.m_busLostRetries = 0; - } - return result; -} - result_t BusHandler::readFromBus(Message* message, const string& inputStr, symbol_t dstAddress, symbol_t srcAddress) { - symbol_t masterAddress = srcAddress == SYN ? m_ownMasterAddress : srcAddress; + symbol_t masterAddress = srcAddress == SYN ? m_protocol->getOwnMasterAddress() : srcAddress; result_t ret = RESULT_EMPTY; MasterSymbolString master; SlaveSymbolString slave; @@ -400,7 +336,7 @@ result_t BusHandler::readFromBus(Message* message, const string& inputStr, symbo break; } // send message - ret = sendAndWait(master, &slave); + ret = m_protocol->sendAndWait(master, &slave); if (ret != RESULT_OK) { logError(lf_bus, "send message part %d: %s", index, getResultCode(ret)); break; @@ -414,795 +350,50 @@ result_t BusHandler::readFromBus(Message* message, const string& inputStr, symbo return ret; } -void BusHandler::run() { - unsigned int symCount = 0; - time_t now, lastTime; - time(&lastTime); - lastTime += 2; - logNotice(lf_bus, "bus started with own address %2.2x/%2.2x%s", m_ownMasterAddress, m_ownSlaveAddress, - m_answer?" in answer mode":""); - - do { - if (m_device->isValid() && !m_reconnect) { - result_t result = handleSymbol(); - time(&now); - if (result != RESULT_ERR_TIMEOUT && now >= lastTime) { - symCount++; - } - if (now > lastTime) { - m_symPerSec = symCount / (unsigned int)(now-lastTime); - if (m_symPerSec > m_maxSymPerSec) { - m_maxSymPerSec = m_symPerSec; - if (m_maxSymPerSec > 100) { - logNotice(lf_bus, "max. symbols per second: %d", m_maxSymPerSec); - } - } - lastTime = now; - symCount = 0; - } - } else { - if (!m_device->isValid()) { - logNotice(lf_bus, "device invalid"); - setState(bs_noSignal, RESULT_ERR_DEVICE); - } - if (!Wait(5)) { - break; - } - m_reconnect = false; - result_t result = m_device->open(); - if (result == RESULT_OK) { - logNotice(lf_bus, "re-opened %s", m_device->getName()); - } else { - logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); - setState(bs_noSignal, result); - } - symCount = 0; - m_symbolLatencyMin = m_symbolLatencyMax = m_arbitrationDelayMin = m_arbitrationDelayMax = -1; - time(&lastTime); - lastTime += 2; - } - } while (isRunning()); +void BusHandler::notifyProtocolStatus(bool signal) { + // ignored } -#ifndef FALLTHROUGH -#if defined(__GNUC__) && __GNUC__ >= 7 -#define FALLTHROUGH [[fallthrough]]; -#else -#define FALLTHROUGH -#endif -#endif - -result_t BusHandler::handleSymbol() { - unsigned int timeout = SYN_TIMEOUT; - symbol_t sendSymbol = ESC; - bool sending = false; - - // 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 : SIGNAL_TIMEOUT; - break; - - case bs_skip: - timeout = SYN_TIMEOUT; - FALLTHROUGH - case bs_ready: - if (m_currentRequest != nullptr) { - setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up +result_t BusHandler::notifyProtocolAnswer(const MasterSymbolString& command, SlaveSymbolString* response) { + Message* message = m_messages->find(command); + if (message == nullptr) { + message = m_messages->find(command, true); + if (message != nullptr && message->getSrcAddress() != SYN) { + message = nullptr; } - 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); - if (m_lastPoll == 0 || difftime(now, m_lastPoll) > m_pollInterval) { - Message* message = m_messages->getNextPoll(); - if (message != nullptr) { - m_lastPoll = now; - if (difftime(now, message->getLastUpdateTime()) > m_pollInterval) { - // only poll this message if it was not updated already by other means within the interval - 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)); - delete request; - } else { - startRequest = request; - m_nextRequests.push(request); - } - } - } - } - } - 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]); - } 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 - } - } - } - break; - - case bs_recvCmd: - case bs_recvCmdCrc: - timeout = m_slaveRecvTimeout; - break; - - case bs_recvCmdAck: - timeout = m_slaveRecvTimeout; - break; - - case bs_recvRes: - case bs_recvResCrc: - if (m_response.size() > 0 || m_slaveRecvTimeout > SYN_TIMEOUT) { - timeout = m_slaveRecvTimeout; - } else { - timeout = SYN_TIMEOUT; - } - break; - - case bs_recvResAck: - timeout = m_slaveRecvTimeout; - break; - - case bs_sendCmd: - if (m_currentRequest != nullptr) { - sendSymbol = m_currentRequest->m_master[m_nextSendPos]; // unescaped command - sending = true; - } - break; - - case bs_sendCmdCrc: - if (m_currentRequest != nullptr) { - sendSymbol = m_crc; - sending = true; - } - break; - - case bs_sendResAck: - if (m_currentRequest != nullptr) { - sendSymbol = m_crcValid ? ACK : NAK; - sending = true; - } - break; - - case bs_sendCmdAck: - if (m_answer) { - sendSymbol = m_crcValid ? ACK : NAK; - sending = true; - } - break; - - case bs_sendRes: - if (m_answer) { - sendSymbol = m_response[m_nextSendPos]; // unescaped response - sending = true; - } - break; - - case bs_sendResCrc: - if (m_answer) { - sendSymbol = m_crc; - sending = true; - } - break; - - case bs_sendSyn: - sendSymbol = SYN; - sending = true; - break; } - - // send symbol if necessary - result_t result; - struct timespec sentTime, recvTime; - if (sending) { - if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) { - if (m_escape) { - sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01); - } else { - m_escape = sendSymbol; - sendSymbol = ESC; - } - } - result = m_device->send(sendSymbol); - clockGettime(&sentTime); - if (result == RESULT_OK) { - if (m_state == bs_ready) { - timeout = m_busAcquireTimeout; - } else { - timeout = SEND_TIMEOUT; - } - } else { - sending = false; - timeout = SYN_TIMEOUT; - setState(bs_skip, result); - } - } else { - clockGettime(&sentTime); // for measuring arbitration delay in enhanced protocol + if (message == nullptr || message->isWrite()) { + // don't know this request or definition has wrong direction, deny + return RESULT_ERR_INVALID_ARG; } - - // receive next symbol (optionally check reception of sent symbol) - symbol_t recvSymbol; - ArbitrationState arbitrationState = as_none; - result = m_device->recv(timeout, &recvSymbol, &arbitrationState); - if (sending) { - clockGettime(&recvTime); + istringstream input; // TODO create input from database of internal variables + if (message == m_messages->getScanMessage() + || message == m_messages->getScanMessage(m_protocol->getOwnMasterAddress())) { + input.str(SCAN_ANSWER); } - bool sentAutoSyn = false; - if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 - && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { - // check if acting as AUTO-SYN generator is required - result = m_device->send(SYN); - if (result != RESULT_OK) { - return setState(bs_skip, result); - } - clockGettime(&sentTime); - recvSymbol = ESC; - 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)); - 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; - setState(bs_ready, RESULT_OK); - } - switch (arbitrationState) { - case as_lost: - case as_timeout: - logDebug(lf_bus, arbitrationState == as_lost ? "arbitration lost" : "arbitration lost (timed out)"); - 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); - 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; - } - } - break; - case as_running: - break; - case as_error: - 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; - } - if (sentAutoSyn && !sending) { - return RESULT_OK; - } - time_t now; - time(&now); - if (result != RESULT_OK) { - 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) { - return setState(bs_noSignal, result); - } - return setState(bs_skip, result); - } - - m_lastReceive = now; - if ((recvSymbol == SYN) && (m_state != bs_sendSyn)) { - if (!sending && m_remainLockCount > 0 && m_command.size() != 1) { - m_remainLockCount--; - } else if (!sending && m_remainLockCount == 0 && m_command.size() == 1) { - m_remainLockCount = 1; // wait for next AUTO-SYN after SYN / address / SYN (bus locked for own priority) - } - clockGettime(&m_lastSynReceiveTime); - return setState(bs_ready, m_state == bs_skip ? RESULT_OK : RESULT_ERR_SYN); - } - - if (sending && m_state != bs_ready) { // check received symbol for equality if not in arbitration - if (recvSymbol != sendSymbol) { - return setState(bs_skip, RESULT_ERR_SYMBOL); - } - measureLatency(&sentTime, &recvTime); - } - - switch (m_state) { - case bs_ready: - case bs_recvCmd: - case bs_recvRes: - case bs_sendCmd: - case bs_sendRes: - SymbolString::updateCrc(recvSymbol, &m_crc); - break; - default: - break; - } - - if (m_escape) { - // check escape/unescape state - if (sending) { - if (sendSymbol == ESC) { - return RESULT_OK; - } - sendSymbol = recvSymbol = m_escape; - } else { - if (recvSymbol > 0x01) { - return setState(bs_skip, RESULT_ERR_ESC); - } - recvSymbol = recvSymbol == 0x00 ? ESC : SYN; - } - m_escape = 0; - } else if (!sending && recvSymbol == ESC) { - m_escape = ESC; - return RESULT_OK; - } - - switch (m_state) { - case bs_noSignal: - return setState(bs_skip, RESULT_OK); - - case bs_skip: - return RESULT_OK; - - case bs_ready: - if (m_currentRequest != nullptr && sending) { - // check arbitration - if (recvSymbol == sendSymbol) { // arbitration successful - // measure arbitration delay - int64_t 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 - 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) { - m_arbitrationDelayMin = latency; - } - if (m_arbitrationDelayMax == -1 || latency > m_arbitrationDelayMax) { - m_arbitrationDelayMax = latency; - } - logInfo(lf_bus, "arbitration delay %d - %d micros", m_arbitrationDelayMin, m_arbitrationDelayMax); - } - } - m_nextSendPos = 1; - m_repeat = false; - return setState(bs_sendCmd, RESULT_OK); - } - // arbitration lost. if same priority class found, try again after next AUTO-SYN - m_remainLockCount = isMaster(recvSymbol) ? 2 : 1; // number of SYN to wait for before next send try - if ((recvSymbol & 0x0f) != (sendSymbol & 0x0f) && m_lockCount > m_remainLockCount) { - // if different priority class found, try again after N AUTO-SYN symbols (at least next AUTO-SYN) - m_remainLockCount = m_lockCount; - } - setState(m_state, RESULT_ERR_BUS_LOST); // try again later - } - m_command.push_back(recvSymbol); - m_repeat = false; - return setState(bs_recvCmd, RESULT_OK); - - case bs_recvCmd: - m_command.push_back(recvSymbol); - if (m_command.isComplete()) { // all data received - return setState(bs_recvCmdCrc, RESULT_OK); - } - return RESULT_OK; - - case bs_recvCmdCrc: - m_crcValid = recvSymbol == m_crc; - if (m_command[1] == BROADCAST) { - if (m_crcValid) { - addSeenAddress(m_command[0]); - messageCompleted(); - return setState(bs_skip, RESULT_OK); - } - return setState(bs_skip, RESULT_ERR_CRC); - } - if (m_answer) { - symbol_t dstAddress = m_command[1]; - if (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress) { - if (m_crcValid) { - addSeenAddress(m_command[0]); - m_currentAnswering = true; - return setState(bs_sendCmdAck, RESULT_OK); - } - return setState(bs_sendCmdAck, RESULT_ERR_CRC); - } - } - if (m_crcValid) { - addSeenAddress(m_command[0]); - return setState(bs_recvCmdAck, RESULT_OK); - } - if (m_repeat) { - return setState(bs_skip, RESULT_ERR_CRC); - } - return setState(bs_recvCmdAck, RESULT_ERR_CRC); - - case bs_recvCmdAck: - if (recvSymbol == ACK) { - if (!m_crcValid) { - return setState(bs_skip, RESULT_ERR_ACK); - } - if (m_currentRequest != nullptr) { - if (isMaster(m_currentRequest->m_master[1])) { - messageCompleted(); - return setState(bs_sendSyn, RESULT_OK); - } - } else if (isMaster(m_command[1])) { - messageCompleted(); - return setState(bs_skip, RESULT_OK); - } - - m_repeat = false; - return setState(bs_recvRes, RESULT_OK); - } - if (recvSymbol == NAK) { - if (!m_repeat) { - m_repeat = true; - m_crc = 0; - m_nextSendPos = 0; - m_command.clear(); - if (m_currentRequest != nullptr) { - return setState(bs_sendCmd, RESULT_ERR_NAK, true); - } - return setState(bs_recvCmd, RESULT_ERR_NAK); - } - return setState(bs_skip, RESULT_ERR_NAK); - } - return setState(bs_skip, RESULT_ERR_ACK); - - case bs_recvRes: - m_response.push_back(recvSymbol); - if (m_response.isComplete()) { // all data received - return setState(bs_recvResCrc, RESULT_OK); - } - return RESULT_OK; - - case bs_recvResCrc: - m_crcValid = recvSymbol == m_crc; - if (m_crcValid) { - if (m_currentRequest != nullptr) { - return setState(bs_sendResAck, RESULT_OK); - } - return setState(bs_recvResAck, RESULT_OK); - } - if (m_repeat) { - if (m_currentRequest != nullptr) { - return setState(bs_sendSyn, RESULT_ERR_CRC); - } - return setState(bs_skip, RESULT_ERR_CRC); - } - if (m_currentRequest != nullptr) { - return setState(bs_sendResAck, RESULT_ERR_CRC); - } - return setState(bs_recvResAck, RESULT_ERR_CRC); - - case bs_recvResAck: - if (recvSymbol == ACK) { - if (!m_crcValid) { - return setState(bs_skip, RESULT_ERR_ACK); - } - messageCompleted(); - return setState(bs_skip, RESULT_OK); - } - if (recvSymbol == NAK) { - if (!m_repeat) { - m_repeat = true; - if (m_currentAnswering) { - m_nextSendPos = 0; - return setState(bs_sendRes, RESULT_ERR_NAK, true); - } - m_response.clear(); - return setState(bs_recvRes, RESULT_ERR_NAK, true); - } - return setState(bs_skip, RESULT_ERR_NAK); - } - return setState(bs_skip, RESULT_ERR_ACK); - - case bs_sendCmd: - if (!sending || m_currentRequest == nullptr) { - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - m_nextSendPos++; - if (m_nextSendPos >= m_currentRequest->m_master.size()) { - return setState(bs_sendCmdCrc, RESULT_OK); - } - return RESULT_OK; - - case bs_sendCmdCrc: - if (m_currentRequest->m_master[1] == BROADCAST) { - messageCompleted(); - return setState(bs_sendSyn, RESULT_OK); - } - m_crcValid = true; - return setState(bs_recvCmdAck, RESULT_OK); - - case bs_sendResAck: - if (!sending || m_currentRequest == nullptr) { - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - if (!m_crcValid) { - if (!m_repeat) { - m_repeat = true; - m_response.clear(); - return setState(bs_recvRes, RESULT_ERR_NAK, true); - } - return setState(bs_sendSyn, RESULT_ERR_ACK); - } - messageCompleted(); - return setState(bs_sendSyn, RESULT_OK); - - case bs_sendCmdAck: - if (!sending || !m_answer) { - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - if (!m_crcValid) { - if (!m_repeat) { - m_repeat = true; - m_crc = 0; - m_command.clear(); - return setState(bs_recvCmd, RESULT_ERR_NAK, true); - } - return setState(bs_skip, RESULT_ERR_ACK); - } - if (isMaster(m_command[1])) { - messageCompleted(); // TODO decode command and store value into database of internal variables - return setState(bs_skip, RESULT_OK); - } - - m_nextSendPos = 0; - m_repeat = false; - { - Message* message; - message = m_messages->find(m_command); - if (message == nullptr) { - message = m_messages->find(m_command, true); - if (message != nullptr && message->getSrcAddress() != SYN) { - message = nullptr; - } - } - if (message == nullptr || message->isWrite()) { - // don't know this request or definition has wrong direction, deny - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - istringstream input; // TODO create input from database of internal variables - if (message == m_messages->getScanMessage() || message == m_messages->getScanMessage(m_ownSlaveAddress)) { - input.str(SCAN_ANSWER); - } - // build response and store in m_response for sending back to requesting master - m_response.clear(); - result = message->prepareSlave(&input, &m_response); - if (result != RESULT_OK) { - return setState(bs_skip, result); - } - } - return setState(bs_sendRes, RESULT_OK); - - case bs_sendRes: - if (!sending || !m_answer) { - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - m_nextSendPos++; - if (m_nextSendPos >= m_response.size()) { - // slave data completely sent - return setState(bs_sendResCrc, RESULT_OK); - } - return RESULT_OK; - - case bs_sendResCrc: - if (!sending || !m_answer) { - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - return setState(bs_recvResAck, RESULT_OK); - - case bs_sendSyn: - if (!sending) { - return setState(bs_ready, RESULT_ERR_INVALID_ARG); - } - return setState(bs_ready, RESULT_OK); - } - return RESULT_OK; + // build response and store in m_response for sending back to requesting master + return message->prepareSlave(&input, response); } -result_t BusHandler::setState(BusState state, result_t result, bool firstRepetition) { - if (m_currentRequest != nullptr) { - if (result == RESULT_ERR_BUS_LOST && m_currentRequest->m_busLostRetries < m_busLostRetries) { - logDebug(lf_bus, "%s during %s, retry", getResultCode(result), getStateCode(m_state)); - m_currentRequest->m_busLostRetries++; - m_nextRequests.push(m_currentRequest); // repeat - m_currentRequest = nullptr; - } else if (state == bs_sendSyn || (result != RESULT_OK && !firstRepetition)) { - logDebug(lf_bus, "notify request: %s", getResultCode(result)); - bool restart = m_currentRequest->notify( - result == RESULT_ERR_SYN && (m_state == bs_recvCmdAck || m_state == bs_recvRes) - ? RESULT_ERR_TIMEOUT : result, m_response); - if (restart) { - m_currentRequest->m_busLostRetries = 0; - m_nextRequests.push(m_currentRequest); - } else if (m_currentRequest->m_deleteOnFinish) { - delete m_currentRequest; - } else { - m_finishedRequests.push(m_currentRequest); - } - m_currentRequest = nullptr; - } - if (state == bs_skip) { - m_device->startArbitration(SYN); // reset arbitration state - } - } - if (state == bs_noSignal) { // notify all requests - m_response.clear(); // notify with empty response - while ((m_currentRequest = m_nextRequests.pop()) != nullptr) { - bool restart = m_currentRequest->notify(RESULT_ERR_NO_SIGNAL, m_response); - if (restart) { // should not occur with no signal - m_currentRequest->m_busLostRetries = 0; - m_nextRequests.push(m_currentRequest); - } else if (m_currentRequest->m_deleteOnFinish) { - delete m_currentRequest; - } else { - m_finishedRequests.push(m_currentRequest); - } - } - } - - m_escape = 0; - if (state == m_state) { - return result; - } - if ((result < RESULT_OK && !(result == RESULT_ERR_TIMEOUT && state == bs_skip && m_state == bs_ready)) - || (result != RESULT_OK && state == bs_skip && m_state != bs_ready)) { - logDebug(lf_bus, "%s during %s, switching to %s", getResultCode(result), getStateCode(m_state), - getStateCode(state)); - } else if (m_currentRequest != nullptr || state == bs_sendCmd || state == bs_sendCmdCrc || state == bs_sendCmdAck - || state == bs_sendRes || state == bs_sendResCrc || state == bs_sendResAck || state == bs_sendSyn - || m_state == bs_sendSyn) { - logDebug(lf_bus, "switching from %s to %s", getStateCode(m_state), getStateCode(state)); - } - if (state == bs_noSignal) { - if (m_generateSynInterval == 0 || m_state != bs_skip) { - logError(lf_bus, "signal lost"); - } - } else if (m_state == bs_noSignal) { - if (m_generateSynInterval == 0 || state != bs_skip) { - logNotice(lf_bus, "signal acquired"); - } - } - m_state = state; - - if (state == bs_ready || state == bs_skip) { - m_command.clear(); - m_crc = 0; - m_crcValid = false; - m_response.clear(); - m_nextSendPos = 0; - m_currentAnswering = false; - } else if (state == bs_recvRes || state == bs_sendRes) { - m_crc = 0; - } - return result; +void BusHandler::notifyProtocolSeenAddress(symbol_t address) { + m_seenAddresses[address] |= SEEN; } -void BusHandler::measureLatency(struct timespec* sentTime, struct timespec* recvTime) { - int64_t latencyLong = (recvTime->tv_sec*1000000000 + recvTime->tv_nsec - - sentTime->tv_sec*1000000000 - sentTime->tv_nsec)/1000000; - if (latencyLong < 0 || latencyLong > 1000) { - return; // clock skew or out of reasonable range - } - 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; - } - if (m_symbolLatencyMin == -1 || latency < m_symbolLatencyMin) { - m_symbolLatencyMin = latency; - } - if (m_symbolLatencyMax == -1 || latency > m_symbolLatencyMax) { - m_symbolLatencyMax = latency; - } - logInfo(lf_bus, "send/receive symbol latency %d - %d ms", m_symbolLatencyMin, m_symbolLatencyMax); -} - -bool BusHandler::addSeenAddress(symbol_t address) { - if (!isValidAddress(address, false)) { - return false; - } - bool hadConflict = m_addressConflict; - if (!isMaster(address)) { - if (!m_device->isReadOnly() && address == m_ownSlaveAddress) { - if (!m_addressConflict) { - m_addressConflict = true; - logError(lf_bus, "own slave address %2.2x is used by another participant", address); - } - } - m_seenAddresses[address] |= SEEN; - address = getMasterAddress(address); - if (address == SYN) { - return m_addressConflict && !hadConflict; - } - } - if ((m_seenAddresses[address]&SEEN) == 0) { - if (!m_device->isReadOnly() && address == m_ownMasterAddress) { - if (!m_addressConflict) { - m_addressConflict = true; - logError(lf_bus, "own master address %2.2x is used by another participant", address); - } - } else { - m_masterCount++; - if (m_autoLockCount && m_masterCount > m_lockCount) { - m_lockCount = m_masterCount; - } - logNotice(lf_bus, "new master %2.2x, master count %d", address, m_masterCount); - } - m_seenAddresses[address] |= SEEN; - } - return m_addressConflict && !hadConflict; -} - -void BusHandler::messageCompleted() { - const char* prefix = m_currentRequest ? "sent" : "received"; - // do an explicit copy here in case being called by another thread - const MasterSymbolString command(m_currentRequest ? m_currentRequest->m_master : m_command); - const SlaveSymbolString response(m_response); +void BusHandler::notifyProtocolMessage(bool sent, const MasterSymbolString& command, + const SlaveSymbolString& response) { symbol_t srcAddress = command[0], dstAddress = command[1]; - if (srcAddress == dstAddress) { - logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress); - return; - } - if (!m_currentAnswering) { - addSeenAddress(dstAddress); - } - bool master = isMaster(dstAddress); if (dstAddress == BROADCAST) { - logInfo(lf_update, "%s BC cmd: %s", prefix, command.getStr().c_str()); if (command.getDataSize() >= 10 && command[2] == 0x07 && command[3] == 0x04) { symbol_t slaveAddress = getSlaveAddress(srcAddress); - addSeenAddress(slaveAddress); + notifyProtocolSeenAddress(slaveAddress); Message* message = m_messages->getScanMessage(slaveAddress); if (message && (message->getLastUpdateTime() == 0 || message->getLastSlaveData().getDataSize() < 10)) { // e.g. 10fe07040a b5564149303001248901 MasterSymbolString dummyMaster; istringstream input; - result_t result = message->prepareMaster(0, m_ownMasterAddress, SYN, UI_FIELD_SEPARATOR, &input, + result_t result = message->prepareMaster(0, m_protocol->getOwnMasterAddress(), SYN, UI_FIELD_SEPARATOR, &input, &dummyMaster); if (result == RESULT_OK) { SlaveSymbolString idData; @@ -1223,10 +414,7 @@ void BusHandler::messageCompleted() { logNotice(lf_update, "store broadcast ident: %s", getResultCode(result)); } } - } else if (master) { - logInfo(lf_update, "%s MM cmd: %s", prefix, command.getStr().c_str()); - } else { - logInfo(lf_update, "%s MS cmd: %s / %s", prefix, command.getStr().c_str(), response.getStr().c_str()); + } else if (!master) { if (command.size() >= 5 && command[2] == 0x07 && command[3] == 0x04) { Message* message = m_messages->getScanMessage(dstAddress); if (message && (message->getLastUpdateTime() == 0 || message->getLastSlaveData().getDataSize() < 10)) { @@ -1253,11 +441,10 @@ void BusHandler::messageCompleted() { } m_grabbedMessages[key].setLastData(command, response); } + const char* prefix = sent ? "sent" : "received"; if (message == nullptr) { - if (dstAddress == BROADCAST) { - logNotice(lf_update, "%s unknown BC cmd: %s", prefix, command.getStr().c_str()); - } else if (master) { - logNotice(lf_update, "%s unknown MM cmd: %s", prefix, command.getStr().c_str()); + if (dstAddress == BROADCAST || master) { + logNotice(lf_update, "%s unknown %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str()); } else { logNotice(lf_update, "%s unknown MS cmd: %s / %s", prefix, command.getStr().c_str(), response.getStr().c_str()); @@ -1280,7 +467,7 @@ void BusHandler::messageCompleted() { command.getStr().c_str(), response.getStr().c_str(), getResultCode(result)); } else { string data = output.str(); - if (m_answer && dstAddress == (master ? m_ownMasterAddress : m_ownSlaveAddress)) { + if (m_protocol->isOwnAddress(dstAddress)) { logNotice(lf_update, "%s %s self-update %s %s QQ=%2.2x: %s", prefix, mode, circuit.c_str(), name.c_str(), srcAddress, data.c_str()); // TODO store in database of internal variables } else if (message->getDstAddress() == SYN) { // any destination @@ -1307,7 +494,7 @@ result_t BusHandler::prepareScan(symbol_t slave, bool full, const string& levels if (scanMessage == nullptr) { return RESULT_ERR_NOTFOUND; } - if (m_device->isReadOnly()) { + if (m_protocol->getDevice()->isReadOnly()) { return RESULT_OK; } deque messages; @@ -1353,7 +540,7 @@ result_t BusHandler::prepareScan(symbol_t slave, bool full, const string& levels return RESULT_OK; } *request = new ScanRequest(slave == SYN, m_messages, messages, slaves, this, *reload ? 0 : 1); - result_t result = (*request)->prepare(m_ownMasterAddress); + result_t result = (*request)->prepare(m_protocol->getOwnMasterAddress()); if (result < RESULT_OK) { delete *request; *request = nullptr; @@ -1377,7 +564,8 @@ result_t BusHandler::startScan(bool full, const string& levels) { } m_scanResults.clear(); m_runningScans++; - m_nextRequests.push(request); + m_protocol->addRequest(request, false); + // request is deleted by ProtocolHandler after finish return RESULT_OK; } @@ -1447,7 +635,7 @@ void BusHandler::formatScanResult(ostringstream* output) const { void BusHandler::formatSeenInfo(ostringstream* output) const { symbol_t address = 0; for (int index = 0; index < 256; index++, address++) { - bool ownAddress = !m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress); + bool ownAddress = m_protocol->isOwnAddress(address); if (!isValidAddress(address, false) || ((m_seenAddresses[address]&SEEN) == 0 && !ownAddress)) { continue; } @@ -1465,10 +653,10 @@ void BusHandler::formatSeenInfo(ostringstream* output) const { } if (ownAddress) { *output << ", ebusd"; - if (m_answer) { + if (m_protocol->isAnswering()) { *output << " (answering)"; } - if (m_addressConflict && (m_seenAddresses[address]&SEEN) != 0) { + if (m_protocol->isAddressConflict(address)) { *output << ", conflict"; } } @@ -1511,14 +699,14 @@ void BusHandler::formatSeenInfo(ostringstream* output) const { } void BusHandler::formatUpdateInfo(ostringstream* output) const { - if (hasSignal()) { - *output << ",\"s\":" << m_maxSymPerSec; + if (m_protocol->hasSignal()) { + *output << ",\"s\":" << m_protocol->getMaxSymPerSec(); } - *output << ",\"c\":" << m_masterCount + *output << ",\"c\":" << m_protocol->getMasterCount() << ",\"m\":" << m_messages->size() - << ",\"ro\":" << (m_device->isReadOnly() ? 1 : 0) - << ",\"an\":" << (m_answer ? 1 : 0) - << ",\"co\":" << (m_addressConflict ? 1 : 0); + << ",\"ro\":" << (m_protocol->getDevice()->isReadOnly() ? 1 : 0) + << ",\"an\":" << (m_protocol->isAnswering() ? 1 : 0) + << ",\"co\":" << (m_protocol->isAddressConflict(SYN) ? 1 : 0); if (m_grabMessages) { size_t unknownCnt = 0; *output << ",\"gm\":["; @@ -1539,7 +727,8 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const { } unsigned char address = 0; for (int index = 0; index < 256; index++, address++) { - bool ownAddress = !m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress); + bool ownAddress = !m_protocol->getDevice()->isReadOnly() + && (address == m_protocol->getOwnMasterAddress() || address == m_protocol->getOwnSlaveAddress()); if (!isValidAddress(address, false) || ((m_seenAddresses[address]&SEEN) == 0 && !ownAddress)) { continue; } @@ -1626,8 +815,7 @@ result_t BusHandler::scanAndWait(symbol_t dstAddress, bool loadScanConfig, bool m_scanResults[dstAddress].resize(1); } m_runningScans++; - m_nextRequests.push(request); - requestExecuted = m_finishedRequests.remove(request, true); + requestExecuted = m_protocol->addRequest(request, true); result = requestExecuted ? request->m_result : RESULT_ERR_TIMEOUT; delete request; request = nullptr; diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index de007443..e0f6c684 100755 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -19,7 +19,6 @@ #ifndef EBUSD_BUSHANDLER_H_ #define EBUSD_BUSHANDLER_H_ -#include #include #include #include @@ -31,59 +30,16 @@ #include "lib/ebus/symbol.h" #include "lib/ebus/result.h" #include "lib/ebus/device.h" -#include "lib/utils/queue.h" -#include "lib/utils/thread.h" +#include "lib/ebus/protocol.h" namespace ebusd { /** @file ebusd/bushandler.h - * Classes, functions, and constants related to handling of symbols on the eBUS. - * - * The following table shows the possible states, symbols, and state transition - * depending on the kind of message to send/receive: - * @image html states.png "ebusd BusHandler states" + * Classes, functions, and constants related to handling messages on the eBUS. */ using std::string; -/** the default time [ms] for retrieving a symbol from an addressed slave. */ -#define SLAVE_RECV_TIMEOUT 15 - -/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */ -#define SYN_TIMEOUT 51 - -/** 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_MICROS 4700 - -/** 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 { - bs_noSignal, //!< no signal on the bus - bs_skip, //!< skip all symbols until next @a SYN - bs_ready, //!< ready for next master (after @a SYN symbol, send/receive QQ) - bs_recvCmd, //!< receive command (ZZ, PBSB, master data) [passive set] - bs_recvCmdCrc, //!< receive command CRC [passive set] - bs_recvCmdAck, //!< receive command ACK/NACK [passive set + active set+get] - bs_recvRes, //!< receive response (slave data) [passive set + active get] - bs_recvResCrc, //!< receive response CRC [passive set + active get] - bs_recvResAck, //!< receive response ACK/NACK [passive set] - bs_sendCmd, //!< send command (ZZ, PBSB, master data) [active set+get] - bs_sendCmdCrc, //!< send command CRC [active set+get] - bs_sendResAck, //!< send response ACK/NACK [active get] - bs_sendCmdAck, //!< send command ACK/NACK [passive get] - bs_sendRes, //!< send response (slave data) [passive get] - bs_sendResCrc, //!< send response CRC [passive get] - bs_sendSyn, //!< send SYN for completed transfer [active set+get] -}; - /** bit for the seen state: seen. */ #define SEEN 0x01 @@ -101,47 +57,6 @@ enum BusState { class BusHandler; -/** - * Generic request for sending to and receiving from the bus. - */ -class BusRequest { - friend class BusHandler; - - public: - /** - * Constructor. - * @param master the master data @a MasterSymbolString to send. - * @param deleteOnFinish whether to automatically delete this @a BusRequest when finished. - */ - BusRequest(const MasterSymbolString& master, bool deleteOnFinish) - : m_master(master), m_busLostRetries(0), - m_deleteOnFinish(deleteOnFinish) {} - - /** - * Destructor. - */ - virtual ~BusRequest() {} - - /** - * Notify the request of the specified result. - * @param result the result of the request. - * @param slave the @a SlaveSymbolString received. - * @return true if the request needs to be restarted. - */ - virtual bool notify(result_t result, const SlaveSymbolString& slave) = 0; - - - protected: - /** the master data @a MasterSymbolString to send. */ - const MasterSymbolString& m_master; - - /** the number of times a send is repeated due to lost arbitration. */ - unsigned int m_busLostRetries; - - /** whether to automatically delete this @a BusRequest when finished. */ - const bool m_deleteOnFinish; -}; - /** * A poll @a BusRequest handled by @a BusHandler itself. @@ -259,39 +174,6 @@ class ScanRequest : public BusRequest { }; -/** - * An active @a BusRequest that can be waited for. - */ -class ActiveBusRequest : public BusRequest { - friend class BusHandler; - - public: - /** - * Constructor. - * @param master the master data @a MasterSymbolString to send. - * @param slave reference to @a SlaveSymbolString for filling in the received slave data. - */ - ActiveBusRequest(const MasterSymbolString& master, SlaveSymbolString* slave) - : BusRequest(master, false), m_result(RESULT_ERR_NO_SIGNAL), m_slave(slave) {} - - /** - * Destructor. - */ - virtual ~ActiveBusRequest() {} - - // @copydoc - bool notify(result_t result, const SlaveSymbolString& slave) override; - - - private: - /** the result of handling the request. */ - result_t m_result; - - /** reference to @a SlaveSymbolString for filling in the received slave data. */ - SlaveSymbolString* m_slave; -}; - - /** * Helper class for keeping track of grabbed messages. */ @@ -362,7 +244,7 @@ class GrabbedMessage { /** * Handles input from and output to the bus with respect to the eBUS protocol. */ -class BusHandler : public WaitThread { +class BusHandler : public ProtocolListener { public: /** * Construct a new instance. @@ -380,82 +262,39 @@ class BusHandler : public WaitThread { * @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled. */ BusHandler(Device* device, MessageMap* messages, ScanHelper* scanHelper, - symbol_t ownAddress, bool answer, - unsigned int busLostRetries, unsigned int failedSendRetries, - 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_scanHelper(scanHelper), - m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)), - m_answer(answer), m_addressConflict(false), - m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), - 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), - m_pollInterval(pollInterval), m_symbolLatencyMin(-1), m_symbolLatencyMax(-1), m_arbitrationDelayMin(-1), - m_arbitrationDelayMax(-1), m_lastReceive(0), m_lastPoll(0), - m_currentRequest(nullptr), m_currentAnswering(false), m_runningScans(0), m_nextSendPos(0), - m_symPerSec(0), m_maxSymPerSec(0), - m_state(bs_noSignal), m_escape(0), m_crc(0), m_crcValid(false), m_repeat(false), + const ebus_protocol_config_t config, unsigned int pollInterval) + : m_messages(messages), m_scanHelper(scanHelper), + m_pollInterval(pollInterval), m_lastPoll(0), m_runningScans(0), m_grabMessages(true) { + m_protocol = ProtocolHandler::create(config, device, this); memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); - m_lastSynReceiveTime.tv_sec = 0; - m_lastSynReceiveTime.tv_nsec = 0; } /** * Destructor. */ virtual ~BusHandler() { - stop(); - join(); - BusRequest* req; - while ((req = m_finishedRequests.pop()) != nullptr) { - delete req; - } - while ((req = m_nextRequests.pop()) != nullptr) { - if (req->m_deleteOnFinish) { - delete req; - } - } - if (m_currentRequest != nullptr) { - delete m_currentRequest; - m_currentRequest = nullptr; + if (m_protocol) { + delete m_protocol; + m_protocol = nullptr; } } + /** + * @return the @a ProtocolHandler instance for accessing the bus. + */ + ProtocolHandler* getProtocol() const { return m_protocol; } + /** * @return the @a Device instance for accessing the bus. */ - const Device* getDevice() const { return m_device; } + const Device* getDevice() const { return m_protocol->getDevice(); } /** * Clear stored values (e.g. scan results). */ void clear(); - /** - * Inject a message from outside and treat it as regularly retrieved from the bus. - * @param master the @a MasterSymbolString with the master data. - * @param slave the @a SlaveSymbolString with the slave data. - */ - void injectMessage(const MasterSymbolString& master, const SlaveSymbolString& slave) { - m_command = master; - m_response = slave; - m_addressConflict = true; // avoid conflict messages - messageCompleted(); - m_addressConflict = false; - } - - /** - * Send a message on the bus and wait for the answer. - * @param master the @a MasterSymbolString with the master data to send. - * @param slave the @a SlaveSymbolString that will be filled with retrieved slave data. - * @return the result code. - */ - result_t sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave); - /** * Prepare the master part for the @a Message, send it to the bus and wait for the answer. * @param message the @a Message instance. @@ -467,11 +306,6 @@ class BusHandler : public WaitThread { result_t readFromBus(Message* message, const string& inputStr, symbol_t dstAddress = SYN, symbol_t srcAddress = SYN); - /** - * Main thread entry. - */ - virtual void run(); - /** * Initiate a scan of the slave addresses. * @param full true for a full scan (all slaves), false for scanning only already seen slaves. @@ -560,59 +394,6 @@ class BusHandler : public WaitThread { void formatGrabResult(bool unknown, OutputFormat outputFormat, ostringstream* output, bool isDirectMode = false, time_t since = 0, time_t until = 0) const; - /** - * Return true when a signal on the bus is available. - * @return true when a signal on the bus is available. - */ - bool hasSignal() const { return m_state != bs_noSignal; } - - /** - * Reconnect the device. - */ - void reconnect() { m_reconnect = true; } - - /** - * Return the current symbol rate. - * @return the number of received symbols in the last second. - */ - unsigned int getSymbolRate() const { return m_symPerSec; } - - /** - * Return the maximum seen symbol rate. - * @return the maximum number of received symbols per second ever seen. - */ - unsigned int getMaxSymbolRate() const { return m_maxSymPerSec; } - - /** - * Return the minimal measured latency between send and receive of a symbol. - * @return the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. - */ - int getMinSymbolLatency() const { return m_symbolLatencyMin; } - - /** - * Return the maximal measured latency between send and receive of a symbol. - * @return the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. - */ - int getMaxSymbolLatency() const { return m_symbolLatencyMax; } - - /** - * Return the minimal measured delay between received SYN and sent own master address in microseconds. - * @return the minimal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known. - */ - int getMinArbitrationDelay() const { return m_arbitrationDelayMin; } - - /** - * Return the maximal measured delay between received SYN and sent own master address in microseconds. - * @return the maximal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known. - */ - int getMaxArbitrationDelay() const { return m_arbitrationDelayMax; } - - /** - * Return the number of masters already seen. - * @return the number of masters already seen (including ebusd itself). - */ - unsigned int getMasterCount() const { return m_masterCount; } - /** * Get the next slave address that still needs to be scanned or loaded. * @param lastAddress the last returned slave address, or 0 for returning the first one. @@ -628,42 +409,19 @@ class BusHandler : public WaitThread { */ void setScanConfigLoaded(symbol_t address, const string& file); + // @copydoc + void notifyProtocolStatus(bool signal) override; + + // @copydoc + result_t notifyProtocolAnswer(const MasterSymbolString& master, SlaveSymbolString* slave) override; + + // @copydoc + void notifyProtocolSeenAddress(symbol_t address) override; + + // @copydoc + void notifyProtocolMessage(bool sent, const MasterSymbolString& master, const SlaveSymbolString& slave) override; private: - /** - * Handle the next symbol on the bus. - * @return RESULT_OK on success, or an error code. - */ - result_t handleSymbol(); - - /** - * Set a new @a BusState and add a log message if necessary. - * @param state the new @a BusState. - * @param result the result code. - * @param firstRepetition true if the first repetition of a message part is being started. - * @return the result code. - */ - result_t setState(BusState state, result_t result, bool firstRepetition = false); - - /** - * Add a seen bus address. - * @param address the seen bus address. - * @return true if a conflict with the own addresses was detected, false otherwise. - */ - bool addSeenAddress(symbol_t address); - - /** - * Called to measure the latency between send and receive of a symbol. - * @param sentTime the time the symbol was sent. - * @param recvTime the time the symbol was received. - */ - void measureLatency(struct timespec* sentTime, struct timespec* recvTime); - - /** - * Called when a message sending or reception was successfully completed. - */ - void messageCompleted(); - /** * Prepare a @a ScanRequest. * @param slave the single slave address to scan, or @a SYN for multiple. @@ -675,11 +433,8 @@ class BusHandler : public WaitThread { */ result_t prepareScan(symbol_t slave, bool full, const string& levels, bool* reload, ScanRequest** request); - /** the @a Device instance for accessing the bus. */ - Device* m_device; - - /** set to @p true when the device shall be reconnected. */ - bool m_reconnect; + /** the @a ProtocolHandler instance for accessing the bus. */ + ProtocolHandler* m_protocol; /** the @a MessageMap instance with all known @a Message instances. */ MessageMap* m_messages; @@ -687,121 +442,15 @@ class BusHandler : public WaitThread { /** the @a ScanHelper instance. */ ScanHelper* m_scanHelper; - /** the own master address. */ - const symbol_t m_ownMasterAddress; - - /** the own slave address. */ - const symbol_t m_ownSlaveAddress; - - /** whether to answer queries for the own master/slave address. */ - const bool m_answer; - - /** set to @p true once an address conflict with the own addresses was detected. */ - bool m_addressConflict; - - /** the number of times a send is repeated due to lost arbitration. */ - const unsigned int m_busLostRetries; - - /** the number of times a failed send is repeated (other than lost arbitration). */ - const unsigned int m_failedSendRetries; - - /** the maximum time in milliseconds for bus acquisition. */ - const unsigned int m_busAcquireTimeout; - - /** the maximum time in milliseconds an addressed slave is expected to acknowledge. */ - const unsigned int m_slaveRecvTimeout; - - /** the number of masters already seen. */ - unsigned int m_masterCount; - - /** whether m_lockCount shall be detected automatically. */ - const bool m_autoLockCount; - - /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */ - unsigned int m_lockCount; - - /** the remaining number of AUTO-SYN symbols before sending is allowed again. */ - unsigned int m_remainLockCount; - - /** 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. */ const unsigned int m_pollInterval; - /** the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */ - int m_symbolLatencyMin; - - /** the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */ - int m_symbolLatencyMax; - - /** - * the minimal measured delay between received SYN and sent own master address in microseconds, - * -1 if not yet known. - */ - int m_arbitrationDelayMin; - - /** - * the maximal measured delay between received SYN and sent own master address in microseconds, - * -1 if not yet known. - */ - int m_arbitrationDelayMax; - - /** the time of the last received SYN symbol, or 0 for never. */ - struct timespec m_lastSynReceiveTime; - - /** the time of the last received symbol, or 0 for never. */ - time_t m_lastReceive; - /** the time of the last poll, or 0 for never. */ time_t m_lastPoll; - /** the queue of @a BusRequests that shall be handled. */ - Queue m_nextRequests; - - /** the currently handled BusRequest, or nullptr. */ - BusRequest* m_currentRequest; - - /** whether currently answering a request from another participant. */ - bool m_currentAnswering; - - /** the queue of @a BusRequests that are already finished. */ - Queue m_finishedRequests; - /** the number of scan requests currently running. */ unsigned int m_runningScans; - /** the offset of the next symbol that needs to be sent from the command or response, - * (only relevant if m_request is set and state is @a bs_command or @a bs_response). */ - size_t m_nextSendPos; - - /** the number of received symbols in the last second. */ - unsigned int m_symPerSec; - - /** the maximum number of received symbols per second ever seen. */ - unsigned int m_maxSymPerSec; - - /** the current @a BusState. */ - BusState m_state; - - /** 0 when not escaping/unescaping, or @a ESC when receiving, or the original value when sending. */ - symbol_t m_escape; - - /** the calculated CRC. */ - symbol_t m_crc; - - /** whether the CRC matched. */ - bool m_crcValid; - - /** whether the current message part is being repeated. */ - bool m_repeat; - - /** the received command @a MasterSymbolString. */ - MasterSymbolString m_command; - - /** the received response @a SlaveSymbolString or response to send. */ - SlaveSymbolString m_response; - /** the participating bus addresses seen so far (0 if not seen yet, or combination of @a SEEN bits). */ symbol_t m_seenAddresses[256]; diff --git a/src/ebusd/knxhandler.cpp b/src/ebusd/knxhandler.cpp index 3a4113e2..2e73a8d1 100644 --- a/src/ebusd/knxhandler.cpp +++ b/src/ebusd/knxhandler.cpp @@ -609,7 +609,7 @@ void KnxHandler::handleGroupTelegram(knx_addr_t src, knx_addr_t dest, int len, c sendGlobalValue(GLOBAL_UPTIME, static_cast(time(nullptr) - m_start), true); break; case GLOBAL_SIGNAL: - sendGlobalValue(GLOBAL_SIGNAL, m_busHandler->hasSignal() ? 1 : 0, true); + sendGlobalValue(GLOBAL_SIGNAL, m_busHandler->getProtocol()->hasSignal() ? 1 : 0, true); break; case GLOBAL_SCAN: sendGlobalValue(GLOBAL_SCAN, m_lastScanStatus == SCAN_STATUS_RUNNING ? 1 : 0, true); @@ -893,7 +893,7 @@ void KnxHandler::run() { time(&lastTaskRun); } if (sendSignal) { - if (m_busHandler->hasSignal()) { + if (m_busHandler->getProtocol()->hasSignal()) { lastSignal = now; if (!signal || reconnected) { signal = true; diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 2269a570..dc99f5c2 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -412,7 +412,7 @@ int main(int argc, char* argv[], char* envp[]) { if (!s_scanHelper->parseMessage(argv[arg_index++], false, &master, &slave)) { continue; } - busHandler->injectMessage(master, slave); + busHandler->getProtocol()->injectMessage(master, slave); if (s_opt.scanConfig && master.size() >= 5 && master[4] == 0 && master[2] == 0x07 && master[3] == 0x04 && isValidAddress(master[1], false) && !isMaster(master[1]) && !scanAddresses[master[1]]) { // scan message, simulate scanning diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 48e83c4f..5e0eec94 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -151,13 +151,20 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag } } // create BusHandler + ebus_protocol_config_t config = { + .ownAddress = m_address, + .answer = opt.answer, + .busLostRetries = opt.acquireRetries, + .failedSendRetries = opt.sendRetries, + .busAcquireTimeout = opt.acquireTimeout, + .slaveRecvTimeout = opt.receiveTimeout, + .lockCount = opt.masterCount, + .generateSyn = opt.generateSyn, + }; m_busHandler = new BusHandler(m_device, m_messages, scanHelper, - m_address, opt.answer, - opt.acquireRetries, opt.sendRetries, - opt.acquireTimeout, opt.receiveTimeout, - opt.masterCount, opt.generateSyn, - opt.pollInterval); - m_busHandler->start("bushandler"); + config, opt.pollInterval); + m_protocol = m_busHandler->getProtocol(); + m_protocol->start("bushandler"); // create network m_htmlPath = opt.htmlPath; @@ -194,6 +201,7 @@ MainLoop::~MainLoop() { if (m_busHandler != nullptr) { delete m_busHandler; m_busHandler = nullptr; + m_protocol = nullptr; // ProtocolHandler is freed by BusHandler } if (m_device != nullptr) { delete m_device; @@ -255,16 +263,16 @@ void MainLoop::run() { lastTaskRun = now; } else if (!m_shutdown && now > lastTaskRun+taskDelay) { logDebug(lf_main, "performing regular tasks"); - if (m_busHandler->hasSignal()) { + if (m_protocol->hasSignal()) { lastSignal = now; } else if (lastSignal && now > lastSignal+RECONNECT_MISSING_SIGNAL) { lastSignal = 0; - m_busHandler->reconnect(); + m_protocol->reconnect(); m_reconnectCount++; } if (m_scanConfig && scanRetry <= m_scanRetries) { bool loadDelay = false; - if (m_initialScan != ESC && reload && m_busHandler->hasSignal()) { + if (m_initialScan != ESC && reload && m_protocol->hasSignal()) { loadDelay = true; result_t result; if (m_initialScan == SYN) { @@ -282,7 +290,7 @@ void MainLoop::run() { istringstream input; result = message->prepareMaster(0, m_address, SYN, UI_FIELD_SEPARATOR, &input, &master); if (result == RESULT_OK) { - result = m_busHandler->sendAndWait(master, &slave); + result = m_protocol->sendAndWait(master, &slave); } } else { result = RESULT_ERR_NOTFOUND; @@ -304,7 +312,7 @@ void MainLoop::run() { reload = false; } } - if (!loadDelay && m_busHandler->hasSignal()) { + if (!loadDelay && m_protocol->hasSignal()) { lastScanAddress = m_busHandler->getNextScanAddress(lastScanAddress, scanCompleted >= SCAN_REPEAT_COUNT); if (lastScanAddress == SYN) { taskDelay = 5; @@ -335,7 +343,7 @@ void MainLoop::run() { dataSink->notifyScanStatus(lastScanStatus); } } - } else if (reload && m_busHandler->hasSignal()) { + } else if (reload && m_protocol->hasSignal()) { reload = false; // execute initial instructions m_scanHelper->executeInstructions(m_busHandler); @@ -873,7 +881,7 @@ result_t MainLoop::executeRead(const vector& args, const string& levels, // send message SlaveSymbolString slave; - ret = m_busHandler->sendAndWait(master, &slave); + ret = m_protocol->sendAndWait(master, &slave); if (ret == RESULT_OK) { ret = message->storeLastData(master, slave); @@ -1095,7 +1103,7 @@ result_t MainLoop::executeWrite(const vector& args, const string levels, } // send message SlaveSymbolString slave; - ret = m_busHandler->sendAndWait(master, &slave); + ret = m_protocol->sendAndWait(master, &slave); if (ret == RESULT_OK) { // also update read messages @@ -1227,7 +1235,7 @@ result_t MainLoop::parseHexAndSend(const vector& args, size_t& argPos, b // send message SlaveSymbolString slave; - ret = m_busHandler->sendAndWait(master, &slave); + ret = m_protocol->sendAndWait(master, &slave); if (ret == RESULT_OK) { if (master[1] == BROADCAST) { @@ -1601,11 +1609,11 @@ result_t MainLoop::executeState(const vector& args, ostringstream* ostre " Report bus state."; return RESULT_OK; } - if (m_busHandler->hasSignal()) { + if (m_protocol->hasSignal()) { *ostream << "signal acquired, " - << m_busHandler->getSymbolRate() << " symbols/sec (" - << m_busHandler->getMaxSymbolRate() << " max), " - << m_busHandler->getMasterCount() << " masters"; + << m_protocol->getSymbolRate() << " symbols/sec (" + << m_protocol->getMaxSymbolRate() << " max), " + << m_protocol->getMasterCount() << " masters"; return RESULT_OK; } return RESULT_ERR_NO_SIGNAL; @@ -1818,7 +1826,7 @@ result_t MainLoop::executeScan(const vector& args, const string& levels, return RESULT_OK; } - if (!m_busHandler->hasSignal()) { + if (!m_protocol->hasSignal()) { return RESULT_ERR_NO_SIGNAL; } result_t result; @@ -1941,17 +1949,17 @@ result_t MainLoop::executeInfo(const vector& args, const string& user, o if (!user.empty() || !levels.empty()) { *ostream << "access: " << levels << "\n"; } - if (m_busHandler->hasSignal()) { + if (m_protocol->hasSignal()) { *ostream << "signal: acquired\n" - << "symbol rate: " << m_busHandler->getSymbolRate() << "\n" - << "max symbol rate: " << m_busHandler->getMaxSymbolRate() << "\n"; - if (m_busHandler->getMinArbitrationDelay() >= 0) { - *ostream << "min arbitration micros: " << m_busHandler->getMinArbitrationDelay() << "\n" - << "max arbitration micros: " << m_busHandler->getMaxArbitrationDelay() << "\n"; + << "symbol rate: " << m_protocol->getSymbolRate() << "\n" + << "max symbol rate: " << m_protocol->getMaxSymbolRate() << "\n"; + if (m_protocol->getMinArbitrationDelay() >= 0) { + *ostream << "min arbitration micros: " << m_protocol->getMinArbitrationDelay() << "\n" + << "max arbitration micros: " << m_protocol->getMaxArbitrationDelay() << "\n"; } - if (m_busHandler->getMinSymbolLatency() >= 0) { - *ostream << "min symbol latency: " << m_busHandler->getMinSymbolLatency() << "\n" - << "max symbol latency: " << m_busHandler->getMaxSymbolLatency() << "\n"; + if (m_protocol->getMinSymbolLatency() >= 0) { + *ostream << "min symbol latency: " << m_protocol->getMinSymbolLatency() << "\n" + << "max symbol latency: " << m_protocol->getMaxSymbolLatency() << "\n"; } if (m_scanStatus != SCAN_STATUS_NONE) { *ostream << "scan: " << (m_scanStatus == SCAN_STATUS_FINISHED ? "finished" : "running"); @@ -1965,7 +1973,7 @@ result_t MainLoop::executeInfo(const vector& args, const string& user, o *ostream << "signal: no signal\n"; } *ostream << "reconnects: " << m_reconnectCount << "\n" - << "masters: " << m_busHandler->getMasterCount() << "\n" + << "masters: " << m_protocol->getMasterCount() << "\n" << "messages: " << m_messages->size() << "\n" << "conditional: " << m_messages->sizeConditional() << "\n" << "poll: " << m_messages->sizePoll() << "\n" @@ -2210,24 +2218,24 @@ result_t MainLoop::executeGet(const vector& args, bool* connected, ostri if (!user.empty() || !levels.empty()) { *ostream << ",\n \"access\": \"" << levels << "\""; } - *ostream << ",\n \"signal\": " << (m_busHandler->hasSignal() ? "true" : "false"); - if (m_busHandler->hasSignal()) { - *ostream << ",\n \"symbolrate\": " << m_busHandler->getSymbolRate() - << ",\n \"maxsymbolrate\": " << m_busHandler->getMaxSymbolRate(); - if (m_busHandler->getMinArbitrationDelay() >= 0) { - *ostream << ",\n \"minarbitrationmicros\": " << m_busHandler->getMinArbitrationDelay() - << ",\n \"maxarbitrationmicros\": " << m_busHandler->getMaxArbitrationDelay(); + *ostream << ",\n \"signal\": " << (m_protocol->hasSignal() ? "true" : "false"); + if (m_protocol->hasSignal()) { + *ostream << ",\n \"symbolrate\": " << m_protocol->getSymbolRate() + << ",\n \"maxsymbolrate\": " << m_protocol->getMaxSymbolRate(); + if (m_protocol->getMinArbitrationDelay() >= 0) { + *ostream << ",\n \"minarbitrationmicros\": " << m_protocol->getMinArbitrationDelay() + << ",\n \"maxarbitrationmicros\": " << m_protocol->getMaxArbitrationDelay(); } - if (m_busHandler->getMinSymbolLatency() >= 0) { - *ostream << ",\n \"minsymbollatency\": " << m_busHandler->getMinSymbolLatency() - << ",\n \"maxsymbollatency\": " << m_busHandler->getMaxSymbolLatency(); + if (m_protocol->getMinSymbolLatency() >= 0) { + *ostream << ",\n \"minsymbollatency\": " << m_protocol->getMinSymbolLatency() + << ",\n \"maxsymbollatency\": " << m_protocol->getMaxSymbolLatency(); } } if (!m_device->isReadOnly()) { *ostream << ",\n \"qq\": " << static_cast(m_address); } *ostream << ",\n \"reconnects\": " << m_reconnectCount - << ",\n \"masters\": " << m_busHandler->getMasterCount() + << ",\n \"masters\": " << m_protocol->getMasterCount() << ",\n \"messages\": " << m_messages->size() << ",\n \"lastup\": " << static_cast(maxLastUp) << "\n }" diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index d5efc5a0..4ef5ed08 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -443,6 +443,9 @@ class MainLoop : public Thread, DeviceListener { /** the created @a BusHandler instance. */ BusHandler* m_busHandler; + /** the created @a ProtocolHandler instance. */ + ProtocolHandler* m_protocol; + /** the reference to the @a Request @a Queue. */ Queue* m_requestQueue; diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp index c6340255..bd7aff72 100755 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -1041,7 +1041,7 @@ void MqttHandler::run() { time(&lastTaskRun); } if (sendSignal) { - if (m_busHandler->hasSignal()) { + if (m_busHandler->getProtocol()->hasSignal()) { lastSignal = now; if (!signal || reconnected) { signal = true; diff --git a/src/lib/ebus/CMakeLists.txt b/src/lib/ebus/CMakeLists.txt index 22b89d0c..468260af 100644 --- a/src/lib/ebus/CMakeLists.txt +++ b/src/lib/ebus/CMakeLists.txt @@ -7,6 +7,8 @@ set(libebus_a_SOURCES datatype.h datatype.cpp data.h data.cpp device.h device.cpp + protocol.h protocol.cpp + protocol_direct.h protocol_direct.cpp message.h message.cpp stringhelper.h stringhelper.cpp ) diff --git a/src/lib/ebus/Makefile.am b/src/lib/ebus/Makefile.am index 245ad754..15b40a5e 100644 --- a/src/lib/ebus/Makefile.am +++ b/src/lib/ebus/Makefile.am @@ -11,6 +11,8 @@ libebus_a_SOURCES = \ datatype.h datatype.cpp \ data.h data.cpp \ device.h device.cpp \ + protocol.h protocol.cpp \ + protocol_direct.h protocol_direct.cpp \ message.h message.cpp \ stringhelper.h stringhelper.cpp diff --git a/src/lib/ebus/protocol.cpp b/src/lib/ebus/protocol.cpp new file mode 100644 index 00000000..7842120a --- /dev/null +++ b/src/lib/ebus/protocol.cpp @@ -0,0 +1,140 @@ +/* + * ebusd - daemon for communication with eBUS heating systems. + * Copyright (C) 2014-2023 John Baier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "lib/ebus/protocol.h" +#include "lib/ebus/protocol_direct.h" +#include +#include "lib/utils/log.h" + +namespace ebusd { + +bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) { + if (result == RESULT_OK) { + string str = slave.getStr(); + logDebug(lf_bus, "read res: %s", str.c_str()); + } + m_result = result; + *m_slave = slave; + return false; +} + + +ProtocolHandler* ProtocolHandler::create(const ebus_protocol_config_t config, + Device* device, ProtocolListener* listener) { + return new DirectProtocolHandler(config, device, listener); +} + +void ProtocolHandler::clear() { + memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); + m_masterCount = 1; +} + +bool ProtocolHandler::addRequest(BusRequest* request, bool wait) { + m_nextRequests.push(request); + return !wait || m_finishedRequests.remove(request, true); +} + +result_t ProtocolHandler::sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave) { + if (!hasSignal()) { + return RESULT_ERR_NO_SIGNAL; // don't wait when there is no signal + } + result_t result = RESULT_ERR_NO_SIGNAL; + slave->clear(); + ActiveBusRequest request(master, slave); + logInfo(lf_bus, "send message: %s", master.getStr().c_str()); + + for (int sendRetries = m_config.failedSendRetries + 1; sendRetries > 0; sendRetries--) { + bool success = addRequest(&request, true); + result = success ? request.m_result : RESULT_ERR_TIMEOUT; + if (result == RESULT_OK) { + break; + } + if (!success || result == RESULT_ERR_NO_SIGNAL || result == RESULT_ERR_SEND || result == RESULT_ERR_DEVICE) { + logError(lf_bus, "send to %2.2x: %s, give up", master[1], getResultCode(result)); + break; + } + logError(lf_bus, "send to %2.2x: %s%s", master[1], getResultCode(result), sendRetries > 1 ? ", retry" : ""); + request.m_busLostRetries = 0; + } + return result; +} + +void ProtocolHandler::measureLatency(struct timespec* sentTime, struct timespec* recvTime) { + int64_t latencyLong = (recvTime->tv_sec*1000000000 + recvTime->tv_nsec + - sentTime->tv_sec*1000000000 - sentTime->tv_nsec)/1000000; + if (latencyLong < 0 || latencyLong > 1000) { + return; // clock skew or out of reasonable range + } + 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; + } + if (m_symbolLatencyMin == -1 || latency < m_symbolLatencyMin) { + m_symbolLatencyMin = latency; + } + if (m_symbolLatencyMax == -1 || latency > m_symbolLatencyMax) { + m_symbolLatencyMax = latency; + } + logInfo(lf_bus, "send/receive symbol latency %d - %d ms", m_symbolLatencyMin, m_symbolLatencyMax); +} + +bool ProtocolHandler::addSeenAddress(symbol_t address) { + if (!isValidAddress(address, false)) { + return false; + } + if (!isMaster(address)) { + if (!m_device->isReadOnly() && address == m_ownSlaveAddress) { + if (!m_addressConflict) { + m_addressConflict = true; + logError(lf_bus, "own slave address %2.2x is used by another participant", address); + } + } + if (!m_seenAddresses[address]) { + m_listener->notifyProtocolSeenAddress(address); + } + m_seenAddresses[address] = true; + address = getMasterAddress(address); + if (address == SYN) { + return false; + } + } + if (m_seenAddresses[address]) { + return false; + } + bool ret = false; + if (!m_device->isReadOnly() && address == m_ownMasterAddress) { + if (!m_addressConflict) { + m_addressConflict = true; + logError(lf_bus, "own master address %2.2x is used by another participant", address); + } + } else { + m_masterCount++; + ret = true; + logNotice(lf_bus, "new master %2.2x, master count %d", address, m_masterCount); + } + m_listener->notifyProtocolSeenAddress(address); + m_seenAddresses[address] = true; + return ret; +} + +} // namespace ebusd diff --git a/src/lib/ebus/protocol.h b/src/lib/ebus/protocol.h new file mode 100755 index 00000000..b7faa8e7 --- /dev/null +++ b/src/lib/ebus/protocol.h @@ -0,0 +1,477 @@ +/* + * ebusd - daemon for communication with eBUS heating systems. + * Copyright (C) 2014-2023 John Baier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIB_EBUS_PROTOCOL_H_ +#define LIB_EBUS_PROTOCOL_H_ + +#include "lib/ebus/symbol.h" +#include "lib/ebus/result.h" +#include "lib/ebus/device.h" +#include "lib/utils/queue.h" +#include "lib/utils/thread.h" + +namespace ebusd { + +/** @file lib/ebus/protocol.h + * Classes, functions, and constants related to handling the eBUS protocol. + */ + +/** the default time [ms] for retrieving a symbol from an addressed slave. */ +#define SLAVE_RECV_TIMEOUT 15 + +/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */ +#define SYN_TIMEOUT 51 + +/** 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_MICROS 4700 + +/** 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)) + + +/** settings for the eBUS protocol handler. */ +typedef struct ebus_protocol_config { + /** the own master address. */ + symbol_t ownAddress; + /** whether to answer queries for the own master/slave address. */ + bool answer; + /** the number of times a send is repeated due to lost arbitration. */ + unsigned int busLostRetries; + /** the number of times a failed send is repeated (other than lost arbitration). */ + unsigned int failedSendRetries; + /** the maximum time in milliseconds for bus acquisition. */ + unsigned int busAcquireTimeout; + /** the maximum time in milliseconds an addressed slave is expected to acknowledge. */ + unsigned int slaveRecvTimeout; + /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. */ + unsigned int lockCount; + /** whether to enable AUTO-SYN symbol generation. */ + bool generateSyn; +} ebus_protocol_config_t; + + +class ProtocolHandler; + +/** + * Generic request for sending to and receiving from the bus. + */ +class BusRequest { + friend class ProtocolHandler; + + public: + /** + * Constructor. + * @param master the master data @a MasterSymbolString to send. + * @param deleteOnFinish whether to automatically delete this @a BusRequest when finished. + */ + BusRequest(const MasterSymbolString& master, bool deleteOnFinish) + : m_master(master), m_busLostRetries(0), + m_deleteOnFinish(deleteOnFinish) {} + + /** + * Destructor. + */ + virtual ~BusRequest() {} + + /** + * @return the master data @a MasterSymbolString to send. + */ + const MasterSymbolString& getMaster() const { return m_master; } + + /** + * @return the number of times a send was repeated due to lost arbitration. + */ + unsigned int getBusLostRetries() const { return m_busLostRetries; } + + /** + * Increment the number of times a send was repeated due to lost arbitration. + */ + void incrementBusLostRetries() { m_busLostRetries++; } + + /** + * Reset the number of times a send was repeated due to lost arbitration. + */ + void resetBusLostRetries() { m_busLostRetries = 0; } + + /** + * @return whether to automatically delete this @a BusRequest when finished. + */ + bool deleteOnFinish() const { return m_deleteOnFinish; } + + /** + * Notify the request of the specified result. + * @param result the result of the request. + * @param slave the @a SlaveSymbolString received. + * @return true if the request needs to be restarted. + */ + virtual bool notify(result_t result, const SlaveSymbolString& slave) = 0; // abstract + + + protected: + /** the master data @a MasterSymbolString to send. */ + const MasterSymbolString& m_master; + + /** the number of times a send was repeated due to lost arbitration. */ + unsigned int m_busLostRetries; + + /** whether to automatically delete this @a BusRequest when finished. */ + const bool m_deleteOnFinish; +}; + + +/** + * An active @a BusRequest that can be waited for. + */ +class ActiveBusRequest : public BusRequest { + friend class ProtocolHandler; + + public: + /** + * Constructor. + * @param master the master data @a MasterSymbolString to send. + * @param slave reference to @a SlaveSymbolString for filling in the received slave data. + */ + ActiveBusRequest(const MasterSymbolString& master, SlaveSymbolString* slave) + : BusRequest(master, false), m_result(RESULT_ERR_NO_SIGNAL), m_slave(slave) {} + + /** + * Destructor. + */ + virtual ~ActiveBusRequest() {} + + // @copydoc + bool notify(result_t result, const SlaveSymbolString& slave) override; + + + private: + /** the result of handling the request. */ + result_t m_result; + + /** reference to @a SlaveSymbolString for filling in the received slave data. */ + SlaveSymbolString* m_slave; +}; + + +/** + * Interface for listening to eBUS protocol data. + */ +class ProtocolListener { + public: + /** + * Destructor. + */ + virtual ~ProtocolListener() {} + + /** + * Called to notify a status update from the protocol. + * @param signal true when signal is acquired, false otherwise. + */ + virtual void notifyProtocolStatus(bool signal) = 0; // abstract + + /** + * Called to notify a new valid seen address on the bus. + * @param address the seen address. + */ + virtual void notifyProtocolSeenAddress(symbol_t address) = 0; // abstract + + /** + * Listener method that is called when a message was sent or received. + * @param sent true when the master part was actively sent, false if the whole message + * was received only. + * @param master the @a MasterSymbolString received. + * @param slave the @a SlaveSymbolString received. + */ + virtual void notifyProtocolMessage(bool sent, const MasterSymbolString& master, + const SlaveSymbolString& slave) = 0; // abstract + + /** + * Listener method that is called when in answer mode and a message targeting ourself was received. + * @param master the @a MasterSymbolString received. + * @param slave the @a SlaveSymbolString for writing the response to. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t notifyProtocolAnswer(const MasterSymbolString& master, + SlaveSymbolString* slave) = 0; // abstract +}; + + + +/** + * Handles input from and output to eBUS with respect to the eBUS protocol. + */ +class ProtocolHandler : public WaitThread { + public: + /** + * Construct a new instance. + * @param config the configuration to use. + * @param device the @a Device instance for accessing the bus. + * @param listener the @a ProtocolListener. + */ + ProtocolHandler(const ebus_protocol_config_t config, + Device* device, ProtocolListener* listener) + : WaitThread(), m_config(config), m_device(device), m_listener(listener), + m_reconnect(false), + m_ownMasterAddress(config.ownAddress), m_ownSlaveAddress(getSlaveAddress(config.ownAddress)), + m_addressConflict(false), + m_masterCount(device->isReadOnly()?0:1), + m_symbolLatencyMin(-1), m_symbolLatencyMax(-1), m_arbitrationDelayMin(-1), + m_arbitrationDelayMax(-1), m_lastReceive(0), + m_symPerSec(0), m_maxSymPerSec(0) { + memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); + } + + /** + * Destructor. + */ + virtual ~ProtocolHandler() { + stop(); + join(); + BusRequest* req; + while ((req = m_finishedRequests.pop()) != nullptr) { + delete req; + } + while ((req = m_nextRequests.pop()) != nullptr) { + if (req->m_deleteOnFinish) { + delete req; + } + } + } + + /** + * Create a new instance. + * @param config the configuration to use. + * @param device the @a Device instance for accessing the bus. + * @param listener the @a ProtocolListener. + * @return the new ProtocolHandler, or @a nullptr on error. + */ + static ProtocolHandler* create(const ebus_protocol_config_t config, Device* device, ProtocolListener* listener); + + /** + * @return the own master address. + */ + symbol_t getOwnMasterAddress() const { return m_ownMasterAddress; } + + /** + * @return the own slave address. + */ + symbol_t getOwnSlaveAddress() const { return m_ownSlaveAddress; } + + /** + * @return @p true if answering queries for the own master/slave address (if not readonly). + */ + bool isAnswering() const { return m_config.answer; } + + /** + * @param address the address to check. + * @return @p true when the address is the own master or slave address (if not readonly). + */ + bool isOwnAddress(symbol_t address) const { + return !m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress); + } + + /** + * @param address the own address to check for conflict or @a SYN for any. + * @return @p true when an address conflict with any of the own addresses or the specified own address was detected. + */ + bool isAddressConflict(symbol_t address) const { + return m_addressConflict && (address == SYN || m_seenAddresses[address]); + } + + /** + * @return the maximum number of received symbols per second ever seen. + */ + unsigned int getMaxSymPerSec() const { return m_maxSymPerSec; } + + /** + * @return the @a Device instance for accessing the bus. + */ + const Device* getDevice() const { return m_device; } + + /** + * Clear stored values (e.g. scan results). + */ + virtual void clear(); + + /** + * Inject a message from outside and treat it as regularly retrieved from the bus. + * This may only be called before bus handling was actually started. + * @param master the @a MasterSymbolString with the master data. + * @param slave the @a SlaveSymbolString with the slave data. + */ + virtual void injectMessage(const MasterSymbolString& master, const SlaveSymbolString& slave) = 0; // abstract + + /** + * Add a @a BusRequest to the internal queue and optionally wait for it to complete. + * @param request the @a BusRequest to add. + * @param wait true to wait for it to complete, false to return immediately. + * @return true when it was not waited for or when it was completed. + */ + virtual bool addRequest(BusRequest* request, bool wait); + + /** + * Send a message on the bus and wait for the answer. + * @param master the @a MasterSymbolString with the master data to send. + * @param slave the @a SlaveSymbolString that will be filled with retrieved slave data. + * @return the result code. + */ + virtual result_t sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave); + + /** + * Main thread entry. + */ + virtual void run() = 0; // abstract + + /** + * Return true when a signal on the bus is available. + * @return true when a signal on the bus is available. + */ + virtual bool hasSignal() const = 0; // abstract + + /** + * Reconnect the device. + */ + virtual void reconnect() { m_reconnect = true; } + + /** + * Return the current symbol rate. + * @return the number of received symbols in the last second. + */ + unsigned int getSymbolRate() const { return m_symPerSec; } + + /** + * Return the maximum seen symbol rate. + * @return the maximum number of received symbols per second ever seen. + */ + unsigned int getMaxSymbolRate() const { return m_maxSymPerSec; } + + /** + * Return the minimal measured latency between send and receive of a symbol. + * @return the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. + */ + int getMinSymbolLatency() const { return m_symbolLatencyMin; } + + /** + * Return the maximal measured latency between send and receive of a symbol. + * @return the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. + */ + int getMaxSymbolLatency() const { return m_symbolLatencyMax; } + + /** + * Return the minimal measured delay between received SYN and sent own master address in microseconds. + * @return the minimal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known. + */ + int getMinArbitrationDelay() const { return m_arbitrationDelayMin; } + + /** + * Return the maximal measured delay between received SYN and sent own master address in microseconds. + * @return the maximal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known. + */ + int getMaxArbitrationDelay() const { return m_arbitrationDelayMax; } + + /** + * Return the number of masters already seen. + * @return the number of masters already seen (including ebusd itself). + */ + unsigned int getMasterCount() const { return m_masterCount; } + + + protected: + /** + * Called to measure the latency between send and receive of a symbol. + * @param sentTime the time the symbol was sent. + * @param recvTime the time the symbol was received. + */ + virtual void measureLatency(struct timespec* sentTime, struct timespec* recvTime); + + /** + * Add a seen bus address. + * @param address the seen bus address. + * @return true if a conflict with the own addresses was detected, false otherwise. + */ + virtual bool addSeenAddress(symbol_t address); + + /** the client configuration to use. */ + const ebus_protocol_config_t m_config; + + /** the @a Device instance for accessing the bus. */ + Device* m_device; + + /** the @a ProtocolListener. */ + ProtocolListener *m_listener; + + /** set to @p true when the device shall be reconnected. */ + bool m_reconnect; + + /** the own master address. */ + const symbol_t m_ownMasterAddress; + + /** the own slave address. */ + const symbol_t m_ownSlaveAddress; + + /** set to @p true once an address conflict with the own addresses was detected. */ + bool m_addressConflict; + + /** the number of masters already seen. */ + unsigned int m_masterCount; + + /** the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */ + int m_symbolLatencyMin; + + /** the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */ + int m_symbolLatencyMax; + + /** + * the minimal measured delay between received SYN and sent own master address in microseconds, + * -1 if not yet known. + */ + int m_arbitrationDelayMin; + + /** + * the maximal measured delay between received SYN and sent own master address in microseconds, + * -1 if not yet known. + */ + int m_arbitrationDelayMax; + + /** the time of the last received symbol, or 0 for never. */ + time_t m_lastReceive; + + /** the queue of @a BusRequests that shall be handled. */ + Queue m_nextRequests; + + /** the queue of @a BusRequests that are already finished. */ + Queue m_finishedRequests; + + /** the number of received symbols in the last second. */ + unsigned int m_symPerSec; + + /** the maximum number of received symbols per second ever seen. */ + unsigned int m_maxSymPerSec; + + /** the participating bus addresses seen so far. */ + bool m_seenAddresses[256]; +}; + +} // namespace ebusd + +#endif // LIB_EBUS_PROTOCOL_H_ diff --git a/src/lib/ebus/protocol_direct.cpp b/src/lib/ebus/protocol_direct.cpp new file mode 100644 index 00000000..1b170b92 --- /dev/null +++ b/src/lib/ebus/protocol_direct.cpp @@ -0,0 +1,762 @@ +/* + * ebusd - daemon for communication with eBUS heating systems. + * Copyright (C) 2014-2023 John Baier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "lib/ebus/protocol_direct.h" +#include "lib/utils/log.h" + +namespace ebusd { + +/** + * Return the string corresponding to the @a BusState. + * @param state the @a BusState. + * @return the string corresponding to the @a BusState. + */ +const char* getStateCode(BusState state) { + switch (state) { + case bs_noSignal: return "no signal"; + case bs_skip: return "skip"; + case bs_ready: return "ready"; + case bs_sendCmd: return "send command"; + case bs_recvCmdCrc: return "receive command CRC"; + case bs_recvCmdAck: return "receive command ACK"; + case bs_recvRes: return "receive response"; + case bs_recvResCrc: return "receive response CRC"; + case bs_sendResAck: return "send response ACK"; + case bs_recvCmd: return "receive command"; + case bs_recvResAck: return "receive response ACK"; + case bs_sendCmdCrc: return "send command CRC"; + case bs_sendCmdAck: return "send command ACK"; + case bs_sendRes: return "send response"; + case bs_sendResCrc: return "send response CRC"; + case bs_sendSyn: return "send SYN"; + default: return "unknown"; + } +} + + +void DirectProtocolHandler::run() { + unsigned int symCount = 0; + time_t now, lastTime; + time(&lastTime); + lastTime += 2; + logNotice(lf_bus, "bus started with own address %2.2x/%2.2x%s", m_ownMasterAddress, m_ownSlaveAddress, + m_config.answer?" in answer mode":""); + + do { + if (m_device->isValid() && !m_reconnect) { + result_t result = handleSymbol(); + time(&now); + if (result != RESULT_ERR_TIMEOUT && now >= lastTime) { + symCount++; + } + if (now > lastTime) { + m_symPerSec = symCount / (unsigned int)(now-lastTime); + if (m_symPerSec > m_maxSymPerSec) { + m_maxSymPerSec = m_symPerSec; + if (m_maxSymPerSec > 100) { + logNotice(lf_bus, "max. symbols per second: %d", m_maxSymPerSec); + } + } + lastTime = now; + symCount = 0; + } + } else { + if (!m_device->isValid()) { + logNotice(lf_bus, "device invalid"); + setState(bs_noSignal, RESULT_ERR_DEVICE); + } + if (!Wait(5)) { + break; + } + m_reconnect = false; + result_t result = m_device->open(); + if (result == RESULT_OK) { + logNotice(lf_bus, "re-opened %s", m_device->getName()); + } else { + logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); + setState(bs_noSignal, result); + } + symCount = 0; + m_symbolLatencyMin = m_symbolLatencyMax = m_arbitrationDelayMin = m_arbitrationDelayMax = -1; + time(&lastTime); + lastTime += 2; + } + } while (isRunning()); +} + +#ifndef FALLTHROUGH +#if defined(__GNUC__) && __GNUC__ >= 7 +#define FALLTHROUGH [[fallthrough]]; +#else +#define FALLTHROUGH +#endif +#endif + +result_t DirectProtocolHandler::handleSymbol() { + unsigned int timeout = SYN_TIMEOUT; + symbol_t sendSymbol = ESC; + bool sending = false; + + // 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 : SIGNAL_TIMEOUT; + break; + + case bs_skip: + timeout = SYN_TIMEOUT; + FALLTHROUGH + case bs_ready: + if (m_currentRequest != nullptr) { + setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up + } + if (!m_device->isArbitrating() && m_currentRequest == nullptr && m_remainLockCount == 0) { + BusRequest* startRequest = m_nextRequests.peek(); + if (startRequest != nullptr) { // initiate arbitration + symbol_t master = startRequest->getMaster()[0]; + logDebug(lf_bus, "start request %2.2x", master); + result_t ret = m_device->startArbitration(master); + if (ret == RESULT_OK) { + logDebug(lf_bus, "arbitration start with %2.2x", master); + } 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 + } + } + } + break; + + case bs_recvCmd: + case bs_recvCmdCrc: + timeout = m_config.slaveRecvTimeout; + break; + + case bs_recvCmdAck: + timeout = m_config.slaveRecvTimeout; + break; + + case bs_recvRes: + case bs_recvResCrc: + if (m_response.size() > 0 || m_config.slaveRecvTimeout > SYN_TIMEOUT) { + timeout = m_config.slaveRecvTimeout; + } else { + timeout = SYN_TIMEOUT; + } + break; + + case bs_recvResAck: + timeout = m_config.slaveRecvTimeout; + break; + + case bs_sendCmd: + if (m_currentRequest != nullptr) { + sendSymbol = m_currentRequest->getMaster()[m_nextSendPos]; // unescaped command + sending = true; + } + break; + + case bs_sendCmdCrc: + if (m_currentRequest != nullptr) { + sendSymbol = m_crc; + sending = true; + } + break; + + case bs_sendResAck: + if (m_currentRequest != nullptr) { + sendSymbol = m_crcValid ? ACK : NAK; + sending = true; + } + break; + + case bs_sendCmdAck: + if (m_config.answer) { + sendSymbol = m_crcValid ? ACK : NAK; + sending = true; + } + break; + + case bs_sendRes: + if (m_config.answer) { + sendSymbol = m_response[m_nextSendPos]; // unescaped response + sending = true; + } + break; + + case bs_sendResCrc: + if (m_config.answer) { + sendSymbol = m_crc; + sending = true; + } + break; + + case bs_sendSyn: + sendSymbol = SYN; + sending = true; + break; + } + + // send symbol if necessary + result_t result; + struct timespec sentTime, recvTime; + if (sending) { + if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) { + if (m_escape) { + sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01); + } else { + m_escape = sendSymbol; + sendSymbol = ESC; + } + } + result = m_device->send(sendSymbol); + clockGettime(&sentTime); + if (result == RESULT_OK) { + if (m_state == bs_ready) { + timeout = m_config.busAcquireTimeout; + } else { + timeout = SEND_TIMEOUT; + } + } else { + sending = false; + 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) + symbol_t recvSymbol; + ArbitrationState arbitrationState = as_none; + result = m_device->recv(timeout, &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)) { + // check if acting as AUTO-SYN generator is required + result = m_device->send(SYN); + if (result != RESULT_OK) { + return setState(bs_skip, result); + } + clockGettime(&sentTime); + recvSymbol = ESC; + 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)); + 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; + setState(bs_ready, RESULT_OK); + } + switch (arbitrationState) { + case as_lost: + case as_timeout: + logDebug(lf_bus, arbitrationState == as_lost ? "arbitration lost" : "arbitration lost (timed out)"); + 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); + 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->getMaster()[0]; + sending = true; + } + } + break; + case as_running: + break; + case as_error: + 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; + } + if (sentAutoSyn && !sending) { + return RESULT_OK; + } + time_t now; + time(&now); + if (result != RESULT_OK) { + 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) { + return setState(bs_noSignal, result); + } + return setState(bs_skip, result); + } + + m_lastReceive = now; + if ((recvSymbol == SYN) && (m_state != bs_sendSyn)) { + if (!sending && m_remainLockCount > 0 && m_command.size() != 1) { + m_remainLockCount--; + } else if (!sending && m_remainLockCount == 0 && m_command.size() == 1) { + m_remainLockCount = 1; // wait for next AUTO-SYN after SYN / address / SYN (bus locked for own priority) + } + clockGettime(&m_lastSynReceiveTime); + return setState(bs_ready, m_state == bs_skip ? RESULT_OK : RESULT_ERR_SYN); + } + + if (sending && m_state != bs_ready) { // check received symbol for equality if not in arbitration + if (recvSymbol != sendSymbol) { + return setState(bs_skip, RESULT_ERR_SYMBOL); + } + measureLatency(&sentTime, &recvTime); + } + + switch (m_state) { + case bs_ready: + case bs_recvCmd: + case bs_recvRes: + case bs_sendCmd: + case bs_sendRes: + SymbolString::updateCrc(recvSymbol, &m_crc); + break; + default: + break; + } + + if (m_escape) { + // check escape/unescape state + if (sending) { + if (sendSymbol == ESC) { + return RESULT_OK; + } + sendSymbol = recvSymbol = m_escape; + } else { + if (recvSymbol > 0x01) { + return setState(bs_skip, RESULT_ERR_ESC); + } + recvSymbol = recvSymbol == 0x00 ? ESC : SYN; + } + m_escape = 0; + } else if (!sending && recvSymbol == ESC) { + m_escape = ESC; + return RESULT_OK; + } + + switch (m_state) { + case bs_noSignal: + return setState(bs_skip, RESULT_OK); + + case bs_skip: + return RESULT_OK; + + case bs_ready: + if (m_currentRequest != nullptr && sending) { + // check arbitration + if (recvSymbol == sendSymbol) { // arbitration successful + // measure arbitration delay + int64_t 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 + 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) { + m_arbitrationDelayMin = latency; + } + if (m_arbitrationDelayMax == -1 || latency > m_arbitrationDelayMax) { + m_arbitrationDelayMax = latency; + } + logInfo(lf_bus, "arbitration delay %d - %d micros", m_arbitrationDelayMin, m_arbitrationDelayMax); + } + } + m_nextSendPos = 1; + m_repeat = false; + return setState(bs_sendCmd, RESULT_OK); + } + // arbitration lost. if same priority class found, try again after next AUTO-SYN + m_remainLockCount = isMaster(recvSymbol) ? 2 : 1; // number of SYN to wait for before next send try + if ((recvSymbol & 0x0f) != (sendSymbol & 0x0f) && m_lockCount > m_remainLockCount) { + // if different priority class found, try again after N AUTO-SYN symbols (at least next AUTO-SYN) + m_remainLockCount = m_lockCount; + } + setState(m_state, RESULT_ERR_BUS_LOST); // try again later + } + m_command.push_back(recvSymbol); + m_repeat = false; + return setState(bs_recvCmd, RESULT_OK); + + case bs_recvCmd: + m_command.push_back(recvSymbol); + if (m_command.isComplete()) { // all data received + return setState(bs_recvCmdCrc, RESULT_OK); + } + return RESULT_OK; + + case bs_recvCmdCrc: + m_crcValid = recvSymbol == m_crc; + if (m_command[1] == BROADCAST) { + if (m_crcValid) { + addSeenAddress(m_command[0]); + messageCompleted(); + return setState(bs_skip, RESULT_OK); + } + return setState(bs_skip, RESULT_ERR_CRC); + } + if (m_config.answer) { + symbol_t dstAddress = m_command[1]; + if (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress) { + if (m_crcValid) { + addSeenAddress(m_command[0]); + m_currentAnswering = true; + return setState(bs_sendCmdAck, RESULT_OK); + } + return setState(bs_sendCmdAck, RESULT_ERR_CRC); + } + } + if (m_crcValid) { + addSeenAddress(m_command[0]); + return setState(bs_recvCmdAck, RESULT_OK); + } + if (m_repeat) { + return setState(bs_skip, RESULT_ERR_CRC); + } + return setState(bs_recvCmdAck, RESULT_ERR_CRC); + + case bs_recvCmdAck: + if (recvSymbol == ACK) { + if (!m_crcValid) { + return setState(bs_skip, RESULT_ERR_ACK); + } + if (m_currentRequest != nullptr) { + if (isMaster(m_currentRequest->getMaster()[1])) { + messageCompleted(); + return setState(bs_sendSyn, RESULT_OK); + } + } else if (isMaster(m_command[1])) { + messageCompleted(); + return setState(bs_skip, RESULT_OK); + } + + m_repeat = false; + return setState(bs_recvRes, RESULT_OK); + } + if (recvSymbol == NAK) { + if (!m_repeat) { + m_repeat = true; + m_crc = 0; + m_nextSendPos = 0; + m_command.clear(); + if (m_currentRequest != nullptr) { + return setState(bs_sendCmd, RESULT_ERR_NAK, true); + } + return setState(bs_recvCmd, RESULT_ERR_NAK); + } + return setState(bs_skip, RESULT_ERR_NAK); + } + return setState(bs_skip, RESULT_ERR_ACK); + + case bs_recvRes: + m_response.push_back(recvSymbol); + if (m_response.isComplete()) { // all data received + return setState(bs_recvResCrc, RESULT_OK); + } + return RESULT_OK; + + case bs_recvResCrc: + m_crcValid = recvSymbol == m_crc; + if (m_crcValid) { + if (m_currentRequest != nullptr) { + return setState(bs_sendResAck, RESULT_OK); + } + return setState(bs_recvResAck, RESULT_OK); + } + if (m_repeat) { + if (m_currentRequest != nullptr) { + return setState(bs_sendSyn, RESULT_ERR_CRC); + } + return setState(bs_skip, RESULT_ERR_CRC); + } + if (m_currentRequest != nullptr) { + return setState(bs_sendResAck, RESULT_ERR_CRC); + } + return setState(bs_recvResAck, RESULT_ERR_CRC); + + case bs_recvResAck: + if (recvSymbol == ACK) { + if (!m_crcValid) { + return setState(bs_skip, RESULT_ERR_ACK); + } + messageCompleted(); + return setState(bs_skip, RESULT_OK); + } + if (recvSymbol == NAK) { + if (!m_repeat) { + m_repeat = true; + if (m_currentAnswering) { + m_nextSendPos = 0; + return setState(bs_sendRes, RESULT_ERR_NAK, true); + } + m_response.clear(); + return setState(bs_recvRes, RESULT_ERR_NAK, true); + } + return setState(bs_skip, RESULT_ERR_NAK); + } + return setState(bs_skip, RESULT_ERR_ACK); + + case bs_sendCmd: + if (!sending || m_currentRequest == nullptr) { + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + } + m_nextSendPos++; + if (m_nextSendPos >= m_currentRequest->getMaster().size()) { + return setState(bs_sendCmdCrc, RESULT_OK); + } + return RESULT_OK; + + case bs_sendCmdCrc: + if (m_currentRequest->getMaster()[1] == BROADCAST) { + messageCompleted(); + return setState(bs_sendSyn, RESULT_OK); + } + m_crcValid = true; + return setState(bs_recvCmdAck, RESULT_OK); + + case bs_sendResAck: + if (!sending || m_currentRequest == nullptr) { + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + } + if (!m_crcValid) { + if (!m_repeat) { + m_repeat = true; + m_response.clear(); + return setState(bs_recvRes, RESULT_ERR_NAK, true); + } + return setState(bs_sendSyn, RESULT_ERR_ACK); + } + messageCompleted(); + return setState(bs_sendSyn, RESULT_OK); + + case bs_sendCmdAck: + if (!sending || !m_config.answer) { + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + } + if (!m_crcValid) { + if (!m_repeat) { + m_repeat = true; + m_crc = 0; + m_command.clear(); + return setState(bs_recvCmd, RESULT_ERR_NAK, true); + } + return setState(bs_skip, RESULT_ERR_ACK); + } + if (isMaster(m_command[1])) { + messageCompleted(); // TODO decode command and store value into database of internal variables + return setState(bs_skip, RESULT_OK); + } + + m_nextSendPos = 0; + m_repeat = false; + // build response and store in m_response for sending back to requesting master + m_response.clear(); + result = m_listener->notifyProtocolAnswer(m_command, &m_response); + if (result != RESULT_OK) { + return setState(bs_skip, result); + } + return setState(bs_sendRes, RESULT_OK); + + case bs_sendRes: + if (!sending || !m_config.answer) { + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + } + m_nextSendPos++; + if (m_nextSendPos >= m_response.size()) { + // slave data completely sent + return setState(bs_sendResCrc, RESULT_OK); + } + return RESULT_OK; + + case bs_sendResCrc: + if (!sending || !m_config.answer) { + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + } + return setState(bs_recvResAck, RESULT_OK); + + case bs_sendSyn: + if (!sending) { + return setState(bs_ready, RESULT_ERR_INVALID_ARG); + } + return setState(bs_ready, RESULT_OK); + } + return RESULT_OK; +} + +result_t DirectProtocolHandler::setState(BusState state, result_t result, bool firstRepetition) { + if (m_currentRequest != nullptr) { + if (result == RESULT_ERR_BUS_LOST && m_currentRequest->getBusLostRetries() < m_config.busLostRetries) { + logDebug(lf_bus, "%s during %s, retry", getResultCode(result), getStateCode(m_state)); + m_currentRequest->incrementBusLostRetries(); + m_nextRequests.push(m_currentRequest); // repeat + m_currentRequest = nullptr; + } else if (state == bs_sendSyn || (result != RESULT_OK && !firstRepetition)) { + logDebug(lf_bus, "notify request: %s", getResultCode(result)); + bool restart = m_currentRequest->notify( + result == RESULT_ERR_SYN && (m_state == bs_recvCmdAck || m_state == bs_recvRes) + ? RESULT_ERR_TIMEOUT : result, m_response); + if (restart) { + m_currentRequest->resetBusLostRetries(); + m_nextRequests.push(m_currentRequest); + } else if (m_currentRequest->deleteOnFinish()) { + delete m_currentRequest; + } else { + m_finishedRequests.push(m_currentRequest); + } + m_currentRequest = nullptr; + } + if (state == bs_skip) { + m_device->startArbitration(SYN); // reset arbitration state + } + } + + if (state == bs_noSignal) { // notify all requests + if (m_state != bs_noSignal) { + m_listener->notifyProtocolStatus(false); + } + m_response.clear(); // notify with empty response + while ((m_currentRequest = m_nextRequests.pop()) != nullptr) { + bool restart = m_currentRequest->notify(RESULT_ERR_NO_SIGNAL, m_response); + if (restart) { // should not occur with no signal + m_currentRequest->resetBusLostRetries(); + m_nextRequests.push(m_currentRequest); + } else if (m_currentRequest->deleteOnFinish()) { + delete m_currentRequest; + } else { + m_finishedRequests.push(m_currentRequest); + } + } + } else if (m_state == bs_noSignal) { + m_listener->notifyProtocolStatus(true); + } + + m_escape = 0; + if (state == m_state) { + return result; + } + if ((result < RESULT_OK && !(result == RESULT_ERR_TIMEOUT && state == bs_skip && m_state == bs_ready)) + || (result != RESULT_OK && state == bs_skip && m_state != bs_ready)) { + logDebug(lf_bus, "%s during %s, switching to %s", getResultCode(result), getStateCode(m_state), + getStateCode(state)); + } else if (m_currentRequest != nullptr || state == bs_sendCmd || state == bs_sendCmdCrc || state == bs_sendCmdAck + || state == bs_sendRes || state == bs_sendResCrc || state == bs_sendResAck || state == bs_sendSyn + || m_state == bs_sendSyn) { + logDebug(lf_bus, "switching from %s to %s", getStateCode(m_state), getStateCode(state)); + } + if (state == bs_noSignal) { + if (m_generateSynInterval == 0 || m_state != bs_skip) { + logError(lf_bus, "signal lost"); + } + } else if (m_state == bs_noSignal) { + if (m_generateSynInterval == 0 || state != bs_skip) { + logNotice(lf_bus, "signal acquired"); + } + } + m_state = state; + + if (state == bs_ready || state == bs_skip) { + m_command.clear(); + m_crc = 0; + m_crcValid = false; + m_response.clear(); + m_nextSendPos = 0; + m_currentAnswering = false; + } else if (state == bs_recvRes || state == bs_sendRes) { + m_crc = 0; + } + return result; +} + +bool DirectProtocolHandler::addSeenAddress(symbol_t address) { + if (!ProtocolHandler::addSeenAddress(address)) { + return false; + } + if (m_config.lockCount == 0 && m_masterCount > m_lockCount) { + m_lockCount = m_masterCount; + } + return true; +} + +void DirectProtocolHandler::messageCompleted() { + const char* prefix = m_currentRequest ? "sent" : "received"; + // do an explicit copy here in case being called by another thread + const MasterSymbolString command(m_currentRequest ? m_currentRequest->getMaster() : m_command); + const SlaveSymbolString response(m_response); + symbol_t srcAddress = command[0], dstAddress = command[1]; + if (srcAddress == dstAddress) { + logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress); + return; + } + if (!m_currentAnswering) { + addSeenAddress(dstAddress); + } + + bool master = isMaster(dstAddress); + if (dstAddress == BROADCAST || master) { + logInfo(lf_update, "%s %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str()); + } else { + logInfo(lf_update, "%s MS cmd: %s / %s", prefix, command.getStr().c_str(), response.getStr().c_str()); + } + m_listener->notifyProtocolMessage(m_currentRequest != nullptr, command, response); +} + +} // namespace ebusd diff --git a/src/lib/ebus/protocol_direct.h b/src/lib/ebus/protocol_direct.h new file mode 100755 index 00000000..eb185b11 --- /dev/null +++ b/src/lib/ebus/protocol_direct.h @@ -0,0 +1,180 @@ +/* + * ebusd - daemon for communication with eBUS heating systems. + * Copyright (C) 2014-2023 John Baier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIB_EBUS_PROTOCOL_DIRECT_H_ +#define LIB_EBUS_PROTOCOL_DIRECT_H_ + +#include "lib/ebus/protocol.h" + +namespace ebusd { + +/** @file lib/ebus/protocol_direct.h + * Implementation of directly handled eBUS protocol. + * + * The following table shows the possible states, symbols, and state transition + * depending on the kind of message to send/receive: + * @image html states.png "ebusd direct ProtocolHandler states" + */ + +/** the possible bus states. */ +enum BusState { + bs_noSignal, //!< no signal on the bus + bs_skip, //!< skip all symbols until next @a SYN + bs_ready, //!< ready for next master (after @a SYN symbol, send/receive QQ) + bs_recvCmd, //!< receive command (ZZ, PBSB, master data) [passive set] + bs_recvCmdCrc, //!< receive command CRC [passive set] + bs_recvCmdAck, //!< receive command ACK/NACK [passive set + active set+get] + bs_recvRes, //!< receive response (slave data) [passive set + active get] + bs_recvResCrc, //!< receive response CRC [passive set + active get] + bs_recvResAck, //!< receive response ACK/NACK [passive set] + bs_sendCmd, //!< send command (ZZ, PBSB, master data) [active set+get] + bs_sendCmdCrc, //!< send command CRC [active set+get] + bs_sendResAck, //!< send response ACK/NACK [active get] + bs_sendCmdAck, //!< send command ACK/NACK [passive get] + bs_sendRes, //!< send response (slave data) [passive get] + bs_sendResCrc, //!< send response CRC [passive get] + bs_sendSyn, //!< send SYN for completed transfer [active set+get] +}; + + + +/** + * Directly handles input from and output to eBUS with respect to the eBUS protocol. + */ +class DirectProtocolHandler : public ProtocolHandler { + public: + /** + * Construct a new instance. + * @param config the configuration to use. + * @param device the @a Device instance for accessing the bus. + * @param listener the @a ProtocolListener. + */ + DirectProtocolHandler(const ebus_protocol_config_t config, + Device* device, ProtocolListener* listener) + : ProtocolHandler(config, device, listener), + m_lockCount(config.lockCount <= 3 ? 3 : config.lockCount), + m_remainLockCount(config.lockCount == 0 ? 1 : 0), + m_generateSynInterval(config.generateSyn ? SYN_TIMEOUT*getMasterNumber(config.ownAddress)+SYMBOL_DURATION : 0), + m_currentRequest(nullptr), m_currentAnswering(false), m_nextSendPos(0), + m_state(bs_noSignal), m_escape(0), m_crc(0), m_crcValid(false), m_repeat(false) { + m_lastSynReceiveTime.tv_sec = 0; + m_lastSynReceiveTime.tv_nsec = 0; + } + + /** + * Destructor. + */ + virtual ~DirectProtocolHandler() { + if (m_currentRequest != nullptr) { + delete m_currentRequest; + m_currentRequest = nullptr; + } + } + + // @copydoc + void injectMessage(const MasterSymbolString& master, const SlaveSymbolString& slave) override { + if (isRunning()) { + return; + } + m_command = master; + m_response = slave; + m_addressConflict = true; // avoid conflict messages + messageCompleted(); + m_addressConflict = false; + } + + /** + * Main thread entry. + */ + virtual void run(); + + // @copydoc + bool hasSignal() const override { return m_state != bs_noSignal; } + + + private: + /** + * Handle the next symbol on the bus. + * @return RESULT_OK on success, or an error code. + */ + result_t handleSymbol(); + + /** + * Set a new @a BusState and add a log message if necessary. + * @param state the new @a BusState. + * @param result the result code. + * @param firstRepetition true if the first repetition of a message part is being started. + * @return the result code. + */ + result_t setState(BusState state, result_t result, bool firstRepetition = false); + + // @copydoc + bool addSeenAddress(symbol_t address) override; + + /** + * Called when a message sending or reception was successfully completed. + */ + void messageCompleted(); + + /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */ + unsigned int m_lockCount; + + /** the remaining number of AUTO-SYN symbols before sending is allowed again. */ + unsigned int m_remainLockCount; + + /** the interval in milliseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */ + unsigned int m_generateSynInterval; + + /** the time of the last received SYN symbol, or 0 for never. */ + struct timespec m_lastSynReceiveTime; + + /** the currently handled BusRequest, or nullptr. */ + BusRequest* m_currentRequest; + + /** whether currently answering a request from another participant. */ + bool m_currentAnswering; + + /** the offset of the next symbol that needs to be sent from the command or response, + * (only relevant if m_request is set and state is @a bs_command or @a bs_response). */ + size_t m_nextSendPos; + + /** the current @a BusState. */ + BusState m_state; + + /** 0 when not escaping/unescaping, or @a ESC when receiving, or the original value when sending. */ + symbol_t m_escape; + + /** the calculated CRC. */ + symbol_t m_crc; + + /** whether the CRC matched. */ + bool m_crcValid; + + /** whether the current message part is being repeated. */ + bool m_repeat; + + /** the received command @a MasterSymbolString. */ + MasterSymbolString m_command; + + /** the received response @a SlaveSymbolString or response to send. */ + SlaveSymbolString m_response; +}; + +} // namespace ebusd + +#endif // LIB_EBUS_PROTOCOL_DIRECT_H_ diff --git a/src/ebusd/states.png b/src/lib/ebus/states.png similarity index 100% rename from src/ebusd/states.png rename to src/lib/ebus/states.png