diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 0b145dd5..16c3d95a 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -628,23 +628,23 @@ string strtolower(const string& str) } - -result_t Condition::create(vector::iterator& it, const vector::iterator end, Condition*& returnValue) +result_t Condition::create(vector::iterator& it, const vector::iterator end, SimpleCondition*& returnValue) { - if (it==end) + // name,circuit,messagename,[comment],[fieldname],[ZZ],values (name already skipped by caller) + if (it+6>end) { + it = end; // for error reporting return RESULT_ERR_EOF; - string circuit = *(it++); + } + string circuit = *(it++); // circuit if (circuit.length()==0) return RESULT_ERR_INVALID_ARG; - if (it==end) - return RESULT_ERR_EOF; - string name = *(it++); + string name = *(it++); // messagename if (name.length()==0) return RESULT_ERR_INVALID_ARG; - if (it==end) - return RESULT_ERR_EOF; - string field = *(it++); - + it++; // comment + string field = *(it++); // fieldname + string zz = *(it++); // ZZ +//TODO use ZZ as optional param for ident, create ident message if necessary istringstream stream(*(it++)); string str; vector valueRanges; @@ -660,7 +660,7 @@ result_t Condition::create(vector::iterator& it, const vector::i if (upto) valueRanges.push_back(0); bool inclusive = str[1]=='='; - unsigned int val = parseInt(str.substr(inclusive?2:1).c_str(), 10, 0, UINT_MAX, result); + unsigned int val = parseInt(str.substr(inclusive?2:1).c_str(), 10, inclusive?0:1, inclusive?UINT_MAX:(UINT_MAX-1), result); if (result!=RESULT_OK) return result; valueRanges.push_back(inclusive ? val : (val+(upto?-1:1))); @@ -681,19 +681,28 @@ result_t Condition::create(vector::iterator& it, const vector::i if (result!=RESULT_OK) return result; valueRanges.push_back(val); - if (pos>0) + if (pos==0) valueRanges.push_back(val); // single value } } if (valueRanges.empty()) return RESULT_ERR_INVALID_LIST; - returnValue = new Condition(circuit, name, field, valueRanges); + returnValue = new SimpleCondition(circuit, name, field, valueRanges); return RESULT_OK; } -result_t Condition::resolve(MessageMap* messages, ostringstream& errorMessage) + +CombinedCondition* SimpleCondition::combineAnd(Condition* other) { + CombinedCondition* ret = new CombinedCondition(); + return ret->combineAnd(this)->combineAnd(other); +} + +result_t SimpleCondition::resolve(MessageMap* messages, ostringstream& errorMessage) +{ + if (m_message!=NULL) + return RESULT_OK; // already resolved Message* message = messages->find(m_circuit, m_name, false); if (!message) message = messages->find(m_circuit, m_name, false, true); @@ -708,10 +717,12 @@ result_t Condition::resolve(MessageMap* messages, ostringstream& errorMessage) } } m_message = message; + message->setUsedByCondition(); + messages->addPollMessage(message, true); return RESULT_OK; } -bool Condition::isTrue() +bool SimpleCondition::isTrue() { if (!m_message) return false; @@ -734,6 +745,27 @@ bool Condition::isTrue() } +result_t CombinedCondition::resolve(MessageMap* messages, ostringstream& errorMessage) +{ + for (vector::iterator it = m_conditions.begin(); it!=m_conditions.end(); it++) { + Condition* condition = *it; + result_t ret = condition->resolve(messages, errorMessage); + if (ret!=RESULT_OK) + return ret; + } + return RESULT_OK; +} + +bool CombinedCondition::isTrue() +{ + for (vector::iterator it = m_conditions.begin(); it!=m_conditions.end(); it++) { + if (!(*it)->isTrue()) + return false; + } + return true; +} + + result_t MessageMap::add(Message* message) { unsigned long long key = message->getKey(); @@ -802,7 +834,7 @@ result_t MessageMap::addDefaultFromFile(vector< vector >& defaults, vect if (row.size()>1 && defaultCircuit.length()>0 && row[1].length()==0) row[1] = defaultCircuit; // set default circuit - Condition* condition = NULL; + SimpleCondition* condition = NULL; result_t result = Condition::create(++begin, row.end(), condition); if (result!=RESULT_OK) { if (condition) @@ -811,7 +843,6 @@ result_t MessageMap::addDefaultFromFile(vector< vector >& defaults, vect } if (!condition) return RESULT_ERR_INVALID_ARG; - m_conditions[key] = condition; return RESULT_OK; } @@ -825,22 +856,43 @@ result_t MessageMap::addFromFile(vector::iterator& begin, const vector::iterator restart = begin; Condition* condition = NULL; string types = *restart; - if (types.length()>0 && types[0]=='[') { - // condition - size_t pos = types.find(']'); - if (pos!=string::npos) { - string key = filename+":"+types.substr(1, pos-1); - map::iterator it = m_conditions.find(key); // TODO add support for global conditions without filename - if (it==m_conditions.end()) - return RESULT_ERR_NOTFOUND; + size_t pos; + if (types.length()>0 && types[0]=='[' && (pos=types.find_last_of(']'))!=string::npos) { + // check if combined or simple condition is already known + const string combinedkey = filename+":"+types.substr(1, pos-1); + map::iterator it = m_conditions.find(combinedkey); + if (it!=m_conditions.end()) { condition = it->second; types = types.substr(pos+1); + } else { + bool store = false; + while ((pos=types.find(']'))!=string::npos) { + // simple condition + string key = filename+":"+types.substr(1, pos-1); + map::iterator it = m_conditions.find(key); + if (it==m_conditions.end()) + return RESULT_ERR_NOTFOUND; + if (condition) { + condition = condition->combineAnd(it->second); + store = true; + } else + condition = it->second; + types = types.substr(pos+1); + if (types.length()==0 || types[0]!='[') + break; + } + if (store) { + m_conditions[combinedkey] = condition; // store combined condition + } } } + if (types.length() == 0) types.append("r"); - result_t result = RESULT_ERR_EOF; + else if (types.find(']')!=string::npos) + return RESULT_ERR_INVALID_ARG; + result_t result = RESULT_ERR_EOF; istringstream stream(types); string type; vector messages; @@ -868,7 +920,7 @@ result_t MessageMap::addFromFile(vector::iterator& begin, const vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { @@ -879,16 +931,12 @@ result_t MessageMap::resolveConditions(string& errorMessage, bool verbose) { overallResult = result; error << ", "; } else { - errorMessage = error.str(); + m_lastError = error.str(); return result; } - } else { - Message* message = condition->getMessage(); - message->setUsedByCondition(); - addPollMessage(message, true); } } - errorMessage = error.str(); + m_lastError = error.str(); return overallResult; } @@ -1067,9 +1115,17 @@ void MessageMap::clear() m_pollMessages.pop(); } // free message instances - for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { - if (it->first[0] != '-') // avoid double free: instances stored multiple times have a key starting with "-" - it->second.clear(); + for (map >::iterator mit = m_messagesByName.begin(); mit != m_messagesByName.end(); mit++) { + if (mit->first[0] != '-') { // avoid double free: instances stored multiple times have a key starting with "-" + vector messages = mit->second; + for (vector::iterator it = messages.begin(); it != messages.end(); it++) + delete *it; + messages.clear(); + } + } + // free condition instances + for (map::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + delete it->second; } // clear messages by name m_messageCount = 0; diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index f309ae50..d1d54967 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -33,6 +33,8 @@ using namespace std; class Condition; +class SimpleCondition; +class CombinedCondition; class MessageMap; /** @@ -420,7 +422,7 @@ public: /** - * Holds a reference to a @a Message as condition for another @a Message. + * An abstract condition based on the value of one or more @a Message instances. */ class Condition { @@ -428,16 +430,9 @@ public: /** * Construct a new instance. - * @param circuit the circuit name. - * @param name the message name. - * @param field the field name. - * @param valueRanges the valid value ranges (pairs of from/to inclusive). */ - Condition(const string circuit, const string name, const string field, const vector valueRanges) - : m_circuit(circuit), m_name(name), m_field(field), m_valueRanges(valueRanges), - m_message(NULL), m_lastCheckTime(0), m_isTrue(false) { -// cout << "condition " << circuit << " " << name << " " << field << ":" << static_cast(valueRanges.size()) << endl; - } + Condition() + : m_lastCheckTime(0), m_isTrue(false) { } /** * Destructor. @@ -451,27 +446,79 @@ public: * @param returnValue the variable in which to store the created instance. * @return @a RESULT_OK on success, or an error code. */ - static result_t create(vector::iterator& it, const vector::iterator end, Condition*& returnValue); + static result_t create(vector::iterator& it, const vector::iterator end, SimpleCondition*& returnValue); /** - * Resolve the referred @a Message instance and field index. - * @param messages the @a MessageMap instance for resolving the referred @a Message. + * Combine this condition with another instance using a logical and. + * @param other the @a Condition to combine with. + */ + virtual CombinedCondition* combineAnd(Condition* other) = 0; + + /** + * 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. */ - result_t resolve(MessageMap* messages, ostringstream& errorMessage); - - /** - * Get the resolved @a Message instance - * @return the resolved @a Message instance, or NULL. - */ - Message* getMessage() { return m_message; } + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage) = 0; /** * Check and return whether this condition is fulfilled. * @return whether this condition is fulfilled. */ - bool isTrue(); + virtual bool isTrue() = 0; + +protected: + + /** the system time when the condition was last checked, 0 for never. */ + time_t m_lastCheckTime; + + /** whether the condition was @a true during the last check. */ + bool m_isTrue; + +}; + + +/** + * A simple condition based on the value of one @a Message. + */ +class SimpleCondition : public Condition +{ +public: + + /** + * Construct a new instance. + * @param circuit the circuit name. + * @param name the message name. + * @param field the field name. + * @param valueRanges the valid value ranges (pairs of from/to inclusive). + */ + SimpleCondition(const string circuit, const string name, const string field, const vector valueRanges) + : Condition(), + m_circuit(circuit), m_name(name), m_field(field), m_valueRanges(valueRanges), + m_message(NULL) { } + + /** + * Destructor. + */ + virtual ~SimpleCondition() {} + + // @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); + + /** + * Check and return whether this condition is fulfilled. + * @return whether this condition is fulfilled. + */ + virtual bool isTrue(); private: @@ -490,11 +537,40 @@ private: /** the resolved @a Message instance, or NULL. */ Message* m_message; - /** the system time when the condition was last checked, 0 for never. */ - time_t m_lastCheckTime; +}; - /** whether the condition was @a true during the last check. */ - bool m_isTrue; + +/** + * A condition combining two or more @a SimpleCondition instances with a logical and. + */ +class CombinedCondition : public Condition +{ +public: + + /** + * Construct a new instance. + */ + CombinedCondition() + : Condition() { } + + /** + * Destructor. + */ + virtual ~CombinedCondition() {} + + // @copydoc + virtual CombinedCondition* combineAnd(Condition* other) { m_conditions.push_back(other); return this; } + + // @copydoc + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage); + + // @copydoc + virtual bool isTrue(); + +private: + + /** the @a Condition instances used. */ + vector m_conditions; }; @@ -537,11 +613,10 @@ public: /** * Resolve all @a Condition instances. - * @param errorMessage a @a string reference to which to add an optional error message. * @param verbose whether to verbosely add all problems to the error message. * @return @a RESULT_OK on success, or an error code. */ - result_t resolveConditions(string& errorMessage, bool verbose=false); + result_t resolveConditions(bool verbose=false); /** * Find the @a Message instance for the specified circuit and name.