changed from m_isActive to m_isPassive, made defaults more flexible (every char besides r/w/p/0-9 can be used for defining messages to listen to - aka cyc), fix for defaults field list and duplicate message definitions, multiple message types now need to be separated by comma, optimized finding messages

This commit is contained in:
john30
2014-11-30 20:12:59 +01:00
parent 46fb6763bc
commit 377c9c61c0
2 changed files with 150 additions and 127 deletions
+108 -93
View File
@@ -28,21 +28,21 @@
using namespace std;
Message::Message(const string clazz, const string name, const bool isSet,
const bool isActive, const string comment,
const bool isPassive, const string comment,
const unsigned char srcAddress, const unsigned char dstAddress,
const vector<unsigned char> id, DataField* data,
const unsigned int pollPriority)
: m_class(clazz), m_name(name), m_isSet(isSet),
m_isActive(isActive), m_comment(comment),
m_isPassive(isPassive), m_comment(comment),
m_srcAddress(srcAddress), m_dstAddress(dstAddress),
m_id(id), m_data(data), m_pollPriority(pollPriority)
{
int exp = 7;
unsigned long long key = (unsigned long long)(id.size()-2) << (8 * exp + 5);
if (isActive == true)
key |= 0x1fLL << (8 * exp--);
if (isPassive == true)
key |= (unsigned long long)getMasterNumber(srcAddress) << (8 * exp--); // 0..25
else
key |= (unsigned long long)getMasterNumber(srcAddress) << (8 * exp--);
key |= 0x1fLL << (8 * exp--); // special value for active
key |= (unsigned long long)dstAddress << (8 * exp--);
for (vector<unsigned char>::const_iterator it=id.begin(); it<id.end(); it++)
key |= (unsigned long long)(*it) << (8 * exp--);
@@ -52,26 +52,36 @@ Message::Message(const string clazz, const string name, const bool isSet,
/**
* @brief Helper method for getting a default if the value is empty.
* @param value the value to check.
* @param defaults a @a verctor of defaults, or NULL.
* @param defaults a @a vector of defaults, or NULL.
* @param pos the position in defaults.
* @return the default if available and value is empty, or the value.
*/
string getDefault(string value, vector<string>* defaults, size_t pos)
{
/*cout<<"getDefault("<<value<<",";
if (defaults==NULL)
cout<<"NULL";
else
cout<<static_cast<unsigned>(defaults->size());
cout<<","<<static_cast<unsigned>(pos)<<"=";*/
if (value.length() > 0 || defaults == NULL || pos > defaults->size()) {
//cout<<value<<endl;
return value;
}
string ret = defaults->at(pos);
return ret;
value = defaults->at(pos);
//cout<<value<<endl;
return value;
}
result_t Message::create(vector<string>::iterator& it, const vector<string>::iterator end,
vector<string>* defaults, DataFieldTemplates* templates, Message*& returnValue)
vector< vector<string> >* defaultsRows,
DataFieldTemplates* templates, Message*& returnValue)
{
// [type];[class];name;[comment];[QQ];ZZ;id;fields...
result_t result;
bool isSet, isActive;
bool isSet = false, isPassive = true;
char defaultsChar;
unsigned int pollPriority = 0;
size_t defaultPos = 1;
if (it == end)
@@ -80,15 +90,15 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
const char* str = (*it++).c_str();
if (it == end)
return RESULT_ERR_EOF;
if (strcasecmp(str, "W") == 0) {
isActive = true;
if (str[0] == 0 || strcasecmp(str, "R") == 0) { // default: active get
isPassive = false;
defaultsChar = 'r';
} else if (strcasecmp(str, "W") == 0) { // active set
isPassive = false;
isSet = true;
} else if (str[0] == 'C' || str[0] == 'c') {
isActive = false;
isSet = str[1] == 'W' || str[1] == 'w';
} else if (str[0] == 'P' || str[0] == 'p') { // poll priority
isActive = true;
isSet = false;
defaultsChar = 'w';
} else if (str[0] == 'P' || str[0] == 'p') { // poll (=active get)
isPassive = false;
if (str[1] == 0)
pollPriority = 1;
else {
@@ -97,16 +107,28 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
if (result != RESULT_OK)
return result;
}
} else if (str[0] >= '0' && str[0] <= '9') { // poll priority
isActive = true;
isSet = false;
defaultsChar = 'r';
} else if (str[0] >= '0' && str[0] <= '9') { // poll priority (=active get)
isPassive = false;
result_t result;
pollPriority = parseInt(str, 10, 1, 9, result);
if (result != RESULT_OK)
return result;
} else { // default "r"
isActive = true;
isSet = false;
defaultsChar = 'r';
} else { // any other: passive set/get
isSet = str[1] == 'W' || str[1] == 'w';
defaultsChar = str[0];
}
vector<string>* defaults = NULL;
if (defaultsRows != NULL && defaultsRows->size() > 0) {
for (vector< vector<string> >::reverse_iterator it = defaultsRows->rbegin(); it != defaultsRows->rend(); it++) {
string check = (*it)[0];
if (check[0] == defaultsChar) {
defaults = &(*it);
break;
}
}
}
string clazz = getDefault(*it++, defaults, defaultPos++);
@@ -153,9 +175,9 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
string token = *it++;
if (useDefaults == 1) {
if (pos == 0 && token.size() > 0) {
useDefaults = false;
useDefaults = 0;
} else {
token.append(getDefault("", defaults, defaultPos));
token = getDefault("", defaults, defaultPos).append(token);
}
}
istringstream input(token);
@@ -189,9 +211,9 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
}
vector<string>::iterator realEnd = end;
if (defaults!=NULL && defaults->size() > defaultPos + 3) { // need at least "[name];[part];type" (optional: "[divisor|values][;[unit][;[comment]]]]")
vector<string> newTypes;
while (defaults->at(defaultPos + 3).size() > 0) {
vector<string> newTypes;
if (defaults!=NULL && defaults->size() > defaultPos + 2) { // need at least "[name];[part];type" (optional: "[divisor|values][;[unit][;[comment]]]]")
while (defaults->size() > defaultPos + 2 && defaults->at(defaultPos + 2).size() > 0) {
for (size_t i = 0; i < 6; i++) {
if (defaults->size() > defaultPos)
newTypes.push_back(defaults->at(defaultPos));
@@ -200,8 +222,6 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
defaultPos++;
}
if (defaults->size() <= defaultPos + 3)
break;
}
if (newTypes.size() > 0) {
while (it != end) {
@@ -216,74 +236,77 @@ result_t Message::create(vector<string>::iterator& it, const vector<string>::ite
if (result != RESULT_OK) {
return result;
}
returnValue = new Message(clazz, name, isSet, isActive, comment, srcAddress, dstAddress, id, data, pollPriority);
returnValue = new Message(clazz, name, isSet, isPassive, comment, srcAddress, dstAddress, id, data, pollPriority);
return RESULT_OK;
}
result_t Message::prepare(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator)
result_t Message::prepareMaster(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator)
{
if (m_isActive == true) {
masterData.clear();
masterData.push_back(srcAddress, false);
masterData.push_back(m_dstAddress, false);
masterData.push_back(m_id[0], false);
masterData.push_back(m_id[1], false);
unsigned char addData = m_data->getLength(pt_masterData);
masterData.push_back(m_id.size() - 2 + addData, false);
for (size_t i=2; i<m_id.size(); i++)
masterData.push_back(m_id[i], false);
SymbolString slaveData;
result_t result = m_data->write(input, masterData, m_id.size() - 2, slaveData, 0, separator);
if (result != RESULT_OK)
return result;
masterData.push_back(masterData.getCRC(), false, false);
return RESULT_OK;
}
return RESULT_ERR_INVALID_ARG; // prepare not possible
if (m_isPassive == true)
return RESULT_ERR_INVALID_ARG; // prepare not possible
masterData.clear();
masterData.push_back(srcAddress, false);
masterData.push_back(m_dstAddress, false);
masterData.push_back(m_id[0], false);
masterData.push_back(m_id[1], false);
unsigned char addData = m_data->getLength(pt_masterData);
masterData.push_back(m_id.size() - 2 + addData, false);
for (size_t i=2; i<m_id.size(); i++)
masterData.push_back(m_id[i], false);
SymbolString slaveData;
result_t result = m_data->write(input, masterData, m_id.size() - 2, slaveData, 0, separator);
if (result != RESULT_OK)
return result;
masterData.push_back(masterData.getCRC(), false, false);
/*if (slaveData.size() > 0) {
return RESULT_ERR_INVALID_ARG; // TODO support answering MS queries (set slave length, calc crc)
}*/
return RESULT_OK;
}
result_t Message::decode(SymbolString& masterData, SymbolString& slaveData,
ostringstream& output, char separator, bool answer)
ostringstream& output, char separator)
{
result_t result = m_data->read(masterData, m_id.size() - 2, slaveData, 0, output, false, separator);
if (result != RESULT_OK)
return result;
if (m_isActive == true && answer == true) {
/*if (m_isPassive == false && answer == true) {
istringstream input; // TODO create input from database of internal variables
result_t result = m_data->write(input, masterData, m_id.size() - 2, slaveData, 0, separator);
if (result != RESULT_OK)
return result;
}
}*/
return RESULT_OK;
}
result_t MessageMap::add(Message* message)
{
string key = message->getClass().append(";").append(message->getName());
if (message->isActive() == true)
if (message->isPassive() == false) {
string key = message->getClass().append(";").append(message->getName());
key.append(message->isSet() ? ";W" : ";R");
else
key.append(";C");
map<string, Message*>::iterator nameIt = m_messagesByName.find(key);
if (nameIt != m_messagesByName.end()) {
return RESULT_ERR_DUPLICATE; // duplicate key
}
if (message->isActive() == false) {
unsigned long long pkey = message->getKey();
map<unsigned long long, Message*>::iterator keyIt = m_passiveMessagesByKey.find(pkey);
if (keyIt != m_passiveMessagesByKey.end()) {
map<string, Message*>::iterator nameIt = m_messagesByName.find(key);
if (nameIt != m_messagesByName.end()) {
return RESULT_ERR_DUPLICATE; // duplicate key
}
unsigned char idLength = message->getId().size() - 2;
if (idLength > m_maxIdLength)
m_maxIdLength = idLength;
m_passiveMessagesByKey[pkey] = message;
m_messagesByName[key] = message;
return RESULT_OK;
}
m_messagesByName[key] = message;
unsigned long long key = message->getKey();
map<unsigned long long, Message*>::iterator keyIt = m_passiveMessagesByKey.find(key);
if (keyIt != m_passiveMessagesByKey.end()) {
return RESULT_ERR_DUPLICATE; // duplicate key
}
unsigned char idLength = message->getId().size() - 2;
if (idLength < m_minIdLength)
m_minIdLength = idLength;
if (idLength > m_maxIdLength)
m_maxIdLength = idLength;
m_passiveMessagesByKey[key] = message;
return RESULT_OK;
}
@@ -296,19 +319,12 @@ result_t MessageMap::addFromFile(vector<string>& row, DataFieldTemplates* arg, v
types.append("r");
result_t result = RESULT_ERR_EOF;
for (size_t i=0; i<types.length(); i++) {
string type = types.substr(i, 1);
vector<string>* defaultRow = NULL;
if (defaults != NULL && defaults->size() > 0) {
for (vector< vector<string> >::reverse_iterator it = defaults->rbegin(); it != defaults->rend(); it++) {
if ((*it)[0] == type || (type[0] >= '0' && type[0] <= '9' && ((*it)[0][0] == 'r' && (*it)[0][0] == 'R'))) {
defaultRow = &(*it);
break;
}
}
}
istringstream stream(types);
string type;
while (getline(stream, type, ',') != 0) {
row[0] = type;
vector<string>::iterator it = row.begin();
result = Message::create(it, row.end(), defaultRow, arg, message);
result = Message::create(it, row.end(), defaults, arg, message);
if (result != RESULT_OK) {
printErrorPos(row.begin(), row.end(), it);
return result;
@@ -321,15 +337,11 @@ result_t MessageMap::addFromFile(vector<string>& row, DataFieldTemplates* arg, v
return result;
}
Message* MessageMap::find(const string& clazz, const string& name, const bool isActive, const bool isSet)
Message* MessageMap::find(const string& clazz, const string& name, const bool isSet)
{
string key = clazz;
for (int i=0; i<2; i++) {
key.append(";").append(name);
if (isActive == true)
key.append(isSet ? ";W" : ";R");
else
key.append(";C");
key.append(";").append(name).append(isSet ? ";W" : ";R");
map<string, Message*>::iterator it = m_messagesByName.find(key);
if (it != m_messagesByName.end())
return it->second;
@@ -339,17 +351,20 @@ Message* MessageMap::find(const string& clazz, const string& name, const bool is
return NULL;
}
Message* MessageMap::find(SymbolString& master) {
Message* MessageMap::find(SymbolString& master)
{
if (master.size() < 5)
return NULL;
unsigned char maxIdLength = master[4];
unsigned char maxIdLength = master[4];
if (maxIdLength < m_minIdLength)
return NULL;
if (maxIdLength > m_maxIdLength)
maxIdLength = m_maxIdLength;
if (master.size() < 5+maxIdLength)
return NULL;
unsigned long long sourceMask = 0x1fLL << (8 * 7);
for (int idLength=maxIdLength; idLength>=0; idLength--) {
for (int idLength=maxIdLength; idLength>=m_maxIdLength; idLength--) {
int exp = 7;
unsigned long long key = (unsigned long long)idLength << (8 * exp + 5);
key |= (unsigned long long)getMasterNumber(master[0]) << (8 * exp--);
+42 -34
View File
@@ -37,22 +37,21 @@ class Message
public:
/**
* @brief Constructs a new instance.
* @brief Construct a new instance.
* @param class the optional device class.
* @param name the message name (unique within the same class and type).
* @param isSet whether this is a set message.
* @param isActive true if message can be initiated by the daemon
* itself any any other participant, false if message can only be initiated
* by a participant other than the daemon.
* @param isPassive true if message can only be initiated by a participant other than us,
* false if message can be initiated by any participant.
* @param comment the comment.
* @param srcAddress the source address (optional if passive), or @a SYN for any.
* @param srcAddress the source address, or @a SYN for any (only relevant if passive).
* @param dstAddress the destination address.
* @param id the primary, secondary, and optional further ID bytes.
* @param data the @a DataField for encoding/decoding the message.
* @param pollPriority the priority for polling, or 0 for no polling at all.
*/
Message(const string clazz, const string name, const bool isSet,
const bool isActive, const string comment,
const bool isPassive, const string comment,
const unsigned char srcAddress, const unsigned char dstAddress,
const vector<unsigned char> id, DataField* data,
const unsigned int pollPriority);
@@ -64,15 +63,15 @@ public:
* @brief 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 defaults a @a vector with known default values, or NULL.
* @param defaultsRows a @a vector with rows containing defaults, or NULL.
* @param templates the @a DataFieldTemplates to be referenced by name, or NULL.
* @param returnValue the variable in which to store the created instance.
* @return @a RESULT_OK on success, or an error code.
* Note: the caller needs to free the created instance.
*/
static result_t create(vector<string>::iterator& it, const vector<string>::iterator end,
vector<string>* defaults,
DataFieldTemplates*, Message*& returnValue);
vector< vector<string> >* defaultsRows,
DataFieldTemplates* templates, Message*& returnValue);
/**
* @brief Get the optional device class.
* @return the optional device class.
@@ -89,13 +88,11 @@ public:
*/
bool isSet() const { return m_isSet; }
/**
* @brief Get whether message can be initiated by the daemon itself and any other
* participant.
* @return true if message can be initiated by the daemon itself and any other
* participant, false if message can only be initiated by a participant
* other than the daemon.
* @brief Get whether message can be initiated only by a participant other than us.
* @return true if message can only be initiated by a participant other than us,
* false if message can be initiated by any participant.
*/
bool isActive() const { return m_isActive; }
bool isPassive() const { return m_isPassive; }
/**
* @brief Get the comment.
* @return the comment.
@@ -117,7 +114,7 @@ public:
*/
vector<unsigned char> getId() const { return m_id; }
/**
* @brief Returns the key for storing in @a MessageSet.
* @brief Return the key for storing in @a MessageSet.
* @return the key for storing in @a MessageSet.
*/
unsigned long long getKey() { return m_key; }
@@ -134,17 +131,24 @@ public:
//result_t read(SymbolString& masterData, SymbolString& slaveData, ostringstream& output,
// bool verbose=false, char separator=';') = 0;
/**
* @brief Writes the value to the master or slave @a SymbolString.
* @param input the @a istringstream to parse the formatted value from.
* @param masterData the unescaped master data @a SymbolString for writing binary data.
* @param slaveData the unescaped slave data @a SymbolString for writing binary data.
* @brief Prepare master @a SymbolString for sending to the bus.
* @param masterData the master data @a SymbolString for writing symbols to.
* @param input the @a istringstream to parse the formatted value(s) from.
* @param separator the separator character between multiple fields.
* @return @a RESULT_OK on success, or an error code.
*/
result_t prepare(const unsigned char srcAddress, SymbolString& masterData,
result_t prepareMaster(const unsigned char srcAddress, SymbolString& masterData,
istringstream& input, char separator=';');
/**
* @brief Decode a received message.
* @param masterData the unescaped received master @a SymbolString.
* @param slaveData the unescaped received slave @a SymbolString.
* @param output the @a ostringstream to append the formatted value to.
* @param separator the separator character between multiple fields.
* @return @a RESULT_OK on success, or an error code.
*/
result_t decode(SymbolString& masterData, SymbolString& slaveData,
ostringstream& output, char separator=';', bool answer=false);
ostringstream& output, char separator=';');
private:
@@ -154,13 +158,12 @@ private:
const string m_name;
/** whether this is a set message. */
const bool m_isSet;
/** true if message can be initiated by the daemon itself and any other
* participant, false if message can only be initiated by a participant
* other than the daemon. */
const bool m_isActive;
/** true if message can only be initiated by a participant other than us,
* false if message can be initiated by any participant. */
const bool m_isPassive;
/** the comment. */
const string m_comment;
/** the source address (optional if passive), or @a SYN for any. */
/** the source address, or @a SYN for any (only relevant if passive). */
const unsigned char m_srcAddress;
/** the destination address. */
const unsigned char m_dstAddress;
@@ -183,15 +186,15 @@ class MessageMap : public FileReader<DataFieldTemplates*>
public:
/**
* @brief Constructs a new instance.
* @brief Construct a new instance.
*/
MessageMap() : FileReader(true), m_maxIdLength(0) {}
MessageMap() : FileReader(true), m_minIdLength(4), m_maxIdLength(0) {}
/**
* @brief Destructor.
*/
virtual ~MessageMap() { clear(); }
/**
* @brief Adds a @a Message instance to this set.
* @brief Add a @a Message instance to this set.
* @param message the @a Message instance to add.
* @return @a RESULT_OK on success, or an error code.
* Note: the caller may not free the added instance on success.
@@ -200,14 +203,16 @@ public:
// @copydoc
virtual result_t addFromFile(vector<string>& row, DataFieldTemplates* arg, vector< vector<string> >* defaults);
/**
* @brief Finds the @a Message instance for the specified class and name.
* @param master the master @a SymbolString for identifying the @a Message.
* @brief Find the @a Message instance for the specified class and name.
* @param class the optional device class.
* @param name the message name.
* @param isSet whether this is a set message.
* @return the @a Message instance, or NULL.
* Note: the caller may not free the returned instance.
*/
Message* find(const string& clazz, const string& name, const bool isActive, const bool isSet);
Message* find(const string& clazz, const string& name, const bool isSet);
/**
* @brief Finds the @a Message instance for the specified master data.
* @brief Find the @a Message instance for the specified master data.
* @param master the master @a SymbolString for identifying the @a Message.
* @return the @a Message instance, or NULL.
* Note: the caller may not free the returned instance.
@@ -221,6 +226,9 @@ public:
private:
/** the minimum ID length used by any of the known @a Message instances. */
unsigned char m_minIdLength;
/** the maximum ID length used by any of the known @a Message instances. */
unsigned char m_maxIdLength;