diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 6bd69d92..7dff31b9 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -27,28 +27,50 @@ using namespace std; +Message::Message(const string clazz, const string name, const bool isSet, + const bool isActive, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, DataField* data, + const unsigned int pollPriority) + : m_class(clazz), m_name(name), m_isSet(isSet), + m_isActive(isActive), m_comment(comment), + m_srcAddress(srcAddress), m_dstAddress(dstAddress), + m_id(id), m_data(data), m_pollPriority(pollPriority) +{ + int exp = 7; + unsigned long long key = (unsigned long long)(id.size()-2) << (8 * exp + 5); + if (isActive == true) + key |= 0x1fLL << (8 * exp--); + else + key |= (unsigned long long)getMasterNumber(srcAddress) << (8 * exp--); + key |= (unsigned long long)dstAddress << (8 * exp--); + for (vector::const_iterator it=id.begin(); it::iterator& it, const vector::iterator end, const map templates, Message*& returnValue) { + // [type];[class];name;[comment];[QQ];ZZ;id;fields... result_t result; - // [type];class;name;[comment];[QQ];ZZ;id;fields... + bool isSet, isActive; + unsigned int pollPriority = 0; if (it == end) return RESULT_ERR_EOF; const char* str = (*it++).c_str(); if (it == end) return RESULT_ERR_EOF; - bool isSetMessage, isActiveMessage; - unsigned int pollPriority = 0; if (strcasecmp(str, "W") == 0) { - isActiveMessage = true; - isSetMessage = true; + isActive = true; + isSet = true; } else if (str[0] == 'C' || str[0] == 'c') { - isActiveMessage = false; - isSetMessage = str[1] == 'W' || str[1] == 'w'; + isActive = false; + isSet = str[1] == 'W' || str[1] == 'w'; } else if (str[0] == 'P' || str[0] == 'p') { - isActiveMessage = true; - isSetMessage = false; + isActive = true; + isSet = false; if (str[1] == 0) pollPriority = 1; else { @@ -58,8 +80,8 @@ result_t Message::create(vector::iterator& it, const vector::ite return result; } } else { - isActiveMessage = true; - isSetMessage = false; + isActive = true; + isSet = false; } string clazz = *it++; @@ -80,7 +102,7 @@ result_t Message::create(vector::iterator& it, const vector::ite if (it == end) return RESULT_ERR_EOF; unsigned char srcAddress; - if (*str == 0 || isActiveMessage == true) + if (*str == 0 || isActive == true) srcAddress = SYN; // no specific source defined, or ignore for active message else { srcAddress = parseInt(str, 16, 0, 0xff, result); @@ -125,17 +147,17 @@ result_t Message::create(vector::iterator& it, const vector::ite return RESULT_ERR_INVALID_ARG; // missing/too short/too long ID DataField* data = NULL; - result = DataField::create(it, end, templates, data, isSetMessage, dstAddress); + result = DataField::create(it, end, templates, data, isSet, dstAddress); if (result != RESULT_OK) return result; - returnValue = new Message(clazz, name, isSetMessage, isActiveMessage, comment, srcAddress, dstAddress, id, data, pollPriority); + returnValue = new Message(clazz, name, isSet, isActive, comment, srcAddress, dstAddress, id, data, pollPriority); return RESULT_OK; } result_t Message::prepare(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator) { - if (m_isActiveMessage == true) { + if (m_isActive == true) { masterData.clear(); masterData.push_back(srcAddress, false); masterData.push_back(m_dstAddress, false); @@ -157,7 +179,7 @@ result_t Message::prepare(const unsigned char srcAddress, SymbolString& masterDa result_t Message::handle(SymbolString& masterData, SymbolString& slaveData, ostringstream& output, char separator, bool answer) { - if (m_isActiveMessage == true) { + if (m_isActive == true) { result_t result = m_data->read(masterData, m_id.size() - 2, slaveData, 0, output, false, separator); if (result != RESULT_OK) return result; @@ -170,3 +192,97 @@ result_t Message::handle(SymbolString& masterData, SymbolString& slaveData, } return RESULT_OK; } + + +result_t MessageMap::add(Message* message) +{ + string key = message->getClass().append(";").append(message->getName()); + if (message->isActive() == true) + key.append(message->isSet() ? ";W" : ";R"); + else + key.append(";C"); + map::iterator nameIt = m_messagesByName.find(key); + if (nameIt != m_messagesByName.end()) + return RESULT_ERR_INVALID_ARG; // duplicate key + + if (message->isActive() == false) { + unsigned long long pkey = message->getKey(); + map::iterator keyIt = m_passiveMessagesByKey.find(pkey); + if (keyIt != m_passiveMessagesByKey.end()) + return RESULT_ERR_INVALID_ARG; // duplicate key + + unsigned char idLength = message->getId().size() - 2; + if (idLength > m_maxIdLength) + m_maxIdLength = idLength; + m_passiveMessagesByKey[pkey] = message; + } + + m_messagesByName[key] = message; + + return RESULT_OK; +} + +Message* MessageMap::find(const string clazz, const string name, const bool isActive, const bool isSet) +{ + string key = clazz; + for (int i=0; i<2; i++) { + key.append(";").append(name); + if (isActive == true) + key.append(isSet ? ";W" : ";R"); + else + key.append(";C"); + map::iterator it = m_messagesByName.find(key); + if (it != m_messagesByName.end()) + return it->second; + key.clear(); // try again without class name + } + + return NULL; +} + +Message* MessageMap::find(SymbolString master) { + if (master.size() < 5) + return NULL; + unsigned char maxIdLength = master[4]; + if (maxIdLength > m_maxIdLength) + maxIdLength = m_maxIdLength; + if (master.size() < 5+maxIdLength) + return NULL; + + unsigned long long sourceMask = 0x1fLL << (8 * 7); + for (int idLength=maxIdLength; idLength>=0; idLength--) { + int exp = 7; + unsigned long long key = (unsigned long long)idLength << (8 * exp + 5); + key |= (unsigned long long)getMasterNumber(master[0]) << (8 * exp--); + key |= (unsigned long long)master[1] << (8 * exp--); + key |= (unsigned long long)master[2] << (8 * exp--); + key |= (unsigned long long)master[3] << (8 * exp--); + for (unsigned char i=0; i::iterator it = m_passiveMessagesByKey.find(key); + if (it != m_passiveMessagesByKey.end()) + return it->second; + + if ((key & sourceMask) != 0) { + key &= ~sourceMask; // try again without specific source master + it = m_passiveMessagesByKey.find(key); + if (it != m_passiveMessagesByKey.end()) + return it->second; + } + } + + return NULL; +} + +void MessageMap::clear() +{ + for (map::iterator it=m_messagesByName.begin(); it!=m_messagesByName.end(); it++) { + delete it->second; + it->second = NULL; + } + m_messagesByName.clear(); + m_passiveMessagesByKey.clear(); + m_maxIdLength = 0; +} + diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 4856f9bb..9e1d5b6b 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -25,11 +25,12 @@ #include "symbol.h" #include #include +#include using namespace std; /** - * @brief Base class for all kinds of bus messages. + * @brief Defines parameters of a message sent or received on the bus. */ class Message { @@ -39,8 +40,8 @@ public: * @brief Constructs a new instance. * @param class the optional device class. * @param name the message name (unique within the same class and type). - * @param isSetMessage whether this is a set message. - * @param isActiveMessage true if message can be initiated by the daemon + * @param isSet whether this is a set message. + * @param isActive true if message can be initiated by the daemon * itself any any other participant, false if message can only be initiated * by a participant other than the daemon. * @param comment the comment. @@ -50,15 +51,11 @@ public: * @param data the @a DataField for encoding/decoding the message. * @param pollPriority the priority for polling, or 0 for no polling at all. */ - Message(const string clazz, const string name, const bool isSetMessage, - const bool isActiveMessage, const string comment, + Message(const string clazz, const string name, const bool isSet, + const bool isActive, const string comment, const unsigned char srcAddress, const unsigned char dstAddress, const vector id, DataField* data, - const unsigned int pollPriority) - : m_class(clazz), m_name(name), m_isSetMessage(isSetMessage), - m_isActiveMessage(isActiveMessage), m_comment(comment), - m_srcAddress(srcAddress), m_dstAddress(dstAddress), - m_id(id), m_data(data), m_pollPriority(pollPriority) {} + const unsigned int pollPriority); /** * @brief Destructor. */ @@ -88,7 +85,7 @@ public: * @brief Get whether this is a set message. * @return whether this is a set message. */ - bool isSetMessage() const { return m_isSetMessage; } + bool isSet() const { return m_isSet; } /** * @brief Get whether message can be initiated by the daemon itself and any other * participant. @@ -96,7 +93,7 @@ public: * participant, false if message can only be initiated by a participant * other than the daemon. */ - bool isActiveMessage() const { return m_isActiveMessage; } + bool isActive() const { return m_isActive; } /** * @brief Get the comment. * @return the comment. @@ -117,6 +114,11 @@ public: * @return the primary, secondary, and optionally further command ID bytes. */ vector getId() const { return m_id; } + /** + * @brief Returns the key for storing in @a MessageSet. + * @return the key for storing in @a MessageSet. + */ + unsigned long long getKey() { return m_key; } /** * @brief Reads the value from the master or slave @a SymbolString. * @param masterData the unescaped master data @a SymbolString for reading binary data. @@ -142,7 +144,6 @@ public: result_t handle(SymbolString& masterData, SymbolString& slaveData, ostringstream& output, char separator=';', bool answer=false); - private: /** the optional device class. */ @@ -150,11 +151,11 @@ private: /** the message name (unique within the same class and type). */ const string m_name; /** whether this is a set message. */ - const bool m_isSetMessage; + const bool m_isSet; /** true if message can be initiated by the daemon itself and any other * participant, false if message can only be initiated by a participant * other than the daemon. */ - const bool m_isActiveMessage; + const bool m_isActive; /** the comment. */ const string m_comment; /** the source address (optional if passive), or @a SYN for any. */ @@ -163,6 +164,8 @@ private: const unsigned char m_dstAddress; /** the primary, secondary, and optionally further command ID bytes. */ const vector m_id; + /** the key for storing in @a MessageSet. */ + unsigned long long m_key; /** the @a DataField for encoding/decoding the message. */ DataField* m_data; /** the priority for polling, or 0 for no polling at all. */ @@ -170,4 +173,58 @@ private: }; +/** + * @brief Holds a map of all known @a Message instances. + */ +class MessageMap +{ +public: + + /** + * @brief Constructs a new instance. + */ + MessageMap() : m_maxIdLength(0) {} + /** + * @brief Destructor. + */ + virtual ~MessageMap() { clear(); } + /** + * @brief Adds a @a Message instance to this set. + * @param message the @a Message instance to add. + * @return @a RESULT_OK on success, or an error code. + * Note: the caller may not free the created instance on success. + */ + result_t add(Message* message); + /** + * @brief Finds the @a Message instance for the specified class and name. + * @param master the master @a SymbolString for identifying the @a Message. + * @return the @a Message instance, or NULL. + * Note: the caller may not free the returned instance. + */ + Message* find(const string clazz, const string name, const bool isActive, const bool isSet); + /** + * @brief Finds the @a Message instance for the specified master data. + * @param master the master @a SymbolString for identifying the @a Message. + * @return the @a Message instance, or NULL. + * Note: the caller may not free the returned instance. + */ + Message* find(SymbolString master); + /** + * @brief Removes all @a Message instances. + */ + void clear(); + +private: + + /** the maximum ID length used by any of the known @a Message instances. */ + unsigned char m_maxIdLength; + + /** the known @a Message instances by class and name. */ + map m_messagesByName; + + /** the known passive @a Message instances by key. */ + map m_passiveMessagesByKey; + +}; + #endif // LIBEBUS_MESSAGE_H_ diff --git a/src/lib/ebus/test/test_message.cpp b/src/lib/ebus/test/test_message.cpp index 58000e5b..5acb2bad 100644 --- a/src/lib/ebus/test/test_message.cpp +++ b/src/lib/ebus/test/test_message.cpp @@ -42,7 +42,7 @@ void verify(bool expectFailMatch, string type, string input, void printErrorPos(vector::iterator it, const vector::iterator end, vector::iterator pos) { - cout << "Errroneous item is here:" << endl; + cout << "Erroneous item is here:" << endl; bool first = true; int cnt = 0; if (pos > it) @@ -71,11 +71,13 @@ int main() // field= name;[pos];type[;[divisor|values][;[unit][;[comment]]]] string checks[][5] = { // "message", "flags" - {";;first;;;fe;0700;x;;bda", "26.10.2014", "fffe0700042610061451", "00", ""}, + {"c;;first;;;fe;0700;x;;bda", "26.10.2014", "fffe0700042610061451", "00", ""}, {"w;;first;;;15;b5090400;date;;bda", "26.10.2014", "ff15b5090604002610061445", "00", ""}, }; map templates; - Message* message = NULL; + Message *message = NULL; + Message* deleteMessage = NULL; + MessageMap* messages = new MessageMap(); for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { string check[5] = checks[i]; istringstream isstr(check[0]); @@ -83,6 +85,7 @@ int main() SymbolString mstr = SymbolString(check[2], false); SymbolString sstr = SymbolString(check[3], false); string flags = check[4]; + bool dontMap = flags.find('m') != string::npos; bool failedCreate = flags.find('c') != string::npos; bool failedPrepare = flags.find('p') != string::npos; bool failedPrepareMatch = flags.find('P') != string::npos; @@ -92,12 +95,12 @@ int main() while (getline(isstr, item, ';') != 0) entries.push_back(item); - if (message != NULL) { - delete message; - message = NULL; + if (deleteMessage != NULL) { + delete deleteMessage; + deleteMessage = NULL; } vector::iterator it = entries.begin(); - result_t result = Message::create(it, entries.end(), templates, message); + result_t result = Message::create(it, entries.end(), templates, deleteMessage); if (failedCreate == true) { if (result == RESULT_OK) @@ -112,7 +115,7 @@ int main() printErrorPos(entries.begin(), entries.end(), it); continue; } - if (message == NULL) { + if (deleteMessage == NULL) { cout << "\"" << check[0] << "\": create error: NULL" << endl; continue; } @@ -122,6 +125,23 @@ int main() } cout << "\"" << check[0] << "\": create OK" << endl; + if (dontMap == false) { + result = messages->add(deleteMessage); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": add error: " + << getResultCode(result) << endl; + continue; + } + cout << " map OK" << endl; + message = deleteMessage; + deleteMessage = NULL; + if (messages->find(mstr) == message) + cout << " find OK" << endl; + else + cout << " find error: NULL" << endl; + } + else + message = deleteMessage; istringstream input(inputStr); SymbolString writeMstr = SymbolString(); result = message->prepare(0xff, writeMstr, input); @@ -142,14 +162,18 @@ int main() bool match = writeMstr==mstr; verify(failedPrepareMatch, "prepare", inputStr, match, mstr.getDataStr(), writeMstr.getDataStr()); + } - delete message; - message = NULL; + if (deleteMessage != NULL) { + delete deleteMessage; + deleteMessage = NULL; } for (map::iterator it = templates.begin(); it != templates.end(); it++) delete it->second; + delete messages; + return 0; }