diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 51c1f692..6de65aec 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -58,7 +58,7 @@ Message::Message(const string circuit, const string name, m_srcAddress(srcAddress), m_dstAddress(dstAddress), m_id(id), m_data(data), m_deleteData(deleteData), m_pollPriority(pollPriority), - m_usedByCondition(false), m_condition(condition), + m_usedByCondition(false), m_isScanMessage(false), m_condition(condition), m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { unsigned long long key = (unsigned long long)(id.size()-2) << (8 * 7 + 5); @@ -74,6 +74,10 @@ Message::Message(const string circuit, const string name, exp = 3; } m_key = key; + if (circuit=="scan") { + setScanMessage(); + m_pollPriority = 0; + } } Message::Message(const string circuit, const string name, @@ -85,7 +89,7 @@ Message::Message(const string circuit, const string name, m_srcAddress(SYN), m_dstAddress(SYN), m_data(data), m_deleteData(true), m_pollPriority(0), - m_usedByCondition(false), m_condition(NULL), + m_usedByCondition(false), m_isScanMessage(false), m_condition(NULL), m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { m_id.push_back(pb); @@ -97,6 +101,10 @@ Message::Message(const string circuit, const string name, key |= (unsigned long long)pb << (8 * 5); key |= (unsigned long long)sb << (8 * 4); m_key = key; + if (circuit=="scan") { + setScanMessage(); + m_pollPriority = 0; + } } /** @@ -371,8 +379,9 @@ result_t Message::create(vector::iterator& it, const vector::ite Message* message; if (chainIds.size()>1) { message = new ChainedMessage(useCircuit, name, isWrite, comment, srcAddress, dstAddress, id, chainIds, chainLengths, data, index==0, pollPriority, condition); - } else + } else { message = new Message(useCircuit, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data, index==0, pollPriority, condition); + } messages.push_back(message); } return RESULT_OK; @@ -387,16 +396,6 @@ Message* Message::derive(const unsigned char dstAddress, const unsigned char src m_pollPriority, m_condition); } -Message* Message::derive(const unsigned char dstAddress, const bool extendCircuit) -{ - if (extendCircuit) { - ostringstream out; - out << m_circuit << '.' << hex << setw(2) << setfill('0') << static_cast(dstAddress); - return derive(dstAddress, SYN, out.str()); - } - return derive(dstAddress); -} - bool Message::checkIdPrefix(vector& id) { if (id.size() > m_id.size()) @@ -439,7 +438,7 @@ unsigned long long Message::getDerivedKey(const unsigned char dstAddress) bool Message::setPollPriority(unsigned char priority) { - if (priority == m_pollPriority || m_isPassive) + if (priority == m_pollPriority || m_isPassive || isScanMessage() || m_dstAddress==SYN) return false; if (m_usedByCondition && (priority==0 || priority>POLL_PRIORITY_CONDITION)) @@ -718,6 +717,22 @@ void Message::dumpColumn(ostream& output, size_t column, bool withConditions) } +Message* ScanMessage::derive(const unsigned char dstAddress, const unsigned char srcAddress, const string circuit) +{ + return new ScanMessage(circuit, dstAddress, this); +} + +ScanMessage* ScanMessage::derive(const unsigned char dstAddress, const bool extendCircuit) +{ + if (extendCircuit) { + ostringstream out; + out << m_circuit << '.' << hex << setw(2) << setfill('0') << static_cast(dstAddress); + return new ScanMessage(out.str(), dstAddress, this); + } + return new ScanMessage(m_circuit, dstAddress, this); +} + + ChainedMessage::ChainedMessage(const string circuit, const string name, const bool isWrite, const string comment, const unsigned char srcAddress, const unsigned char dstAddress, @@ -1150,60 +1165,62 @@ CombinedCondition* SimpleCondition::combineAnd(Condition* other) return ret->combineAnd(this)->combineAnd(other); } -result_t SimpleCondition::resolve(MessageMap* messages, ostringstream& errorMessage) +result_t SimpleCondition::resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)) { - if (m_message!=NULL) { - return RESULT_OK; // already resolved - } - Message* message; - if (m_name.length()==0) { - message = messages->getScanMessage(m_dstAddress); - errorMessage << "scan condition " << nouppercase << setw(2) << hex << setfill('0') << static_cast(m_dstAddress); - } else { - message = messages->find(m_circuit, m_name, false); - if (!message) { - message = messages->find(m_circuit, m_name, false, true); - } - errorMessage << "condition " << m_circuit << " " << m_name; - } - if (!message) { - errorMessage << ": message not found"; - return RESULT_ERR_NOTFOUND; - } - if (message->getDstAddress()==SYN) { - if (message->isPassive()) { - errorMessage << ": invalid passive message"; - return RESULT_ERR_INVALID_ARG; - } - if (m_dstAddress==SYN) { - errorMessage << ": destination address missing"; - return RESULT_ERR_INVALID_ADDR; - } - // clone the message with dedicated dstAddress if necessary - unsigned long long key = message->getDerivedKey(m_dstAddress); - vector* derived = messages->getByKey(key); - if (derived==NULL) { - message = message->derive(m_dstAddress, true); - messages->add(message); + if (m_message==NULL) { + Message* message; + if (m_name.length()==0) { + message = messages->getScanMessage(m_dstAddress); + errorMessage << "scan condition " << nouppercase << setw(2) << hex << setfill('0') << static_cast(m_dstAddress); } else { - message = getFirstAvailable(*derived, *message); - if (message==NULL) { - errorMessage << ": conditional derived message not found"; - return RESULT_ERR_INVALID_ARG; + message = messages->find(m_circuit, m_name, false); + if (!message) { + message = messages->find(m_circuit, m_name, false, true); } + errorMessage << "condition " << m_circuit << " " << m_name; } - } - - if (m_hasValues) { - if (!message->hasField(m_field.length()>0 ? m_field.c_str() : NULL, isNumeric())) { - errorMessage << (isNumeric() ? ": numeric field " : ": string field ") << m_field << " not found"; + if (!message) { + errorMessage << ": message not found"; return RESULT_ERR_NOTFOUND; } + if (message->getDstAddress()==SYN) { + if (message->isPassive()) { + errorMessage << ": invalid passive message"; + return RESULT_ERR_INVALID_ARG; + } + if (m_dstAddress==SYN) { + errorMessage << ": destination address missing"; + return RESULT_ERR_INVALID_ADDR; + } + // clone the message with dedicated dstAddress if necessary + unsigned long long key = message->getDerivedKey(m_dstAddress); + vector* derived = messages->getByKey(key); + if (derived==NULL) { + message = message->derive(m_dstAddress, true); + messages->add(message); + } else { + message = getFirstAvailable(*derived, *message); + if (message==NULL) { + errorMessage << ": conditional derived message not found"; + return RESULT_ERR_INVALID_ARG; + } + } + } + + if (m_hasValues) { + if (!message->hasField(m_field.length()>0 ? m_field.c_str() : NULL, isNumeric())) { + errorMessage << (isNumeric() ? ": numeric field " : ": string field ") << m_field << " not found"; + return RESULT_ERR_NOTFOUND; + } + } + m_message = message; + message->setUsedByCondition(); + if (m_name.length()>0 && !message->isScanMessage()) { + messages->addPollMessage(message, true); + } } - m_message = message; - message->setUsedByCondition(); - if (m_name.length()>0) { - messages->addPollMessage(message, true); + if (m_message->getLastUpdateTime()==0 && readMessageFunc!=NULL) { + (*readMessageFunc)(m_message); } return RESULT_OK; } @@ -1262,12 +1279,12 @@ void CombinedCondition::dump(ostream& output) } } -result_t CombinedCondition::resolve(MessageMap* messages, ostringstream& errorMessage) +result_t CombinedCondition::resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)) { for (vector::iterator it = m_conditions.begin(); it!=m_conditions.end(); it++) { Condition* condition = *it; ostringstream dummy; - result_t ret = condition->resolve(messages, dummy); + result_t ret = condition->resolve(messages, dummy, readMessageFunc); if (ret!=RESULT_OK) { errorMessage << dummy.str(); return ret; @@ -1323,7 +1340,7 @@ string Instruction::getDestination() else ret += m_defaultCircuit; if (!m_defaultSuffix.empty()) - ret += "."+m_defaultSuffix; + ret += m_defaultSuffix; } return ret; } @@ -1581,7 +1598,7 @@ result_t MessageMap::addFromFile(vector::iterator& begin, const vectorgetDerivedKey(dstAddress); vector* msgs = getByKey(key); if (msgs!=NULL) - return msgs->front(); - Message* message = m_scanMessage->derive(dstAddress, true); + return (ScanMessage*)msgs->front(); + ScanMessage* message = m_scanMessage->derive(dstAddress, true); add(message); return message; } @@ -1609,9 +1626,10 @@ result_t MessageMap::resolveConditions(bool verbose) { return overallResult; } -result_t MessageMap::resolveCondition(Condition* condition) { +result_t MessageMap::resolveCondition(Condition* condition, void (*readMessageFunc)(Message* message)) +{ ostringstream error; - result_t result = condition->resolve(this, error); + result_t result = condition->resolve(this, error, readMessageFunc); if (result!=RESULT_OK) { string errorMessage = error.str(); if (errorMessage.length()>0) { @@ -1623,7 +1641,8 @@ result_t MessageMap::resolveCondition(Condition* condition) { return result; } -result_t MessageMap::executeInstructions(ostringstream& log, void (*loadInfoFunc)(MessageMap* messages, const unsigned char address, string filename)) { +result_t MessageMap::executeInstructions(ostringstream& log, void (*loadInfoFunc)(MessageMap* messages, const unsigned char address, string file), void (*readMessageFunc)(Message* message)) +{ m_lastError = ""; result_t overallResult = RESULT_OK; vector remove; @@ -1640,7 +1659,7 @@ result_t MessageMap::executeInstructions(ostringstream& log, void (*loadInfoFunc Condition* condition = instruction->getCondition(); bool execute = condition==NULL; if (!execute) { - result_t result = resolveCondition(condition); + result_t result = resolveCondition(condition, instruction->isSingleton()?readMessageFunc:NULL); if (result!=RESULT_OK) { overallResult = result; } else if (condition->isTrue()) { @@ -1733,8 +1752,10 @@ Message* MessageMap::find(const string& circuit, const string& name, const bool } deque MessageMap::findAll(const string& circuit, const string& name, const bool completeMatch, - const bool withRead, const bool withWrite, const bool withPassive) + const bool withRead, const bool withWrite, const bool withPassive, + const bool completeMatchIgnoreCircuitSuffix, const bool onlyAvailable) { + bool checkCircuitIgnoreSuffix = completeMatch && completeMatchIgnoreCircuitSuffix; deque ret; string lcircuit = circuit; FileReader::tolower(lcircuit); @@ -1742,37 +1763,52 @@ deque MessageMap::findAll(const string& circuit, const string& name, c FileReader::tolower(lname); bool checkCircuit = lcircuit.length() > 0; bool checkName = name.length() > 0; + if (checkCircuit && checkCircuitIgnoreSuffix) { + size_t pos = lcircuit.find('#'); + if (pos!=string::npos) { + lcircuit.resize(pos); + } + } for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { if (it->first[0] == '-') // avoid duplicates: instances stored multiple times have a key starting with "-" continue; - Message* message = getFirstAvailable(it->second); - if (!message) - continue; - if (checkCircuit) { - string check = message->getCircuit(); - FileReader::tolower(check); - if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) - continue; + for (vector::iterator msgIt = it->second.begin(); msgIt != it->second.end(); msgIt++) { + Message* message = *msgIt; + if (checkCircuit) { + string check = message->getCircuit(); + FileReader::tolower(check); + if (checkCircuitIgnoreSuffix) { + size_t pos = check.find('#'); + if (pos!=string::npos) { + check.resize(pos); + } + } + if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) + continue; + } + if (checkName) { + string check = message->getName(); + FileReader::tolower(check); + if (completeMatch ? (check != lname) : (check.find(lname) == check.npos)) + continue; + } + if (message->isPassive()) { + if (!withPassive) { + continue; + } + } else if (message->isWrite()) { + if (!withWrite) { + continue; + } + } else { + if (!withRead) { + continue; + } + } + if (!onlyAvailable || message->isAvailable()) { + ret.push_back(*msgIt); + } } - if (checkName) { - string check = message->getName(); - FileReader::tolower(check); - if (completeMatch ? (check != lname) : (check.find(lname) == check.npos)) - continue; - } - if (message->isPassive()) { - if (!withPassive) - continue; - } - else if (message->isWrite()) { - if (!withWrite) - continue; - } - else { - if (!withRead) - continue; - } - ret.push_back(message); } return ret; @@ -1853,25 +1889,13 @@ void MessageMap::invalidateCache(Message* message) return; message->m_lastUpdateTime = 0; string circuit = message->getCircuit(); - size_t pos = circuit.find('#'); - if (pos!=string::npos) - circuit.resize(pos); string name = message->getName(); - deque messages = findAll(circuit, name, false, true, true, true); + deque messages = findAll(circuit, name, true, true, true, true, true); for (deque::iterator it = messages.begin(); it != messages.end(); it++) { Message* checkMessage = *it; - if (checkMessage==message - || name!=checkMessage->getName()) - continue; // check exact name - string check = checkMessage->getCircuit(); - if (check!=circuit) { - size_t pos = check.find('#'); - if (pos!=string::npos) - check.resize(pos); - if (check!=circuit) - continue; + if (checkMessage!=message) { + checkMessage->m_lastUpdateTime = 0; } - checkMessage->m_lastUpdateTime = 0; } } diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 6cd30082..f21b0a2c 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -90,7 +90,7 @@ public: const unsigned char srcAddress, const unsigned char dstAddress, const vector id, DataField* data, const bool deleteData, - const unsigned char pollPriority, + const unsigned char pollPriority=0, Condition* condition=NULL); /** @@ -139,6 +139,17 @@ public: vector< vector >* defaultsRows, Condition* condition, const string& filename, DataFieldTemplates* templates, vector& messages); + /** + * Set that this is a special scanning @a Message instance. + */ + void setScanMessage() { m_isScanMessage = true; } + + /** + * Return whether this is a special scanning @a Message instance. + * @return whether this is a special scanning @a Message instance. + */ + bool isScanMessage() { return m_isScanMessage; } + /** * Derive a new @a Message from this message. * @param dstAddress the new destination address. @@ -148,14 +159,6 @@ public: */ virtual Message* derive(const unsigned char dstAddress, const unsigned char srcAddress=SYN, const string circuit=""); - /** - * Derive a new @a Message from this message. - * @param dstAddress the new destination address. - * @param extendCircuit whether to extend the current circuit name with a dot and the new destination address in hex. - * @return the derived @a Message instance. - */ - Message* derive(const unsigned char dstAddress, const bool extendCircuit); - /** * Get the optional circuit name. * @return the optional circuit name. @@ -498,6 +501,9 @@ protected: /** whether this message is used by a @a Condition. */ bool m_usedByCondition; + /** whether this is a special scanning @a Message instance. */ + bool m_isScanMessage; + /** the @a Condition for this message, or NULL. */ Condition* m_condition; @@ -522,6 +528,52 @@ protected: }; +/** + * A marker subclass of @a Message for identifying ebusd created scanning @a Message instances. + */ +class ScanMessage : public Message +{ +public: + + /** + * Construct a new instance. + * @param circuit the optional circuit name. + * @param dstAddress the destination address, or @a SYN for any (set later). + * @param copyFrom the @a ScanMessage from which to copy the ID and data. + */ + ScanMessage(const string circuit, + const unsigned char dstAddress, + ScanMessage* copyFrom) + : Message(circuit, "id", false, false, "", SYN, dstAddress, + copyFrom->m_id, copyFrom->m_data, false) { + setScanMessage(); + } + + /** + * Construct a new instance. + * @param data the @a DataField for encoding/decoding the chained message. + * @param deleteData whether to delete the @a DataField during destruction. + */ + ScanMessage(DataField* data, const bool deleteData) + : Message("scan", "id", false, false, 0x07, 0x04, data, deleteData) { + } + + virtual ~ScanMessage() {} + + // @copydoc + virtual Message* derive(const unsigned char dstAddress, const unsigned char srcAddress, const string circuit); + + /** + * Derive a new @a ScanMessage from this message. + * @param dstAddress the new destination address. + * @param extendCircuit whether to extend the current circuit name with a dot and the new destination address in hex. + * @return the derived @a ScanMessage instance. + */ + ScanMessage* derive(const unsigned char dstAddress, const bool extendCircuit); + +}; + + /** * A chained @a Message that needs more than one read/write on the bus to collect/send the data. */ @@ -707,9 +759,10 @@ public: * Resolve the referred @a Message instance(s) and field index(es). * @param messages the @a MessageMap instance for resolving. * @param errorMessage a @a ostringstream to which to add optional error messages. + * @param readMessageFunc the function to call for immediate reading of a @a Message from the bus, or NULL. * @return @a RESULT_OK on success, or an error code. */ - virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage) = 0; + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)=NULL) = 0; /** * Check and return whether this condition is fulfilled. @@ -762,13 +815,8 @@ public: // @copydoc virtual CombinedCondition* combineAnd(Condition* other); - /** - * Resolve the referred @a Message instance(s) and field index(es). - * @param messages the @a MessageMap instance for resolving. - * @param errorMessage a @a ostringstream to which to add optional error messages. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage); + // @copydoc + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)=NULL); // @copydoc virtual bool isTrue(); @@ -919,7 +967,7 @@ public: virtual CombinedCondition* combineAnd(Condition* other) { m_conditions.push_back(other); return this; } // @copydoc - virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage); + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)=NULL); // @copydoc virtual bool isTrue(); @@ -1069,7 +1117,7 @@ public: MessageMap(const bool addAll=false) : FileReader::FileReader(true), m_addAll(addAll), m_maxIdLength(0), m_messageCount(0), m_conditionalMessageCount(0), m_passiveMessageCount(0) { - m_scanMessage = new Message("scan", "ident", false, false, 0x07, 0x04, DataFieldSet::getIdentFields(), true); + m_scanMessage = new ScanMessage(DataFieldSet::getIdentFields(), true); } /** @@ -1109,11 +1157,11 @@ public: const string& filename, unsigned int lineNo); /** - * Get the scan @a Message instance for the specified address. + * Get the scan @a ScanMessage instance for the specified address. * @param dstAddress the destination address, or @a SYN for the base scan @a Message. - * @return the scan @a Message instance, or NULL if the dstAddress is no slave. + * @return the scan @a ScanMessage instance, or NULL if the dstAddress is no slave. */ - Message* getScanMessage(const unsigned char dstAddress=SYN); + ScanMessage* getScanMessage(const unsigned char dstAddress=SYN); /** * Resolve all @a Condition instances. @@ -1125,17 +1173,20 @@ public: /** * Resolve a @a Condition. * @param condition the @a Condition to resolve. + * @param readMessageFunc the function to call for immediate reading of a @a Message from the bus, or NULL. * @return @a RESULT_OK on success, or an error code. */ - result_t resolveCondition(Condition* condition); + result_t resolveCondition(Condition* condition, void (*readMessageFunc)(Message* message)=NULL); /** * Run all executable @a Instruction instances. * @param log the @a ostringstream to log success messages to (if necessary). * @param loadInfoFunc the function to call for successful loading of a file for a participant, or NULL. + * @param readMessageFunc the function to call for immediate reading of a + * @a Message values from the bus required for singleton instructions, or NULL. * @return @a RESULT_OK on success, or an error code. */ - result_t executeInstructions(ostringstream& log, void (*loadInfoFunc)(MessageMap* messages, const unsigned char address, string file)=NULL); + result_t executeInstructions(ostringstream& log, void (*loadInfoFunc)(MessageMap* messages, const unsigned char address, string file)=NULL, void (*readMessageFunc)(Message* message)=NULL); /** * Add a loaded file to a participant. @@ -1179,10 +1230,13 @@ public: * @param withWrite true to include write messages (default false). * @param withPassive true to include passive messages (default false). * @return the found @a Message instances. + * @param completeMatchIgnoreCircuitSuffix ignore different circuit suffixes (after "#") for completeMatch. + * @param onlyAvailable true to include only available messages (default true), false to also include messages that are currently not available (e.g. due to unresolved or false conditions). * Note: the caller may not free the returned instances. */ deque findAll(const string& circuit, const string& name, const bool completeMatch=true, - const bool withRead=true, const bool withWrite=false, const bool withPassive=false); + const bool withRead=true, const bool withWrite=false, const bool withPassive=false, + const bool completeMatchIgnoreCircuitSuffix=false, const bool onlyAvailable=true); /** * Find the @a Message instance for the specified master data. @@ -1270,8 +1324,8 @@ private: /** whether to add all messages, even if duplicate. */ const bool m_addAll; - /** the @a Message instance used for scanning. */ - Message* m_scanMessage; + /** the @a ScanMessage instance used for scanning. */ + ScanMessage* m_scanMessage; /** the loaded configuration files by slave address. */ map m_loadedFiles;