diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 88edb0cf..20757f4f 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -32,9 +32,6 @@ using std::setfill; using std::setw; using std::endl; -// the string used for answering to a scan request (07h 04h) -#define SCAN_ANSWER ("ebusd.eu;" PACKAGE_NAME ";" SCAN_VERSION ";100") - result_t PollRequest::prepare(symbol_t ownMasterAddress) { istringstream input; @@ -378,33 +375,11 @@ void BusHandler::notifyProtocolStatus(ProtocolState state, result_t result) { } } -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 (message == nullptr || message->isWrite()) { - // don't know this request or definition has wrong direction, deny - return RESULT_ERR_INVALID_ARG; - } - 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); - } - // build response and store in m_response for sending back to requesting master - return message->prepareSlave(&input, response); -} - - void BusHandler::notifyProtocolSeenAddress(symbol_t address) { m_seenAddresses[address] |= SEEN; } -void BusHandler::notifyProtocolMessage(bool sent, const MasterSymbolString& command, +void BusHandler::notifyProtocolMessage(MessageDirection direction, const MasterSymbolString& command, const SlaveSymbolString& response) { symbol_t srcAddress = command[0], dstAddress = command[1]; bool master = isMaster(dstAddress); @@ -465,7 +440,21 @@ void BusHandler::notifyProtocolMessage(bool sent, const MasterSymbolString& comm } m_grabbedMessages[key].setLastData(command, response); } - const char* prefix = sent ? "sent" : "received"; + if (direction == md_answer) { + size_t idLen = command.getDataSize(); + if (master && idLen >= response.size()) { + // build MS auto-answer from MM with same ID + SlaveSymbolString answer; + answer.push_back(0); // room for length + idLen -= response.size(); + for (size_t pos = idLen; pos < response.size(); pos++) { + answer.push_back(command.dataAt(pos)); + } + m_protocol->setAnswer(SYN, command[1], command[2], command[3], command.data() + 5, idLen, answer); + // TODO could use loaded messages for identifying MM/MS message pair + } + } + const char* prefix = direction == md_answer ? "answered" : direction == md_send ? "sent" : "received"; if (message == nullptr) { if (dstAddress == BROADCAST || master) { logNotice(lf_update, "%s unknown %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str()); @@ -493,7 +482,7 @@ void BusHandler::notifyProtocolMessage(bool sent, const MasterSymbolString& comm string data = output.str(); 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 + srcAddress, data.c_str()); } else if (message->getDstAddress() == SYN) { // any destination if (message->getSrcAddress() == SYN) { // any destination and any source logNotice(lf_update, "%s %s %s %s QQ=%2.2x ZZ=%2.2x: %s", prefix, mode, circuit.c_str(), name.c_str(), @@ -676,12 +665,12 @@ void BusHandler::formatSeenInfo(ostringstream* output) const { } if (ownAddress) { *output << ", ebusd"; - if (m_protocol->isAnswering()) { - *output << " (answering)"; - } - if (m_protocol->isAddressConflict(address)) { - *output << ", conflict"; - } + } + if (m_protocol->hasAnswer(address)) { + *output << " (answering)"; + } + if (ownAddress && m_protocol->isAddressConflict(address)) { + *output << ", conflict"; } if ((m_seenAddresses[address]&SCAN_DONE) != 0) { *output << ", scanned"; diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index c7cda7c9..ddf253ba 100755 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -398,14 +398,12 @@ class BusHandler : public ProtocolListener { // @copydoc void notifyProtocolStatus(ProtocolState state, result_t result) 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; + void notifyProtocolMessage(MessageDirection direction, const MasterSymbolString& master, + const SlaveSymbolString& slave) override; private: /** diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 9d39b8a7..10f04419 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -415,6 +415,16 @@ int main(int argc, char* argv[], char* envp[]) { return EINVAL; } s_busHandler->setProtocol(s_protocol); + if (s_opt.answer) { + istringstream input; + input.str("ebusd.eu;" PACKAGE_NAME ";" SCAN_VERSION ";100"); + Message* message = s_messageMap->getScanMessage(); + SlaveSymbolString response; + if (message && message->prepareSlave(&input, &response) == RESULT_OK) { + s_protocol->setAnswer(SYN, s_protocol->getOwnSlaveAddress(), message->getPrimaryCommand(), + message->getSecondaryCommand(), nullptr, 0, response); + } + } if (!s_opt.foreground) { if (!setLogFile(s_opt.logFile)) { diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index b0bd5a30..6854fbf5 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -484,6 +484,13 @@ result_t MainLoop::decodeRequest(Request* req, bool* connected, RequestMode* req *ostream << "ERR: command not enabled"; return RESULT_OK; } + if (cmd == "ANSWER") { + if (m_enableHex && !m_protocol->isReadOnly()) { + return executeAnswer(args, ostream); + } + *ostream << "ERR: command not enabled"; + return RESULT_OK; + } if (cmd == "F" || cmd == "FIND") { return executeFind(args, getUserLevels(*user), ostream); } @@ -1170,7 +1177,7 @@ result_t MainLoop::executeInject(const vector& args, ostringstream* ostr if (!m_scanHelper->parseMessage(args[argPos++], false, &master, &slave)) { return RESULT_ERR_INVALID_ARG; } - m_busHandler->notifyProtocolMessage(false, master, slave); + m_busHandler->notifyProtocolMessage(md_recv, master, slave); return RESULT_OK; } *ostream << "usage: inject QQZZPBSBNN[DD]*/[NN[DD]*]\n" @@ -1183,6 +1190,84 @@ result_t MainLoop::executeInject(const vector& args, ostringstream* ostr return RESULT_OK; } +result_t MainLoop::executeAnswer(const vector& args, ostringstream* ostream) { + size_t argPos = 1; + symbol_t srcAddress = SYN; + symbol_t dstAddress = SYN; + bool master = false; + while (args.size() > argPos && args[argPos][0] == '-') { + if (args[argPos] == "-s" && argPos + 1 < args.size()) { + result_t ret; + argPos++; + symbol_t address = (symbol_t)parseInt(args[argPos].c_str(), 16, 0, 0xff, &ret); + if (ret != RESULT_OK || !isValidAddress(address, false) || !isMaster(address)) { + return RESULT_ERR_INVALID_ADDR; + } + srcAddress = address; + } else if (args[argPos] == "-d" && argPos + 1 < args.size()) { + result_t ret; + argPos++; + symbol_t address = (symbol_t)parseInt(args[argPos].c_str(), 16, 0, 0xff, &ret); + if (ret != RESULT_OK || !isValidAddress(address)) { + return RESULT_ERR_INVALID_ADDR; + } + dstAddress = address; + } else if (args[argPos] == "-m") { + master = true; + } else { + argPos = 0; // print usage + break; + } + argPos++; + } + MasterSymbolString id; + if (argPos > 0 && argPos < args.size()) { + result_t ret = id.parseHex(args[argPos++]); + if (ret != RESULT_OK) { + return ret; + } + if (id.size() < 2 || id.size() > 6) { + return RESULT_ERR_INVALID_POS; + } + } + SlaveSymbolString answer; + if (argPos > 0 && argPos < args.size()) { + answer.push_back(0); // room for length byte + result_t ret = answer.parseHex(args[argPos++]); + if (ret != RESULT_OK) { + return ret; + } + if (answer.size() > 16) { + return RESULT_ERR_INVALID_POS; + } + answer[0] = (symbol_t)(answer.size()-1); + } + if (argPos < args.size()) { + argPos = 0; // print usage + } + if (argPos <= 1) { + *ostream << "usage: answer [-m] [-s QQ] [-d ZZ] PBSB[ID]* [DD]*\n" + " Answer to a message from the bus.\n" + " -m destination is a master\n" + " -s QQ source address to limit to\n" + " -d ZZ override destination address (instead of own address)\n" + " PB SB primary/secondary command byte\n" + " ID further ID bytes\n" + " DD data bytes (only length used with -m)"; + return RESULT_OK; + } + if (isMaster(dstAddress)) { + master = true; + } else if (dstAddress == SYN) { + dstAddress = master ? m_address : getSlaveAddress(m_address); + } + if (!m_protocol->setAnswer(srcAddress, dstAddress, id[0], id[1], id.data()+2 + , id.size()-2, answer)) { + return RESULT_ERR_INVALID_ARG; + } + return RESULT_OK; +} + result_t MainLoop::executeDirect(const vector& args, RequestMode* reqMode, ostringstream* ostream) { if (reqMode->listenMode != lm_direct) { if (args.size() == 1) { @@ -1915,12 +2000,13 @@ result_t MainLoop::executeHelp(ostringstream* ostream) { " listen|l Listen for updates: listen [-v|-V] [-n|-N] [-u|-U] [stop]\n" " hex Send hex data: hex [-s QQ] [-n] ZZPBSB[NN][DD]* (if enabled)\n" " inject Inject hex data: inject QQZZPBSBNN[DD]*/[NN[DD]*] (if enabled)\n" + " answer Answer a message: answer [-m] [-s QQ] [-d ZZ] PBSB[ID]* [DD]* (if enabled)\n" " direct Enter direct mode\n" " state|s Report bus state\n" " info|i Report information about the daemon, configuration, seen participants, and the device.\n" " grab|g Grab messages: grab [stop]\n" " Report the messages: grab result [all|decode]\n" - " define Define new message: define [-r] DEFINITION\n" + " define Define new message: define [-r] DEFINITION (if enabled)\n" " decode|d Decode field(s): decode [-v|-V] [-n|-N] DEFINITION DD[DD]*\n" " encode|e Encode field(s): encode DEFINITION VALUE[;VALUE]*\n" " scan Scan slaves: scan [full|ZZ]\n" diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 8b1bd281..599df1e7 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -224,6 +224,15 @@ class MainLoop : public Thread { */ result_t executeDirect(const vector& args, RequestMode* reqMode, ostringstream* ostream); + /** + * Execute the answer command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param reqMode the @a RequestMode to use and update. + * @param ostream the @a ostringstream to format the result string to. + * @return the result code. + */ + result_t executeAnswer(const vector& args, ostringstream* ostream); + /** * Execute the find command. * @param args the arguments passed to the command (starting with the command itself), or empty for help. @@ -403,7 +412,7 @@ class MainLoop : public Thread { /** true when the poll interval is non zero. */ const bool m_polling; - /** whether to enable the hex command. */ + /** whether to enable the hex, inject, and answer commands. */ const bool m_enableHex; /** the MessageMap for handling newly defined messages for testing (if enabled), or nullptr. */ diff --git a/src/lib/ebus/protocol.h b/src/lib/ebus/protocol.h index 08bb4926..f5fbdcec 100755 --- a/src/lib/ebus/protocol.h +++ b/src/lib/ebus/protocol.h @@ -204,6 +204,13 @@ class ActiveBusRequest : public BusRequest { }; +/** the possible message directions. */ +enum MessageDirection { + md_recv, //!< message received from bus + md_send, //!< message sent to bus + md_answer, //!< answered to a message received from bus +}; + /** * Interface for listening to eBUS protocol data. */ @@ -229,22 +236,13 @@ class ProtocolListener { /** * 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. + * @param direction the message direction. + * @param master the @a MasterSymbolString received/sent. + * @param slave the @a SlaveSymbolString received/sent or the answer passed to @a ProtocolHandler::setAnswer() with + * the the length of the data part following the ID as master. */ - virtual void notifyProtocolMessage(bool sent, const MasterSymbolString& master, + virtual void notifyProtocolMessage(MessageDirection direction, 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 }; @@ -350,9 +348,28 @@ class ProtocolHandler : public WaitThread, public DeviceListener { symbol_t getOwnSlaveAddress() const { return m_ownSlaveAddress; } /** - * @return @p true if answering queries for the own master/slave address (if not readonly). + * @return @p true if answering queries (if not readonly). */ - bool isAnswering() const { return m_config.answer; } + virtual bool isAnswering() const { return false; } + + /** + * Add a message to be answered. + * @param srcAddress the source address to limit to, or @a SYN for any. + * @param dstAddress the destination address (either master or slave address). + * @param pb the primary ID byte. + * @param sb the secondary ID byte. + * @param id optional further ID bytes. + * @param idLen the length of the further ID bytes (maximum 4). + * @param answer the sequence to respond when addressed as slave or the length of the data part following the ID as master. + * @return @p true on success, @p false on error (e.g. invalid address, read only, or too long id). + */ + virtual bool setAnswer(symbol_t srcAddress, symbol_t dstAddress, symbol_t pb, symbol_t sb, const symbol_t* id, + size_t idLen, const SlaveSymbolString& answer) { return false; } + + /** + * @return @p true if an answer was set for the destination address. + */ + virtual bool hasAnswer(symbol_t dstAddress) const { return false; } /** * @param address the address to check. @@ -520,7 +537,7 @@ class ProtocolHandler : public WaitThread, public DeviceListener { */ virtual bool addSeenAddress(symbol_t address); - /** the client configuration to use. */ + /** the configuration to use. */ const ebus_protocol_config_t m_config; /** the @a Device instance for accessing the bus. */ diff --git a/src/lib/ebus/protocol_direct.cpp b/src/lib/ebus/protocol_direct.cpp index 67daec63..d9fa0e30 100644 --- a/src/lib/ebus/protocol_direct.cpp +++ b/src/lib/ebus/protocol_direct.cpp @@ -232,21 +232,21 @@ struct timespec* sentTime) { break; case bs_sendCmdAck: - if (m_config.answer) { + if (m_currentAnswering) { sendSymbol = m_crcValid ? ACK : NAK; sending = true; } break; case bs_sendRes: - if (m_config.answer) { + if (m_currentAnswering) { sendSymbol = m_response[m_nextSendPos]; // unescaped response sending = true; } break; case bs_sendResCrc: - if (m_config.answer) { + if (m_currentAnswering) { sendSymbol = m_crc; sending = true; } @@ -511,20 +511,10 @@ struct timespec* sentTime) { } 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); - } - return setState(bs_sendCmdAck, RESULT_ERR_CRC); - } - } if (m_crcValid) { addSeenAddress(m_command[0]); - return setState(bs_recvCmdAck, result); + m_currentAnswering = getAnswer(); + return setState(m_currentAnswering ? bs_sendCmdAck : bs_recvCmdAck, result); } if (m_repeat) { return setState(bs_skip, RESULT_ERR_CRC); @@ -646,7 +636,7 @@ struct timespec* sentTime) { return setState(bs_sendSyn, result); case bs_sendCmdAck: - if (!sending || !m_config.answer) { + if (!sending || !m_currentAnswering) { return setState(bs_skip, RESULT_ERR_INVALID_ARG); } if (!m_crcValid) { @@ -658,25 +648,18 @@ struct timespec* sentTime) { } return setState(bs_skip, RESULT_ERR_ACK); } + // response to send was already prepared during bs_recvCmdCrc in m_response if (isMaster(m_command[1])) { - messageCompleted(); // TODO decode command and store value into database of internal variables + messageCompleted(); return setState(bs_skip, result); } m_nextSendPos = 0; m_repeat = false; - // build response and store in m_response for sending back to requesting master - m_response.clear(); - { - result_t result = m_listener->notifyProtocolAnswer(m_command, &m_response); - if (result != RESULT_OK) { - return setState(bs_skip, result); - } - } return setState(bs_sendRes, result); case bs_sendRes: - if (!sending || !m_config.answer) { + if (!sending || !m_currentAnswering) { return setState(bs_skip, RESULT_ERR_INVALID_ARG); } m_nextSendPos++; @@ -687,7 +670,7 @@ struct timespec* sentTime) { return result; case bs_sendResCrc: - if (!sending || !m_config.answer) { + if (!sending || !m_currentAnswering) { return setState(bs_skip, RESULT_ERR_INVALID_ARG); } return setState(bs_recvResAck, result); @@ -801,7 +784,6 @@ bool DirectProtocolHandler::addSeenAddress(symbol_t address) { } 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); @@ -810,17 +792,105 @@ void DirectProtocolHandler::messageCompleted() { logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress); return; } - if (!m_currentAnswering) { + if (!m_currentAnswering || (dstAddress != m_ownMasterAddress && dstAddress != m_ownSlaveAddress)) { + // also add given answers to list of seen addresses addSeenAddress(dstAddress); } + const char* prefix = m_currentAnswering ? "answered" : m_currentRequest ? "sent" : "received"; + MessageDirection direction = m_currentAnswering ? md_answer : m_currentRequest ? md_send : md_recv; 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); + m_listener->notifyProtocolMessage(direction, command, response); +} + +uint64_t DirectProtocolHandler::createAnswerKey(symbol_t srcAddress, symbol_t dstAddress, symbol_t pb, symbol_t sb, + const symbol_t* id, size_t idLen) { + uint64_t key = (uint64_t)idLen << (8 * 7 + 5); + key |= (uint64_t)getMasterNumber(srcAddress) << (8 * 7); // 0..25 + key |= (uint64_t)dstAddress << (8 * 6); + key |= (uint64_t)pb << (8 * 5); + key |= (uint64_t)sb << (8 * 4); + int exp = 3; + for (size_t pos = 0; pos < idLen; pos++) { + key |= (uint64_t)id[pos] << (8 * exp--); + } + return key; +} + +bool DirectProtocolHandler::setAnswer(symbol_t srcAddress, symbol_t dstAddress, symbol_t pb, symbol_t sb, + const symbol_t* id, size_t idLen, const SlaveSymbolString& answer) { + if (!m_config.answer || (!id && idLen > 0) || idLen > 4 || !isValidAddress(dstAddress, false) + || (srcAddress != SYN && !isMaster(srcAddress))) { + return false; + } + if (isMaster(dstAddress)) { + if (answer.size() > 7) { + return false; + } + // answer used here only for having the expected length of the MM data tail + } else { + if (!answer.isComplete()) { + return false; + } + } + uint64_t key = createAnswerKey(srcAddress, dstAddress, pb, sb, id, idLen); + m_answerByKey[key] = answer; + return true; +} + +bool DirectProtocolHandler::hasAnswer(symbol_t dstAddress) const { + if (m_answerByKey.empty()) { + return false; + } + for (auto const &answer : m_answerByKey) { + if ((answer.first >> (8 * 6)) == dstAddress) { + return true; + } + } + return false; +} + +bool DirectProtocolHandler::getAnswer() { + if (m_answerByKey.empty()) { + return false; + } + // walk through the stored answers to find the longest match + m_response.clear(); + size_t len = m_command[4]; + bool master = isMaster(m_command[1]); + uint64_t key = createAnswerKey(m_command[0], m_command[1], m_command[2], m_command[3], m_command.data()+5, len); + do { + auto it = m_answerByKey.find(key); + if (it == m_answerByKey.end()) { + it = m_answerByKey.find(key&~(0x1fLL << (8 * 7))); // without specific src + } + if (it != m_answerByKey.end()) { + // found the answer + if (master) { + if (len+it->second.size() == m_command[4]) { + m_response = it->second; // copied for having the data size only + return true; + } + // data length mismatch, find shorter one + } else { + m_response = it->second; + return true; + } + break; + } + if (len == 0) { + break; + } + // reduce the key + len--; + key = (key&~(0x07LL << (8 * 7 + 5))&~(0xffLL << (8 * (3-len)))) | (len << (8 * 7 + 5)); + } while (true); + return false; } } // namespace ebusd diff --git a/src/lib/ebus/protocol_direct.h b/src/lib/ebus/protocol_direct.h index 0abcfc62..93dfb042 100755 --- a/src/lib/ebus/protocol_direct.h +++ b/src/lib/ebus/protocol_direct.h @@ -19,6 +19,7 @@ #ifndef LIB_EBUS_PROTOCOL_DIRECT_H_ #define LIB_EBUS_PROTOCOL_DIRECT_H_ +#include #include "lib/ebus/protocol.h" namespace ebusd { @@ -107,6 +108,15 @@ class DirectProtocolHandler : public ProtocolHandler { // @copydoc bool hasSignal() const override { return m_state != bs_noSignal; } + // @copydoc + bool isAnswering() const override { return !m_answerByKey.empty(); } + + // @copydoc + bool setAnswer(symbol_t srcAddress, symbol_t dstAddress, symbol_t pb, symbol_t sb, + const symbol_t* id, size_t idLen, const SlaveSymbolString& answer) override; + + // @copydoc + bool hasAnswer(symbol_t dstAddress) const override; private: /** @@ -146,6 +156,25 @@ class DirectProtocolHandler : public ProtocolHandler { */ void messageCompleted(); + /** + * Create a key for storing an answer. + * @param srcAddress the source address, or @a SYN for any. + * @param dstAddress the destination address. + * @param pb the primary ID byte. + * @param sb the secondary ID byte. + * @param id optional further ID bytes. + * @param idLen the length of the further ID bytes. + * @return a key for storing an answer. + */ + uint64_t createAnswerKey(symbol_t srcAddress, symbol_t dstAddress, symbol_t pb, symbol_t sb, + const symbol_t* id, size_t idLen); + + /** + * Build the answer to the currently received message and store in @a m_response for sending back to requestor. + * @return @p true on success, @p false if the message is not supposed to be answered. + */ + bool getAnswer(); + /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */ unsigned int m_lockCount; @@ -161,6 +190,9 @@ class DirectProtocolHandler : public ProtocolHandler { /** the currently handled BusRequest, or nullptr. */ BusRequest* m_currentRequest; + /** the answers to give by key. */ + std::map m_answerByKey; + /** whether currently answering a request from another participant. */ bool m_currentAnswering;