add option to answer request to non-own addresses (part of #42)

This commit is contained in:
John
2024-03-08 07:19:43 +01:00
parent d6ea68db94
commit c063fb3bff
8 changed files with 299 additions and 88 deletions
+23 -34
View File
@@ -32,9 +32,6 @@ using std::setfill;
using std::setw; using std::setw;
using std::endl; 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) { result_t PollRequest::prepare(symbol_t ownMasterAddress) {
istringstream input; 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) { void BusHandler::notifyProtocolSeenAddress(symbol_t address) {
m_seenAddresses[address] |= SEEN; m_seenAddresses[address] |= SEEN;
} }
void BusHandler::notifyProtocolMessage(bool sent, const MasterSymbolString& command, void BusHandler::notifyProtocolMessage(MessageDirection direction, const MasterSymbolString& command,
const SlaveSymbolString& response) { const SlaveSymbolString& response) {
symbol_t srcAddress = command[0], dstAddress = command[1]; symbol_t srcAddress = command[0], dstAddress = command[1];
bool master = isMaster(dstAddress); bool master = isMaster(dstAddress);
@@ -465,7 +440,21 @@ void BusHandler::notifyProtocolMessage(bool sent, const MasterSymbolString& comm
} }
m_grabbedMessages[key].setLastData(command, response); 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 (message == nullptr) {
if (dstAddress == BROADCAST || master) { if (dstAddress == BROADCAST || master) {
logNotice(lf_update, "%s unknown %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str()); 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(); string data = output.str();
if (m_protocol->isOwnAddress(dstAddress)) { 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(), 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 } else if (message->getDstAddress() == SYN) { // any destination
if (message->getSrcAddress() == SYN) { // any destination and any source 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(), 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) { if (ownAddress) {
*output << ", ebusd"; *output << ", ebusd";
if (m_protocol->isAnswering()) { }
*output << " (answering)"; if (m_protocol->hasAnswer(address)) {
} *output << " (answering)";
if (m_protocol->isAddressConflict(address)) { }
*output << ", conflict"; if (ownAddress && m_protocol->isAddressConflict(address)) {
} *output << ", conflict";
} }
if ((m_seenAddresses[address]&SCAN_DONE) != 0) { if ((m_seenAddresses[address]&SCAN_DONE) != 0) {
*output << ", scanned"; *output << ", scanned";
+2 -4
View File
@@ -398,14 +398,12 @@ class BusHandler : public ProtocolListener {
// @copydoc // @copydoc
void notifyProtocolStatus(ProtocolState state, result_t result) override; void notifyProtocolStatus(ProtocolState state, result_t result) override;
// @copydoc
result_t notifyProtocolAnswer(const MasterSymbolString& master, SlaveSymbolString* slave) override;
// @copydoc // @copydoc
void notifyProtocolSeenAddress(symbol_t address) override; void notifyProtocolSeenAddress(symbol_t address) override;
// @copydoc // @copydoc
void notifyProtocolMessage(bool sent, const MasterSymbolString& master, const SlaveSymbolString& slave) override; void notifyProtocolMessage(MessageDirection direction, const MasterSymbolString& master,
const SlaveSymbolString& slave) override;
private: private:
/** /**
+10
View File
@@ -415,6 +415,16 @@ int main(int argc, char* argv[], char* envp[]) {
return EINVAL; return EINVAL;
} }
s_busHandler->setProtocol(s_protocol); 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 (!s_opt.foreground) {
if (!setLogFile(s_opt.logFile)) { if (!setLogFile(s_opt.logFile)) {
+88 -2
View File
@@ -484,6 +484,13 @@ result_t MainLoop::decodeRequest(Request* req, bool* connected, RequestMode* req
*ostream << "ERR: command not enabled"; *ostream << "ERR: command not enabled";
return RESULT_OK; 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") { if (cmd == "F" || cmd == "FIND") {
return executeFind(args, getUserLevels(*user), ostream); return executeFind(args, getUserLevels(*user), ostream);
} }
@@ -1170,7 +1177,7 @@ result_t MainLoop::executeInject(const vector<string>& args, ostringstream* ostr
if (!m_scanHelper->parseMessage(args[argPos++], false, &master, &slave)) { if (!m_scanHelper->parseMessage(args[argPos++], false, &master, &slave)) {
return RESULT_ERR_INVALID_ARG; return RESULT_ERR_INVALID_ARG;
} }
m_busHandler->notifyProtocolMessage(false, master, slave); m_busHandler->notifyProtocolMessage(md_recv, master, slave);
return RESULT_OK; return RESULT_OK;
} }
*ostream << "usage: inject QQZZPBSBNN[DD]*/[NN[DD]*]\n" *ostream << "usage: inject QQZZPBSBNN[DD]*/[NN[DD]*]\n"
@@ -1183,6 +1190,84 @@ result_t MainLoop::executeInject(const vector<string>& args, ostringstream* ostr
return RESULT_OK; return RESULT_OK;
} }
result_t MainLoop::executeAnswer(const vector<string>& 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<string>& args, RequestMode* reqMode, ostringstream* ostream) { result_t MainLoop::executeDirect(const vector<string>& args, RequestMode* reqMode, ostringstream* ostream) {
if (reqMode->listenMode != lm_direct) { if (reqMode->listenMode != lm_direct) {
if (args.size() == 1) { 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" " 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" " 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" " 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" " direct Enter direct mode\n"
" state|s Report bus state\n" " state|s Report bus state\n"
" info|i Report information about the daemon, configuration, seen participants, and the device.\n" " info|i Report information about the daemon, configuration, seen participants, and the device.\n"
" grab|g Grab messages: grab [stop]\n" " grab|g Grab messages: grab [stop]\n"
" Report the messages: grab result [all|decode]\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" " decode|d Decode field(s): decode [-v|-V] [-n|-N] DEFINITION DD[DD]*\n"
" encode|e Encode field(s): encode DEFINITION VALUE[;VALUE]*\n" " encode|e Encode field(s): encode DEFINITION VALUE[;VALUE]*\n"
" scan Scan slaves: scan [full|ZZ]\n" " scan Scan slaves: scan [full|ZZ]\n"
+10 -1
View File
@@ -224,6 +224,15 @@ class MainLoop : public Thread {
*/ */
result_t executeDirect(const vector<string>& args, RequestMode* reqMode, ostringstream* ostream); result_t executeDirect(const vector<string>& 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<string>& args, ostringstream* ostream);
/** /**
* Execute the find command. * Execute the find command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @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. */ /** true when the poll interval is non zero. */
const bool m_polling; const bool m_polling;
/** whether to enable the hex command. */ /** whether to enable the hex, inject, and answer commands. */
const bool m_enableHex; const bool m_enableHex;
/** the MessageMap for handling newly defined messages for testing (if enabled), or nullptr. */ /** the MessageMap for handling newly defined messages for testing (if enabled), or nullptr. */
+34 -17
View File
@@ -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. * 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. * 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 * @param direction the message direction.
* was received only. * @param master the @a MasterSymbolString received/sent.
* @param master the @a MasterSymbolString received. * @param slave the @a SlaveSymbolString received/sent or the answer passed to @a ProtocolHandler::setAnswer() with
* @param slave the @a SlaveSymbolString received. * 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 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; } 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. * @param address the address to check.
@@ -520,7 +537,7 @@ class ProtocolHandler : public WaitThread, public DeviceListener {
*/ */
virtual bool addSeenAddress(symbol_t address); virtual bool addSeenAddress(symbol_t address);
/** the client configuration to use. */ /** the configuration to use. */
const ebus_protocol_config_t m_config; const ebus_protocol_config_t m_config;
/** the @a Device instance for accessing the bus. */ /** the @a Device instance for accessing the bus. */
+100 -30
View File
@@ -232,21 +232,21 @@ struct timespec* sentTime) {
break; break;
case bs_sendCmdAck: case bs_sendCmdAck:
if (m_config.answer) { if (m_currentAnswering) {
sendSymbol = m_crcValid ? ACK : NAK; sendSymbol = m_crcValid ? ACK : NAK;
sending = true; sending = true;
} }
break; break;
case bs_sendRes: case bs_sendRes:
if (m_config.answer) { if (m_currentAnswering) {
sendSymbol = m_response[m_nextSendPos]; // unescaped response sendSymbol = m_response[m_nextSendPos]; // unescaped response
sending = true; sending = true;
} }
break; break;
case bs_sendResCrc: case bs_sendResCrc:
if (m_config.answer) { if (m_currentAnswering) {
sendSymbol = m_crc; sendSymbol = m_crc;
sending = true; sending = true;
} }
@@ -511,20 +511,10 @@ struct timespec* sentTime) {
} }
return setState(bs_skip, RESULT_ERR_CRC); 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) { if (m_crcValid) {
addSeenAddress(m_command[0]); addSeenAddress(m_command[0]);
return setState(bs_recvCmdAck, result); m_currentAnswering = getAnswer();
return setState(m_currentAnswering ? bs_sendCmdAck : bs_recvCmdAck, result);
} }
if (m_repeat) { if (m_repeat) {
return setState(bs_skip, RESULT_ERR_CRC); return setState(bs_skip, RESULT_ERR_CRC);
@@ -646,7 +636,7 @@ struct timespec* sentTime) {
return setState(bs_sendSyn, result); return setState(bs_sendSyn, result);
case bs_sendCmdAck: case bs_sendCmdAck:
if (!sending || !m_config.answer) { if (!sending || !m_currentAnswering) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG); return setState(bs_skip, RESULT_ERR_INVALID_ARG);
} }
if (!m_crcValid) { if (!m_crcValid) {
@@ -658,25 +648,18 @@ struct timespec* sentTime) {
} }
return setState(bs_skip, RESULT_ERR_ACK); return setState(bs_skip, RESULT_ERR_ACK);
} }
// response to send was already prepared during bs_recvCmdCrc in m_response
if (isMaster(m_command[1])) { if (isMaster(m_command[1])) {
messageCompleted(); // TODO decode command and store value into database of internal variables messageCompleted();
return setState(bs_skip, result); return setState(bs_skip, result);
} }
m_nextSendPos = 0; m_nextSendPos = 0;
m_repeat = false; 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); return setState(bs_sendRes, result);
case bs_sendRes: case bs_sendRes:
if (!sending || !m_config.answer) { if (!sending || !m_currentAnswering) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG); return setState(bs_skip, RESULT_ERR_INVALID_ARG);
} }
m_nextSendPos++; m_nextSendPos++;
@@ -687,7 +670,7 @@ struct timespec* sentTime) {
return result; return result;
case bs_sendResCrc: case bs_sendResCrc:
if (!sending || !m_config.answer) { if (!sending || !m_currentAnswering) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG); return setState(bs_skip, RESULT_ERR_INVALID_ARG);
} }
return setState(bs_recvResAck, result); return setState(bs_recvResAck, result);
@@ -801,7 +784,6 @@ bool DirectProtocolHandler::addSeenAddress(symbol_t address) {
} }
void DirectProtocolHandler::messageCompleted() { void DirectProtocolHandler::messageCompleted() {
const char* prefix = m_currentRequest ? "sent" : "received";
// do an explicit copy here in case being called by another thread // do an explicit copy here in case being called by another thread
const MasterSymbolString command(m_currentRequest ? m_currentRequest->getMaster() : m_command); const MasterSymbolString command(m_currentRequest ? m_currentRequest->getMaster() : m_command);
const SlaveSymbolString response(m_response); const SlaveSymbolString response(m_response);
@@ -810,17 +792,105 @@ void DirectProtocolHandler::messageCompleted() {
logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress); logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress);
return; return;
} }
if (!m_currentAnswering) { if (!m_currentAnswering || (dstAddress != m_ownMasterAddress && dstAddress != m_ownSlaveAddress)) {
// also add given answers to list of seen addresses
addSeenAddress(dstAddress); 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); bool master = isMaster(dstAddress);
if (dstAddress == BROADCAST || master) { if (dstAddress == BROADCAST || master) {
logInfo(lf_update, "%s %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str()); logInfo(lf_update, "%s %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str());
} else { } else {
logInfo(lf_update, "%s MS cmd: %s / %s", prefix, command.getStr().c_str(), response.getStr().c_str()); 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 } // namespace ebusd
+32
View File
@@ -19,6 +19,7 @@
#ifndef LIB_EBUS_PROTOCOL_DIRECT_H_ #ifndef LIB_EBUS_PROTOCOL_DIRECT_H_
#define LIB_EBUS_PROTOCOL_DIRECT_H_ #define LIB_EBUS_PROTOCOL_DIRECT_H_
#include <map>
#include "lib/ebus/protocol.h" #include "lib/ebus/protocol.h"
namespace ebusd { namespace ebusd {
@@ -107,6 +108,15 @@ class DirectProtocolHandler : public ProtocolHandler {
// @copydoc // @copydoc
bool hasSignal() const override { return m_state != bs_noSignal; } 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: private:
/** /**
@@ -146,6 +156,25 @@ class DirectProtocolHandler : public ProtocolHandler {
*/ */
void messageCompleted(); 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. */ /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */
unsigned int m_lockCount; unsigned int m_lockCount;
@@ -161,6 +190,9 @@ class DirectProtocolHandler : public ProtocolHandler {
/** the currently handled BusRequest, or nullptr. */ /** the currently handled BusRequest, or nullptr. */
BusRequest* m_currentRequest; BusRequest* m_currentRequest;
/** the answers to give by key. */
std::map<uint64_t, SlaveSymbolString > m_answerByKey;
/** whether currently answering a request from another participant. */ /** whether currently answering a request from another participant. */
bool m_currentAnswering; bool m_currentAnswering;