From f0f779cf6906c60f1ec99a67c26dd464e51058ea Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 11 Oct 2015 22:36:00 +0200 Subject: [PATCH] added support for conditions based on raw value of numeric fields, adjust error position cout in case of duplicate, corrected getDefault() size check --- src/lib/ebus/message.cpp | 360 +++++++++++++++++++++++++++++++++------ src/lib/ebus/message.h | 221 ++++++++++++++++++++++-- 2 files changed, 519 insertions(+), 62 deletions(-) diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 9a226cdf..4c7655d1 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -27,22 +27,29 @@ #include #include #include +#include using namespace std; /** the bit mask of the source master number in the message key. */ #define ID_SOURCE_MASK (0x1fLL << (8 * 7)) +/** the maximum poll priority for a @a Message referred to by a @a Condition. */ +#define POLL_PRIORITY_CONDITION 5 + Message::Message(const string circuit, const string name, const bool isWrite, const bool isPassive, const string comment, const unsigned char srcAddress, const unsigned char dstAddress, - const vector id, DataField* data, const bool deleteData, - const unsigned char pollPriority) + const vector id, + DataField* data, const bool deleteData, + const unsigned char pollPriority, + Condition* condition) : m_circuit(circuit), m_name(name), m_isWrite(isWrite), m_isPassive(isPassive), m_comment(comment), 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_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { int exp = 7; @@ -59,11 +66,14 @@ Message::Message(const string circuit, const string name, const bool isWrite, Message::Message(const bool isWrite, const bool isPassive, const unsigned char pb, const unsigned char sb, - DataField* data) + DataField* data, + Condition* condition) : m_circuit(), m_name(), m_isWrite(isWrite), m_isPassive(isPassive), m_comment(), m_srcAddress(SYN), m_dstAddress(SYN), - m_data(data), m_deleteData(true), m_pollPriority(0), + m_data(data), m_deleteData(true), + m_pollPriority(0), + m_usedByCondition(false), m_condition(condition), m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { m_id.push_back(pb); @@ -80,7 +90,7 @@ Message::Message(const bool isWrite, const bool isPassive, */ string getDefault(const string value, vector* defaults, size_t pos) { - if (value.length() > 0 || defaults == NULL || pos > defaults->size()) { + if (value.length() > 0 || defaults == NULL || pos >= defaults->size()) { return value; } @@ -88,7 +98,7 @@ string getDefault(const string value, vector* defaults, size_t pos) } result_t Message::create(vector::iterator& it, const vector::iterator end, - vector< vector >* defaultsRows, + vector< vector >* defaultsRows, map* conditions, const string& filename, DataFieldTemplates* templates, vector& messages) { // [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... @@ -100,7 +110,23 @@ result_t Message::create(vector::iterator& it, const vector::ite if (it == end) return RESULT_ERR_EOF; - const char* str = (*it++).c_str(); // [type] + string typeStr = *it++; + Condition* condition = NULL; + if (conditions && typeStr.length()>0 && typeStr[0]=='[') { + // condition + size_t pos = typeStr.find(']'); + if (pos!=string::npos) { + string type = typeStr.substr(1, pos-1); + string key = filename+":"+type; + map::iterator it = conditions->find(key); + if (it==conditions->end()) + return RESULT_ERR_NOTFOUND; + condition = it->second; + typeStr = typeStr.substr(pos+1); + } + } + + const char* str = typeStr.c_str(); // [type] if (it == end) return RESULT_ERR_EOF; size_t len = strlen(str); @@ -278,7 +304,7 @@ result_t Message::create(vector::iterator& it, const vector::ite sprintf(num, ".%d", index); useCircuit = useCircuit + num; } - messages.push_back(new Message(useCircuit, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data, index==0, pollPriority)); + messages.push_back(new Message(useCircuit, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data, index==0, pollPriority, condition)); } return RESULT_OK; } @@ -288,8 +314,30 @@ bool Message::setPollPriority(unsigned char priority) if (priority == m_pollPriority || m_isPassive) return false; + if (m_usedByCondition && (priority==0 || priority>POLL_PRIORITY_CONDITION)) + priority = POLL_PRIORITY_CONDITION; + + bool ret = m_pollPriority==0 && priority>0; m_pollPriority = priority; - return true; + return ret; +} + +void Message::setUsedByCondition() { + if (m_usedByCondition) + return; + m_usedByCondition = true; + if (m_pollPriority==0 || m_pollPriority>POLL_PRIORITY_CONDITION) + setPollPriority(POLL_PRIORITY_CONDITION); +} + +bool Message::isAvailable() +{ + return (m_condition==NULL) || m_condition->isTrue(); +} + +bool Message::hasField(const char* fieldName, bool numeric) +{ + return m_data->hasField(fieldName, numeric); } result_t Message::prepareMaster(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator, const unsigned char dstAddress) @@ -455,17 +503,34 @@ result_t Message::decodeLastData(ostringstream& output, OutputFormat outputForma return result; } +result_t Message::decodeLastDataField(unsigned int& output, const char* fieldName, signed char fieldIndex) +{ + unsigned char offset = (unsigned char)(m_id.size() - 2); + result_t result = m_data->read(pt_masterData, m_lastMasterData, offset, output, fieldName, fieldIndex); + if (result < RESULT_OK) + return result; + if (result == RESULT_EMPTY) + result = m_data->read(pt_slaveData, m_lastSlaveData, 0, output, fieldName, fieldIndex); + if (result < RESULT_OK) + return result; + if (result == RESULT_EMPTY) + return RESULT_ERR_NOTFOUND; + return result; +} + bool Message::isLessPollWeight(const Message* other) { - unsigned int tw = m_pollPriority * m_pollCount; - unsigned int ow = other->m_pollPriority * other->m_pollCount; + unsigned char tprio = m_pollPriority; + unsigned char oprio = other->m_pollPriority; + unsigned int tw = tprio * m_pollCount; + unsigned int ow = oprio * other->m_pollCount; if (tw > ow) return true; if (tw < ow) return false; - if (m_pollPriority > other->m_pollPriority) + if (tprio > oprio) return true; - if (m_pollPriority < other->m_pollPriority) + if (tprio < oprio) return false; if (m_lastPollTime > other->m_lastPollTime) return true; @@ -577,44 +642,191 @@ string strtolower(const string& str) return ret; } + + +result_t Condition::create(vector::iterator& it, const vector::iterator end, Condition*& returnValue) +{ + if (it==end) + return RESULT_ERR_EOF; + string circuit = *(it++); + if (circuit.length()==0) + return RESULT_ERR_INVALID_ARG; + if (it==end) + return RESULT_ERR_EOF; + string name = *(it++); + if (name.length()==0) + return RESULT_ERR_INVALID_ARG; + if (it==end) + return RESULT_ERR_EOF; + string field = *(it++); + + istringstream stream(*(it++)); + string str; + vector valueRanges; + result_t result; + while (getline(stream, str, VALUE_SEPARATOR) != 0) { + DataFieldTemplates::trim(str); + if (str.length()==0) + return RESULT_ERR_INVALID_ARG; + bool upto = str[0]=='<'; + if (upto || str[0]=='>') { + if (str.length()==1) + return RESULT_ERR_INVALID_ARG; + 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); + if (result!=RESULT_OK) + return result; + valueRanges.push_back(inclusive ? val : (val+(upto?-1:1))); + if (!upto) + valueRanges.push_back(UINT_MAX); + } else { + size_t pos = str.find('-'); + if (pos>0) { // range + unsigned int val = parseInt(str.substr(0, pos).c_str(), 10, 0, UINT_MAX, result); + if (result!=RESULT_OK) + return result; + valueRanges.push_back(val); + pos++; + } else { // single value + pos = 0; + } + unsigned int val = parseInt(str.substr(pos).c_str(), 10, 0, UINT_MAX, result); + if (result!=RESULT_OK) + return result; + valueRanges.push_back(val); + if (pos>0) + valueRanges.push_back(val); // single value + } + } + if (valueRanges.empty()) + return RESULT_ERR_INVALID_LIST; + + returnValue = new Condition(circuit, name, field, valueRanges); + return RESULT_OK; +} + +result_t Condition::resolve(MessageMap* messages, ostringstream& errorMessage) +{ + Message* message = messages->find(m_circuit, m_name, false); + if (!message) + message = messages->find(m_circuit, m_name, false, true); + if (!message) { + errorMessage << "condition " << m_circuit << " " << m_name << ": message not found"; + return RESULT_ERR_NOTFOUND; + } + if (m_field.length()>0) { + if (!message->hasField(m_field.c_str(), true)) { + errorMessage << "condition " << m_circuit << " " << m_name << ": numeric field " << m_field << " not found"; + return RESULT_ERR_NOTFOUND; + } + } + m_message = message; + return RESULT_OK; +} + +bool Condition::isTrue() +{ + if (!m_message) + return false; + if (m_message->getLastChangeTime()>m_lastCheckTime) { + unsigned int value = 0; + result_t result = m_message->decodeLastDataField(value, m_field.length()==0 ? NULL : m_field.c_str()); + bool isTrue = false; + if (result==RESULT_OK) { + for (size_t i=0; i+1getLastChangeTime(); + } + return m_isTrue; +} + + result_t MessageMap::add(Message* message) { unsigned long long key = message->getKey(); - map::iterator keyIt = m_messagesByKey.find(key); - if (keyIt != m_messagesByKey.end()) - return RESULT_ERR_DUPLICATE; // duplicate key - + bool conditional = message->isConditional(); + map >::iterator keyIt = m_messagesByKey.find(key); + if (keyIt != m_messagesByKey.end()) { + vector* messages = &keyIt->second; + if (!messages->front()->isConditional() || !conditional) + return RESULT_ERR_DUPLICATE; // duplicate key + } bool isPassive = message->isPassive(); bool isWrite = message->isWrite(); string circuit = strtolower(message->getCircuit()); string name = strtolower(message->getName()); string nameKey = string(isPassive ? "P" : (isWrite ? "W" : "R")) + circuit + FIELD_SEPARATOR + name; - map::iterator nameIt = m_messagesByName.find(nameKey); - if (nameIt != m_messagesByName.end()) - return RESULT_ERR_DUPLICATE; // duplicate key - - m_messagesByName[nameKey] = message; + map >::iterator nameIt = m_messagesByName.find(nameKey); + if (nameIt != m_messagesByName.end()) { + vector* messages = &nameIt->second; + if (!messages->front()->isConditional() || !message->isConditional()) + return RESULT_ERR_DUPLICATE_NAME; // duplicate key + } + m_messagesByName[nameKey].push_back(message); m_messageCount++; + if (conditional) + m_conditionalMessageCount++; if (isPassive) m_passiveMessageCount++; nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit nameIt = m_messagesByName.find(nameKey); if (nameIt == m_messagesByName.end()) - m_messagesByName[nameKey] = message; // only store first key without circuit - + m_messagesByName[nameKey].push_back(message); // always store first message without circuit + else { + vector* messages = &nameIt->second; + if (messages->front()->isConditional() && conditional) + m_messagesByName[nameKey].push_back(message); // store further messages only if both are conditional + } unsigned char idLength = (unsigned char)(message->getId().size() - 2); if (idLength < m_minIdLength) m_minIdLength = idLength; if (idLength > m_maxIdLength) m_maxIdLength = idLength; - m_messagesByKey[key] = message; + m_messagesByKey[key].push_back(message); addPollMessage(message); return RESULT_OK; } +result_t MessageMap::addDefaultFromFile(vector< vector >& defaults, vector& row, vector::iterator& begin, const string& filename, unsigned int lineNo) +{ + // convert conditions in defaults + string type = row[0]; + if (type.length()>0 && type[0]=='[' && type[type.length()-1]==']') { + // condition + type.erase(0, 1); + type.resize(type.length()-1); + string key = filename+":"+type; + map::iterator it = m_conditions.find(key); + if (it != m_conditions.end()) + return RESULT_ERR_DUPLICATE_NAME; + + Condition* condition = NULL; + result_t result = Condition::create(++begin, row.end(), condition); + if (result!=RESULT_OK) { + if (condition) + delete condition; + return result; + } + if (!condition) + return RESULT_ERR_INVALID_ARG; + + m_conditions[key] = condition; + return RESULT_OK; + } + return FileReader::addDefaultFromFile(defaults, row, begin, filename, lineNo); +} + result_t MessageMap::addFromFile(vector::iterator& begin, const vector::iterator end, DataFieldTemplates* arg, vector< vector >* defaults, const string& filename, unsigned int lineNo) { vector::iterator restart = begin; @@ -631,11 +843,16 @@ result_t MessageMap::addFromFile(vector::iterator& begin, const vector::iterator it = messages.begin(); it != messages.end(); it++) { Message* message = *it; - if (result == RESULT_OK) + if (result == RESULT_OK) { result = add(message); + if (result==RESULT_ERR_DUPLICATE_NAME) + begin = restart+3; // mark name as invalid + else if (result==RESULT_ERR_DUPLICATE) + begin = restart+8; // mark ID as invalid + } if (result != RESULT_OK) delete message; // delete all remaining messages on error } @@ -646,6 +863,37 @@ result_t MessageMap::addFromFile(vector::iterator& begin, const vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + Condition* condition = it->second; + result_t result = condition->resolve(this, error); + if (result!=RESULT_OK) { + if (verbose) { + overallResult = result; + error << ", "; + } else { + errorMessage = error.str(); + return result; + } + } else { + Message* message = condition->getMessage(); + message->setUsedByCondition(); + addPollMessage(message, true); + } + } + errorMessage = error.str(); + return overallResult; +} + +Message* getFirstAvailable(vector &messages) { + for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) + if ((*msgIt)->isAvailable()) + return *msgIt; + return NULL; +} + Message* MessageMap::find(const string& circuit, const string& name, const bool isWrite, const bool isPassive) { string lcircuit = strtolower(circuit); @@ -658,9 +906,12 @@ Message* MessageMap::find(const string& circuit, const string& name, const bool key = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + lname; // second try: without circuit else continue; // not allowed without circuit - map::iterator it = m_messagesByName.find(key); - if (it != m_messagesByName.end()) - return it->second; + map >::iterator it = m_messagesByName.find(key); + if (it != m_messagesByName.end()) { + Message* message = getFirstAvailable(it->second); + if (message) + return message; + } } return NULL; @@ -676,10 +927,12 @@ deque MessageMap::findAll(const string& circuit, const string& name, c bool checkCircuit = lcircuit.length() > 0; bool checkName = name.length() > 0; bool checkPb = pb >= 0; - for (map::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { + 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 = it->second; + Message* message = getFirstAvailable(it->second); + if (!message) + continue; if (checkCircuit) { string check = strtolower(message->getCircuit()); if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) @@ -741,18 +994,26 @@ deque MessageMap::findAll(SymbolString& master) for (unsigned char i = 0; i < idLength; i++) key |= (unsigned long long)master[5 + i] << (8 * exp--); - map::iterator it = m_messagesByKey.find(key); + map >::iterator it = m_messagesByKey.find(key); if (it != m_messagesByKey.end()) { - ret.push_back(it->second); + Message* message = getFirstAvailable(it->second); + if (message) + ret.push_back(message); } if ((key & ID_SOURCE_MASK) != 0) { it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master - if (it != m_messagesByKey.end()) - ret.push_back(it->second); + if (it != m_messagesByKey.end()) { + Message* message = getFirstAvailable(it->second); + if (message) + ret.push_back(message); + } } it = m_messagesByKey.find(key | ID_SOURCE_MASK); // try again with special value for active - if (it != m_messagesByKey.end()) - ret.push_back(it->second); + if (it != m_messagesByKey.end()) { + Message* message = getFirstAvailable(it->second); + if (message) + ret.push_back(message); + } } return ret; @@ -785,10 +1046,10 @@ void MessageMap::invalidateCache(Message* message) } } -void MessageMap::addPollMessage(Message* message) +void MessageMap::addPollMessage(Message* message, bool toFront) { if (message != NULL && message->getPollPriority() > 0) { - message->m_lastPollTime = m_pollMessages.size(); + message->m_lastPollTime = toFront ? 0 : m_pollMessages.size(); m_pollMessages.push(message); } } @@ -801,17 +1062,18 @@ void MessageMap::clear() m_pollMessages.pop(); } // free message instances - for (map::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { + 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 "-" - delete it->second; - it->second = NULL; + it->second.clear(); } // clear messages by name m_messageCount = 0; + m_conditionalMessageCount = 0; m_passiveMessageCount = 0; m_messagesByName.clear(); // clear messages by key m_messagesByKey.clear(); + m_conditions.clear(); m_minIdLength = 4; m_maxIdLength = 0; } @@ -831,16 +1093,18 @@ Message* MessageMap::getNextPoll() void MessageMap::dump(ostream& output) { bool first = true; - for (map::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { + for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { if (it->first[0] == '-') // skip instances stored multiple times (key starting with "-") continue; - Message* message = it->second; + Message* message = getFirstAvailable(it->second); + if (!message) + continue; if (first) first = false; else - cout << endl; - message->dump(cout); + output << endl; + message->dump(output); } if (!first) - cout << endl; + output << endl; } diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 27192246..557bfea3 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -32,6 +32,7 @@ using namespace std; +class Condition; class MessageMap; /** @@ -56,12 +57,15 @@ public: * @param data the @a DataField for encoding/decoding the message. * @param deleteData whether to delete the @a DataField during destruction. * @param pollPriority the priority for polling, or 0 for no polling at all. + * @param condition the @a Condition for this message, or NULL. */ Message(const string circuit, const string name, const bool isWrite, const bool isPassive, const string comment, const unsigned char srcAddress, const unsigned char dstAddress, - const vector id, DataField* data, const bool deleteData, - const unsigned char pollPriority); + const vector id, + DataField* data, const bool deleteData, + const unsigned char pollPriority, + Condition* condition=NULL); /** * Construct a new temporary instance. @@ -71,10 +75,12 @@ public: * @param pb the primary ID byte. * @param sb the secondary ID byte. * @param data the @a DataField for encoding/decoding the message. + * @param condition the @a Condition for this message, or NULL. */ Message(const bool isWrite, const bool isPassive, const unsigned char pb, const unsigned char sb, - DataField* data); + DataField* data, + Condition* condition=NULL); /** * Destructor. @@ -86,13 +92,15 @@ public: * @param it the iterator to traverse for the definition parts. * @param end the iterator pointing to the end of the definition parts. * @param defaultsRows a @a vector with rows containing defaults, or NULL. + * @param conditions the @a Condition instances by filename and condition name, or NULL. + * @param filename the name of the file being read. * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. * @param messages the @a vector to which to add created instances. * @return @a RESULT_OK on success, or an error code. * Note: the caller needs to free the created instances. */ static result_t create(vector::iterator& it, const vector::iterator end, - vector< vector >* defaultsRows, + vector< vector >* defaultsRows, map* conditions, const string& filename, DataFieldTemplates* templates, vector& messages); /** @@ -159,10 +167,35 @@ public: /** * Set the polling priority. * @param priority the polling priority, or 0 for no polling at all. - * @return true when the priority was changed, false otherwise. + * @return true when the priority was changed and polling was not enabled before, false otherwise. */ bool setPollPriority(unsigned char priority); + /** + * Set the poll priority suitable for resolving a @a Condition. + */ + void setUsedByCondition(); + + /** + * Return whether this @a Message depends on a @a Condition. + * @return true when this @a Message depends on a @a Condition. + */ + bool isConditional() const { return m_condition!=NULL; } + + /** + * Return whether this @a Message is available (optionally depending on a @a Condition evaluation). + * @return true when this @a Message is available. + */ + bool isAvailable(); + + /** + * Return whether the field is available. + * @param fieldName the name of the field to find. + * @param numeric true for a numeric field, false for a string field. + * @return true if the field is available. + */ + bool hasField(const char* fieldName, bool numeric=true); + /** * Prepare the master @a SymbolString for sending a query or command to the bus. * @param srcAddress the source address to set. @@ -223,6 +256,15 @@ public: result_t decodeLastData(ostringstream& output, OutputFormat outputFormat=0, bool leadingSeparator=false, const char* fieldName=NULL, signed char fieldIndex=-1); + /** + * Decode a particular field value from the last stored data. + * @param output the variable in which to store the value. + * @param fieldName the name of the field to decode, or NULL for the first field. + * @param fieldIndex the optional index of the named field, or -1. + * @return @a RESULT_OK on success, or an error code. + */ + result_t decodeLastDataField(unsigned int& output, const char* fieldName, signed char fieldIndex=-1); + /** * Get the last seen slave data. * @return the last seen slave @a SymbolString. @@ -306,6 +348,12 @@ private: /** the priority for polling, or 0 for no polling at all. */ unsigned char m_pollPriority; + /** whether this message is used by a @a Condition. */ + bool m_usedByCondition; + + /** the @a Condition for this message, or NULL. */ + Condition* m_condition; + /** the last seen master data. */ SymbolString m_lastMasterData; @@ -341,6 +389,110 @@ struct compareMessagePriority : binary_function { }; +/** + * Helper class extending @a priority_queue to hold distinct values only. + */ +class MessagePriorityQueue + : public priority_queue, compareMessagePriority> +{ +public: + /** + * Add data to the queue and ensure it is contained only once. + * @param __x the element to add. + */ + void push(const value_type& __x) + { + for (vector::iterator it = c.begin(); it != c.end(); it++) { + if (*it==__x) { + c.erase(it); + break; + } + } + priority_queue, compareMessagePriority>::push(__x); + } +}; + + +/** + * Holds a reference to a @a Message as condition for another @a Message. + */ +class 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). + */ + 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; + } + + /** + * Destructor. + */ + virtual ~Condition() { } + + /** + * Factory method for creating a new instance. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + * @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); + + /** + * Resolve the referred @a Message instance and field index. + * @param messages the @a MessageMap instance for resolving the referred @a Message. + * @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; } + + /** + * Check and return whether this condition is fulfilled. + * @return whether this condition is fulfilled. + */ + bool isTrue(); + +private: + + /** the circuit name. */ + const string m_circuit; + + /** the message name. */ + const string m_name; + + /** the field name, or empty for first field. */ + const string m_field; + + /** the valid value ranges (pairs of from/to inclusive). */ + const vector m_valueRanges; + + /** 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; + +}; + + /** * Holds a map of all known @a Message instances. */ @@ -352,7 +504,7 @@ public: * Construct a new instance. */ MessageMap() : FileReader::FileReader(true), - m_minIdLength(4), m_maxIdLength(0), m_messageCount(0), m_passiveMessageCount(0) {} + m_minIdLength(4), m_maxIdLength(0), m_messageCount(0), m_conditionalMessageCount(0), m_passiveMessageCount(0) {} /** * Destructor. @@ -367,9 +519,20 @@ public: */ result_t add(Message* message); + // @copydoc + virtual result_t addDefaultFromFile(vector< vector >& defaults, vector& row, vector::iterator& begin, const string& filename, unsigned int lineNo); + // @copydoc virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, DataFieldTemplates* arg, vector< vector >* defaults, const string& filename, unsigned int lineNo); + /** + * 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); + /** * Find the @a Message instance for the specified circuit and name. * @param circuit the optional circuit name. @@ -421,8 +584,9 @@ public: /** * Add a @a Message to the list of instances to poll. * @param message the @a Message to poll. + * @param toFront whether to add the @a Message to the very front of the poll queue. */ - void addPollMessage(Message* message); + void addPollMessage(Message* message, bool toFront=false); /** * Removes all @a Message instances. @@ -430,11 +594,22 @@ public: void clear(); /** - * Get the number of stored @a Message instances. - * @param passiveOnly true to count only passive messages, false to count all messages. - * @return the the number of stored @a Message instances. + * Get the number of all stored @a Message instances. + * @return the the number of all stored @a Message instances. */ - size_t size(const bool passiveOnly=false) { return passiveOnly ? m_passiveMessageCount : m_messageCount; } + size_t size() { return m_messageCount; } + + /** + * Get the number of stored conditional @a Message instances. + * @return the the number of stored conditional @a Message instances. + */ + size_t sizeConditional() { return m_conditionalMessageCount; } + + /** + * Get the number of stored passive @a Message instances. + * @return the the number of stored passive @a Message instances. + */ + size_t sizePassive() { return m_passiveMessageCount; } /** * Get the number of stored @a Message instances with a poll priority. @@ -449,6 +624,18 @@ public: */ Message* getNextPoll(); + /** + * Get the number of stored @a Condition instances. + * @return the number of stored @a Condition instances. + */ + size_t sizeConditions() { return m_conditions.size(); } + + /** + * Get the stored @a Condition instances. + * @return the @a Condition instances by filename and condition name. + */ + map& getConditions() { return m_conditions; } + /** * Write the message definitions to the @a ostream. * @param output the @a ostream to append the formatted messages to. @@ -466,17 +653,23 @@ private: /** the number of distinct @a Message instances stored in @a m_messagesByName. */ size_t m_messageCount; + /** the number of conditional @a Message instances part of @a m_messageCount. */ + size_t m_conditionalMessageCount; + /** the number of distinct passive @a Message instances stored in @a m_messagesByKey. */ size_t m_passiveMessageCount; /** the known @a Message instances by lowercase circuit and name. */ - map m_messagesByName; + map > m_messagesByName; /** the known @a Message instances by key. */ - map m_messagesByKey; + map > m_messagesByKey; /** the known @a Message instances to poll, by priority. */ - priority_queue, compareMessagePriority> m_pollMessages; + MessagePriorityQueue m_pollMessages; + + /** the @a Condition instances by filename and condition name. */ + map m_conditions; };