add option to answer request to non-own addresses (part of #42)
This commit is contained in:
+23
-34
@@ -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";
|
||||||
|
|||||||
@@ -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:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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. */
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user