added support for conditions based on raw value of numeric fields, adjust error position cout in case of duplicate, corrected getDefault() size check

This commit is contained in:
john30
2015-10-11 22:36:00 +02:00
parent e3856c3148
commit f0f779cf69
2 changed files with 519 additions and 62 deletions
+312 -48
View File
@@ -27,22 +27,29 @@
#include <algorithm> #include <algorithm>
#include <locale> #include <locale>
#include <iomanip> #include <iomanip>
#include <climits>
using namespace std; using namespace std;
/** the bit mask of the source master number in the message key. */ /** the bit mask of the source master number in the message key. */
#define ID_SOURCE_MASK (0x1fLL << (8 * 7)) #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, Message::Message(const string circuit, const string name, const bool isWrite,
const bool isPassive, const string comment, const bool isPassive, const string comment,
const unsigned char srcAddress, const unsigned char dstAddress, const unsigned char srcAddress, const unsigned char dstAddress,
const vector<unsigned char> id, DataField* data, const bool deleteData, const vector<unsigned char> id,
const unsigned char pollPriority) DataField* data, const bool deleteData,
const unsigned char pollPriority,
Condition* condition)
: m_circuit(circuit), m_name(name), m_isWrite(isWrite), : m_circuit(circuit), m_name(name), m_isWrite(isWrite),
m_isPassive(isPassive), m_comment(comment), m_isPassive(isPassive), m_comment(comment),
m_srcAddress(srcAddress), m_dstAddress(dstAddress), m_srcAddress(srcAddress), m_dstAddress(dstAddress),
m_id(id), m_data(data), m_deleteData(deleteData), m_id(id), m_data(data), m_deleteData(deleteData),
m_pollPriority(pollPriority), m_pollPriority(pollPriority),
m_usedByCondition(false), m_condition(condition),
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0)
{ {
int exp = 7; 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, Message::Message(const bool isWrite, const bool isPassive,
const unsigned char pb, const unsigned char sb, const unsigned char pb, const unsigned char sb,
DataField* data) DataField* data,
Condition* condition)
: m_circuit(), m_name(), m_isWrite(isWrite), : m_circuit(), m_name(), m_isWrite(isWrite),
m_isPassive(isPassive), m_comment(), m_isPassive(isPassive), m_comment(),
m_srcAddress(SYN), m_dstAddress(SYN), 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_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0)
{ {
m_id.push_back(pb); m_id.push_back(pb);
@@ -80,7 +90,7 @@ Message::Message(const bool isWrite, const bool isPassive,
*/ */
string getDefault(const string value, vector<string>* defaults, size_t pos) string getDefault(const string value, vector<string>* defaults, size_t pos)
{ {
if (value.length() > 0 || defaults == NULL || pos > defaults->size()) { if (value.length() > 0 || defaults == NULL || pos >= defaults->size()) {
return value; return value;
} }
@@ -88,7 +98,7 @@ string getDefault(const string value, vector<string>* defaults, size_t pos)
} }
result_t Message::create(vector<string>::iterator& it, const vector<string>::iterator end, result_t Message::create(vector<string>::iterator& it, const vector<string>::iterator end,
vector< vector<string> >* defaultsRows, vector< vector<string> >* defaultsRows, map<string, Condition*>* conditions, const string& filename,
DataFieldTemplates* templates, vector<Message*>& messages) DataFieldTemplates* templates, vector<Message*>& messages)
{ {
// [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... // [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
@@ -100,7 +110,23 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
if (it == end) if (it == end)
return RESULT_ERR_EOF; 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<string, Condition*>::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) if (it == end)
return RESULT_ERR_EOF; return RESULT_ERR_EOF;
size_t len = strlen(str); size_t len = strlen(str);
@@ -278,7 +304,7 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
sprintf(num, ".%d", index); sprintf(num, ".%d", index);
useCircuit = useCircuit + num; 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; return RESULT_OK;
} }
@@ -288,8 +314,30 @@ bool Message::setPollPriority(unsigned char priority)
if (priority == m_pollPriority || m_isPassive) if (priority == m_pollPriority || m_isPassive)
return false; 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; 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) 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; 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) bool Message::isLessPollWeight(const Message* other)
{ {
unsigned int tw = m_pollPriority * m_pollCount; unsigned char tprio = m_pollPriority;
unsigned int ow = other->m_pollPriority * other->m_pollCount; unsigned char oprio = other->m_pollPriority;
unsigned int tw = tprio * m_pollCount;
unsigned int ow = oprio * other->m_pollCount;
if (tw > ow) if (tw > ow)
return true; return true;
if (tw < ow) if (tw < ow)
return false; return false;
if (m_pollPriority > other->m_pollPriority) if (tprio > oprio)
return true; return true;
if (m_pollPriority < other->m_pollPriority) if (tprio < oprio)
return false; return false;
if (m_lastPollTime > other->m_lastPollTime) if (m_lastPollTime > other->m_lastPollTime)
return true; return true;
@@ -577,44 +642,191 @@ string strtolower(const string& str)
return ret; return ret;
} }
result_t Condition::create(vector<string>::iterator& it, const vector<string>::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<unsigned int> 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+1<m_valueRanges.size(); i+=2) {
if (m_valueRanges[i]<=value && value<=m_valueRanges[i+1]) {
isTrue = true;
break;
}
}
}
m_isTrue = isTrue;
m_lastCheckTime = m_message->getLastChangeTime();
}
return m_isTrue;
}
result_t MessageMap::add(Message* message) result_t MessageMap::add(Message* message)
{ {
unsigned long long key = message->getKey(); unsigned long long key = message->getKey();
map<unsigned long long, Message*>::iterator keyIt = m_messagesByKey.find(key); bool conditional = message->isConditional();
if (keyIt != m_messagesByKey.end()) map<unsigned long long, vector<Message*> >::iterator keyIt = m_messagesByKey.find(key);
return RESULT_ERR_DUPLICATE; // duplicate key if (keyIt != m_messagesByKey.end()) {
vector<Message*>* messages = &keyIt->second;
if (!messages->front()->isConditional() || !conditional)
return RESULT_ERR_DUPLICATE; // duplicate key
}
bool isPassive = message->isPassive(); bool isPassive = message->isPassive();
bool isWrite = message->isWrite(); bool isWrite = message->isWrite();
string circuit = strtolower(message->getCircuit()); string circuit = strtolower(message->getCircuit());
string name = strtolower(message->getName()); string name = strtolower(message->getName());
string nameKey = string(isPassive ? "P" : (isWrite ? "W" : "R")) + circuit + FIELD_SEPARATOR + name; string nameKey = string(isPassive ? "P" : (isWrite ? "W" : "R")) + circuit + FIELD_SEPARATOR + name;
map<string, Message*>::iterator nameIt = m_messagesByName.find(nameKey); map<string, vector<Message*> >::iterator nameIt = m_messagesByName.find(nameKey);
if (nameIt != m_messagesByName.end()) if (nameIt != m_messagesByName.end()) {
return RESULT_ERR_DUPLICATE; // duplicate key vector<Message*>* messages = &nameIt->second;
if (!messages->front()->isConditional() || !message->isConditional())
m_messagesByName[nameKey] = message; return RESULT_ERR_DUPLICATE_NAME; // duplicate key
}
m_messagesByName[nameKey].push_back(message);
m_messageCount++; m_messageCount++;
if (conditional)
m_conditionalMessageCount++;
if (isPassive) if (isPassive)
m_passiveMessageCount++; m_passiveMessageCount++;
nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit
nameIt = m_messagesByName.find(nameKey); nameIt = m_messagesByName.find(nameKey);
if (nameIt == m_messagesByName.end()) 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<Message*>* 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); unsigned char idLength = (unsigned char)(message->getId().size() - 2);
if (idLength < m_minIdLength) if (idLength < m_minIdLength)
m_minIdLength = idLength; m_minIdLength = idLength;
if (idLength > m_maxIdLength) if (idLength > m_maxIdLength)
m_maxIdLength = idLength; m_maxIdLength = idLength;
m_messagesByKey[key] = message; m_messagesByKey[key].push_back(message);
addPollMessage(message); addPollMessage(message);
return RESULT_OK; return RESULT_OK;
} }
result_t MessageMap::addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row, vector<string>::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<string, Condition*>::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<string>::iterator& begin, const vector<string>::iterator end, DataFieldTemplates* arg, vector< vector<string> >* defaults, const string& filename, unsigned int lineNo) result_t MessageMap::addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end, DataFieldTemplates* arg, vector< vector<string> >* defaults, const string& filename, unsigned int lineNo)
{ {
vector<string>::iterator restart = begin; vector<string>::iterator restart = begin;
@@ -631,11 +843,16 @@ result_t MessageMap::addFromFile(vector<string>::iterator& begin, const vector<s
*restart = type; *restart = type;
begin = restart; begin = restart;
messages.clear(); messages.clear();
result = Message::create(begin, end, defaults, arg, messages); result = Message::create(begin, end, defaults, &m_conditions, filename, arg, messages);
for (vector<Message*>::iterator it = messages.begin(); it != messages.end(); it++) { for (vector<Message*>::iterator it = messages.begin(); it != messages.end(); it++) {
Message* message = *it; Message* message = *it;
if (result == RESULT_OK) if (result == RESULT_OK) {
result = add(message); 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) if (result != RESULT_OK)
delete message; // delete all remaining messages on error delete message; // delete all remaining messages on error
} }
@@ -646,6 +863,37 @@ result_t MessageMap::addFromFile(vector<string>::iterator& begin, const vector<s
return result; return result;
} }
result_t MessageMap::resolveConditions(string& errorMessage, bool verbose) {
result_t overallResult = RESULT_OK;
ostringstream error;
for (map<string, Condition*>::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<Message*> &messages) {
for (vector<Message*>::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) Message* MessageMap::find(const string& circuit, const string& name, const bool isWrite, const bool isPassive)
{ {
string lcircuit = strtolower(circuit); 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 key = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + lname; // second try: without circuit
else else
continue; // not allowed without circuit continue; // not allowed without circuit
map<string, Message*>::iterator it = m_messagesByName.find(key); map<string, vector<Message*> >::iterator it = m_messagesByName.find(key);
if (it != m_messagesByName.end()) if (it != m_messagesByName.end()) {
return it->second; Message* message = getFirstAvailable(it->second);
if (message)
return message;
}
} }
return NULL; return NULL;
@@ -676,10 +927,12 @@ deque<Message*> MessageMap::findAll(const string& circuit, const string& name, c
bool checkCircuit = lcircuit.length() > 0; bool checkCircuit = lcircuit.length() > 0;
bool checkName = name.length() > 0; bool checkName = name.length() > 0;
bool checkPb = pb >= 0; bool checkPb = pb >= 0;
for (map<string, Message*>::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { for (map<string, vector<Message*> >::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 "-" if (it->first[0] == '-') // avoid duplicates: instances stored multiple times have a key starting with "-"
continue; continue;
Message* message = it->second; Message* message = getFirstAvailable(it->second);
if (!message)
continue;
if (checkCircuit) { if (checkCircuit) {
string check = strtolower(message->getCircuit()); string check = strtolower(message->getCircuit());
if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos))
@@ -741,18 +994,26 @@ deque<Message*> MessageMap::findAll(SymbolString& master)
for (unsigned char i = 0; i < idLength; i++) for (unsigned char i = 0; i < idLength; i++)
key |= (unsigned long long)master[5 + i] << (8 * exp--); key |= (unsigned long long)master[5 + i] << (8 * exp--);
map<unsigned long long , Message*>::iterator it = m_messagesByKey.find(key); map<unsigned long long , vector<Message*> >::iterator it = m_messagesByKey.find(key);
if (it != m_messagesByKey.end()) { 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) { if ((key & ID_SOURCE_MASK) != 0) {
it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master
if (it != m_messagesByKey.end()) if (it != m_messagesByKey.end()) {
ret.push_back(it->second); 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 it = m_messagesByKey.find(key | ID_SOURCE_MASK); // try again with special value for active
if (it != m_messagesByKey.end()) if (it != m_messagesByKey.end()) {
ret.push_back(it->second); Message* message = getFirstAvailable(it->second);
if (message)
ret.push_back(message);
}
} }
return ret; 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) { if (message != NULL && message->getPollPriority() > 0) {
message->m_lastPollTime = m_pollMessages.size(); message->m_lastPollTime = toFront ? 0 : m_pollMessages.size();
m_pollMessages.push(message); m_pollMessages.push(message);
} }
} }
@@ -801,17 +1062,18 @@ void MessageMap::clear()
m_pollMessages.pop(); m_pollMessages.pop();
} }
// free message instances // free message instances
for (map<string, Message*>::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { for (map<string, vector<Message*> >::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 "-" if (it->first[0] != '-') // avoid double free: instances stored multiple times have a key starting with "-"
delete it->second; it->second.clear();
it->second = NULL;
} }
// clear messages by name // clear messages by name
m_messageCount = 0; m_messageCount = 0;
m_conditionalMessageCount = 0;
m_passiveMessageCount = 0; m_passiveMessageCount = 0;
m_messagesByName.clear(); m_messagesByName.clear();
// clear messages by key // clear messages by key
m_messagesByKey.clear(); m_messagesByKey.clear();
m_conditions.clear();
m_minIdLength = 4; m_minIdLength = 4;
m_maxIdLength = 0; m_maxIdLength = 0;
} }
@@ -831,16 +1093,18 @@ Message* MessageMap::getNextPoll()
void MessageMap::dump(ostream& output) void MessageMap::dump(ostream& output)
{ {
bool first = true; bool first = true;
for (map<string, Message*>::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { for (map<string, vector<Message*> >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) {
if (it->first[0] == '-') // skip instances stored multiple times (key starting with "-") if (it->first[0] == '-') // skip instances stored multiple times (key starting with "-")
continue; continue;
Message* message = it->second; Message* message = getFirstAvailable(it->second);
if (!message)
continue;
if (first) if (first)
first = false; first = false;
else else
cout << endl; output << endl;
message->dump(cout); message->dump(output);
} }
if (!first) if (!first)
cout << endl; output << endl;
} }
+207 -14
View File
@@ -32,6 +32,7 @@
using namespace std; using namespace std;
class Condition;
class MessageMap; class MessageMap;
/** /**
@@ -56,12 +57,15 @@ public:
* @param data the @a DataField for encoding/decoding the message. * @param data the @a DataField for encoding/decoding the message.
* @param deleteData whether to delete the @a DataField during destruction. * @param deleteData whether to delete the @a DataField during destruction.
* @param pollPriority the priority for polling, or 0 for no polling at all. * @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, Message(const string circuit, const string name, const bool isWrite,
const bool isPassive, const string comment, const bool isPassive, const string comment,
const unsigned char srcAddress, const unsigned char dstAddress, const unsigned char srcAddress, const unsigned char dstAddress,
const vector<unsigned char> id, DataField* data, const bool deleteData, const vector<unsigned char> id,
const unsigned char pollPriority); DataField* data, const bool deleteData,
const unsigned char pollPriority,
Condition* condition=NULL);
/** /**
* Construct a new temporary instance. * Construct a new temporary instance.
@@ -71,10 +75,12 @@ public:
* @param pb the primary ID byte. * @param pb the primary ID byte.
* @param sb the secondary ID byte. * @param sb the secondary ID byte.
* @param data the @a DataField for encoding/decoding the message. * @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, Message(const bool isWrite, const bool isPassive,
const unsigned char pb, const unsigned char sb, const unsigned char pb, const unsigned char sb,
DataField* data); DataField* data,
Condition* condition=NULL);
/** /**
* Destructor. * Destructor.
@@ -86,13 +92,15 @@ public:
* @param it the iterator to traverse for the definition parts. * @param it the iterator to traverse for the definition parts.
* @param end the iterator pointing to the end of 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 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 templates the @a DataFieldTemplates to be referenced by name, or NULL.
* @param messages the @a vector to which to add created instances. * @param messages the @a vector to which to add created instances.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
* Note: the caller needs to free the created instances. * Note: the caller needs to free the created instances.
*/ */
static result_t create(vector<string>::iterator& it, const vector<string>::iterator end, static result_t create(vector<string>::iterator& it, const vector<string>::iterator end,
vector< vector<string> >* defaultsRows, vector< vector<string> >* defaultsRows, map<string, Condition*>* conditions, const string& filename,
DataFieldTemplates* templates, vector<Message*>& messages); DataFieldTemplates* templates, vector<Message*>& messages);
/** /**
@@ -159,10 +167,35 @@ public:
/** /**
* Set the polling priority. * Set the polling priority.
* @param priority the polling priority, or 0 for no polling at all. * @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); 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. * Prepare the master @a SymbolString for sending a query or command to the bus.
* @param srcAddress the source address to set. * @param srcAddress the source address to set.
@@ -223,6 +256,15 @@ public:
result_t decodeLastData(ostringstream& output, OutputFormat outputFormat=0, result_t decodeLastData(ostringstream& output, OutputFormat outputFormat=0,
bool leadingSeparator=false, const char* fieldName=NULL, signed char fieldIndex=-1); 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. * Get the last seen slave data.
* @return the last seen slave @a SymbolString. * @return the last seen slave @a SymbolString.
@@ -306,6 +348,12 @@ private:
/** the priority for polling, or 0 for no polling at all. */ /** the priority for polling, or 0 for no polling at all. */
unsigned char m_pollPriority; 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. */ /** the last seen master data. */
SymbolString m_lastMasterData; SymbolString m_lastMasterData;
@@ -341,6 +389,110 @@ struct compareMessagePriority : binary_function <Message*,Message*,bool> {
}; };
/**
* Helper class extending @a priority_queue to hold distinct values only.
*/
class MessagePriorityQueue
: public priority_queue<Message*, vector<Message*>, 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<Message*>::iterator it = c.begin(); it != c.end(); it++) {
if (*it==__x) {
c.erase(it);
break;
}
}
priority_queue<Message*, vector<Message*>, 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<unsigned int> 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<unsigned>(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<string>::iterator& it, const vector<string>::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<unsigned int> 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. * Holds a map of all known @a Message instances.
*/ */
@@ -352,7 +504,7 @@ public:
* Construct a new instance. * Construct a new instance.
*/ */
MessageMap() : FileReader<DataFieldTemplates*>::FileReader(true), MessageMap() : FileReader<DataFieldTemplates*>::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. * Destructor.
@@ -367,9 +519,20 @@ public:
*/ */
result_t add(Message* message); result_t add(Message* message);
// @copydoc
virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row, vector<string>::iterator& begin, const string& filename, unsigned int lineNo);
// @copydoc // @copydoc
virtual result_t addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end, DataFieldTemplates* arg, vector< vector<string> >* defaults, const string& filename, unsigned int lineNo); virtual result_t addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end, DataFieldTemplates* arg, vector< vector<string> >* 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. * Find the @a Message instance for the specified circuit and name.
* @param circuit the optional circuit name. * @param circuit the optional circuit name.
@@ -421,8 +584,9 @@ public:
/** /**
* Add a @a Message to the list of instances to poll. * Add a @a Message to the list of instances to poll.
* @param message the @a Message 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. * Removes all @a Message instances.
@@ -430,11 +594,22 @@ public:
void clear(); void clear();
/** /**
* Get the number of stored @a Message instances. * Get the number of all stored @a Message instances.
* @param passiveOnly true to count only passive messages, false to count all messages. * @return the the number of all stored @a Message instances.
* @return the the number of 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. * Get the number of stored @a Message instances with a poll priority.
@@ -449,6 +624,18 @@ public:
*/ */
Message* getNextPoll(); 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<string, Condition*>& getConditions() { return m_conditions; }
/** /**
* Write the message definitions to the @a ostream. * Write the message definitions to the @a ostream.
* @param output the @a ostream to append the formatted messages to. * @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. */ /** the number of distinct @a Message instances stored in @a m_messagesByName. */
size_t m_messageCount; 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. */ /** the number of distinct passive @a Message instances stored in @a m_messagesByKey. */
size_t m_passiveMessageCount; size_t m_passiveMessageCount;
/** the known @a Message instances by lowercase circuit and name. */ /** the known @a Message instances by lowercase circuit and name. */
map<string, Message*> m_messagesByName; map<string, vector<Message*> > m_messagesByName;
/** the known @a Message instances by key. */ /** the known @a Message instances by key. */
map<unsigned long long, Message*> m_messagesByKey; map<unsigned long long, vector<Message*> > m_messagesByKey;
/** the known @a Message instances to poll, by priority. */ /** the known @a Message instances to poll, by priority. */
priority_queue<Message*, vector<Message*>, compareMessagePriority> m_pollMessages; MessagePriorityQueue m_pollMessages;
/** the @a Condition instances by filename and condition name. */
map<string, Condition*> m_conditions;
}; };