Files
ebusd/src/lib/ebus/message.cpp
T

2262 lines
71 KiB
C++

/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2014-2017 John Baier <ebusd@ebusd.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/ebus/message.h"
#include <string>
#include <vector>
#include <cstring>
#include <locale>
#include <iomanip>
#include <climits>
#include "lib/ebus/data.h"
#include "lib/ebus/result.h"
#include "lib/ebus/symbol.h"
namespace ebusd {
using std::dec;
using std::hex;
using std::nouppercase;
using std::setfill;
using std::setw;
/** the maximum length of the command ID bytes (in addition to PB/SB) for which the key is distinct. */
#define MAX_ID_KEYLEN 4
/** the bit mask of the source master number in the message key. */
#define ID_SOURCE_MASK (0x1fLL << (8 * 7))
/** the bit mask for the ID length and combined ID bytes in the message key. */
#define ID_LENGTH_AND_IDS_MASK ((7LL << (8 * 7 + 5)) | 0xffffffffLL)
/** the bits in the @a ID_SOURCE_MASK for arbitrary source and active read message. */
#define ID_SOURCE_ACTIVE_WRITE (0x1fLL << (8 * 7))
/** the bits in the @a ID_SOURCE_MASK for arbitrary source and active write message. */
#define ID_SOURCE_ACTIVE_READ (0x1eLL << (8 * 7))
/** special value for invalid message key. */
#define INVALID_KEY 0xffffffffffffffffLL
/** the maximum poll priority for a @a Message referred to by a @a Condition. */
#define POLL_PRIORITY_CONDITION 5
extern DataFieldTemplates* getTemplates(const string filename);
Message::Message(const string circuit, const string level, const string name,
const bool isWrite, const bool isPassive, const string comment,
const symbol_t srcAddress, const symbol_t dstAddress,
const vector<symbol_t> id,
DataField* data, const bool deleteData,
const size_t pollPriority,
Condition* condition)
: m_circuit(circuit), m_level(level), 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_isScanMessage(false), m_condition(condition),
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) {
m_key = createKey(id, isWrite, isPassive, srcAddress, dstAddress);
if (circuit == "scan") {
setScanMessage();
m_pollPriority = 0;
}
}
Message::Message(const string circuit, const string level, const string name,
const symbol_t pb, const symbol_t sb,
const bool broadcast, DataField* data, const bool deleteData)
: m_circuit(circuit), m_level(level), m_name(name), m_isWrite(broadcast),
m_isPassive(false), m_comment(),
m_srcAddress(SYN), m_dstAddress(broadcast ? BROADCAST : SYN),
m_data(data), m_deleteData(deleteData),
m_pollPriority(0),
m_usedByCondition(false), m_isScanMessage(true), m_condition(NULL),
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) {
m_id.push_back(pb);
m_id.push_back(sb);
uint64_t key = 0;
key |= (broadcast ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active
key |= (uint64_t)(broadcast ? BROADCAST : SYN) << (8 * 6);
key |= (uint64_t)pb << (8 * 5);
key |= (uint64_t)sb << (8 * 4);
m_key = key;
}
/**
* Helper method for getting a default if the value is empty.
* @param value the value to check.
* @param defaults a @a vector of defaults, or NULL.
* @param pos the position in defaults.
* @param replaceStar whether to replace a star in the default with the value.
* If there is no star in the default and the value is empty, use the complete
* default instead.
* @param required don't combine the value with the default when the value is
* empty and @p replaceStar is @p true.
* @return the default if available and value is empty, or the value.
*/
string getDefault(const string value, vector<string>* defaults, size_t pos, bool replaceStar = false,
bool required = false) {
if (defaults == NULL || pos >= defaults->size()) {
return value;
}
if (value.length() == 0 && replaceStar && required) {
return value;
}
string defaultStr = defaults->at(pos);
if (!replaceStar || defaultStr.length() == 0) {
return value.length() > 0 ? value : defaultStr;
}
string::size_type insertPos = defaultStr.find('*');
if (insertPos == string::npos) {
return value.length() == 0 ? defaultStr : value;
}
return defaultStr.substr(0, insertPos)+value+defaultStr.substr(insertPos+1);
}
uint64_t Message::createKey(const vector<symbol_t> id,
const bool isWrite, const bool isPassive,
const symbol_t srcAddress, const symbol_t dstAddress) {
uint64_t key = (uint64_t)(id.size()-2) << (8 * 7 + 5);
if (isPassive) {
key |= (uint64_t)getMasterNumber(srcAddress) << (8 * 7); // 0..25
} else {
key |= (isWrite ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active
}
key |= (uint64_t)dstAddress << (8 * 6);
int exp = 5;
for (vector<symbol_t>::const_iterator it = id.begin(); it < id.end(); it++) {
key ^= (uint64_t)*it << (8 * exp--);
if (exp == 0) {
exp = 3;
}
}
return key;
}
uint64_t Message::createKey(MasterSymbolString& master, size_t maxIdLength, bool anyDestination) {
if (master.size() < 5) {
return INVALID_KEY;
}
size_t idLength = master.getDataSize();
if (maxIdLength < idLength) {
idLength = maxIdLength;
}
if (master.getDataSize() < idLength) {
return INVALID_KEY;
}
uint64_t key = (uint64_t)idLength << (8 * 7 + 5);
key |= (uint64_t)getMasterNumber(master[0]) << (8 * 7); // QQ address for passive message
key |= (uint64_t)(anyDestination ? SYN : master[1]) << (8 * 6); // ZZ address
key |= (uint64_t)master[2] << (8 * 5); // PB
key |= (uint64_t)master[3] << (8 * 4); // SB
int exp = 3;
for (size_t i = 0; i < idLength; i++) {
key ^= (uint64_t)master.dataAt(i) << (8 * exp--);
if (exp == 0) {
exp = 3;
}
}
return key;
}
result_t Message::parseId(string input, vector<symbol_t>& id) {
istringstream in(input);
while (!in.eof()) {
while (in.peek() == ' ') {
in.get();
}
if (in.eof()) { // no more digits
break;
}
input.clear();
input.push_back(static_cast<char>(in.get()));
if (in.eof()) {
return RESULT_ERR_INVALID_ARG; // too short hex
}
input.push_back(static_cast<char>(in.get()));
result_t result;
symbol_t value = (symbol_t)parseInt(input.c_str(), 16, 0, 0xff, result);
if (result != RESULT_OK) {
return result; // invalid hex value
}
id.push_back(value);
}
return RESULT_OK;
}
result_t Message::create(vector<string>::iterator& it, const vector<string>::iterator end,
vector< vector<string> >* defaultsRows, Condition* condition, const string& filename,
DataFieldTemplates* templates, vector<Message*>& messages) {
// [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
result_t result;
bool isWrite = false, isPassive = false;
string defaultName;
size_t pollPriority = 0;
size_t defaultPos = 1;
if (it == end) {
return RESULT_ERR_EOF;
}
string typeStr = *it++;
const char* str = typeStr.c_str(); // [type]
if (it == end) {
return RESULT_ERR_EOF;
}
size_t len = strlen(str);
if (len == 0) { // default: active read
defaultName = "r";
} else {
defaultName = str;
char type = str[0];
if (type == 'r' || type == 'R') { // active read
char poll = str[1];
if (poll >= '0' && poll <= '9') { // poll priority (=active read)
pollPriority = poll - '0';
defaultName.erase(1, 1); // cut off priority digit
}
} else if (type == 'w' || type == 'W') { // active write
isWrite = true;
} else { // any other: passive read/write
isPassive = true;
type = str[1];
isWrite = type == 'w' || type == 'W'; // if type continues with "w" it is treated as passive write
}
}
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 == defaultName) {
defaults = &(*it);
break;
}
}
}
string circuit = getDefault(*it++, defaults, defaultPos++, true); // [circuit[#level]]
if (it == end) {
return RESULT_ERR_EOF;
}
string level;
size_t pos = circuit.find('#');
if (pos != string::npos) {
level = circuit.substr(pos+1);
circuit.resize(pos);
}
string name = getDefault(*it++, defaults, defaultPos++, true, true); // name
if (it == end) {
return RESULT_ERR_EOF;
}
if (name.length() == 0) {
return RESULT_ERR_INVALID_ARG; // empty name
}
string comment = getDefault(*it++, defaults, defaultPos++, true); // [comment]
if (it == end) {
return RESULT_ERR_EOF;
}
str = getDefault(*it++, defaults, defaultPos++).c_str(); // [QQ[;QQ]*]
if (it == end) {
return RESULT_ERR_EOF;
}
symbol_t srcAddress;
if (*str == 0) {
srcAddress = SYN; // no specific source
} else {
srcAddress = (symbol_t)parseInt(str, 16, 0, 0xff, result);
if (result != RESULT_OK) {
return result;
}
if (!isMaster(srcAddress)) {
return RESULT_ERR_INVALID_ADDR;
}
}
str = getDefault(*it++, defaults, defaultPos++).c_str(); // [ZZ]
if (it == end) {
return RESULT_ERR_EOF;
}
vector<symbol_t> dstAddresses;
bool isBroadcastOrMasterDestination = false;
if (*str == 0) {
dstAddresses.push_back(SYN); // no specific destination
} else {
istringstream stream(str);
string token;
bool first = true;
while (getline(stream, token, VALUE_SEPARATOR)) {
FileReader::trim(token);
symbol_t dstAddress = (symbol_t)parseInt(token.c_str(), 16, 0, 0xff, result);
if (result != RESULT_OK) {
return result;
}
if (!isValidAddress(dstAddress)) {
return RESULT_ERR_INVALID_ADDR;
}
bool broadcastOrMaster = (dstAddress == BROADCAST) || isMaster(dstAddress);
if (first) {
isBroadcastOrMasterDestination = broadcastOrMaster;
first = false;
} else if (isBroadcastOrMasterDestination != broadcastOrMaster) {
return RESULT_ERR_INVALID_ADDR;
}
dstAddresses.push_back(dstAddress);
}
}
vector<symbol_t> id;
string token = *it++; // [PBSB]
bool useDefaults = token.empty();
if (useDefaults) {
token = getDefault(token, defaults, defaultPos);
}
defaultPos++;
result = parseId(token, id);
if (result != RESULT_OK) {
return result;
}
if (id.size() != 2) {
return RESULT_ERR_INVALID_ARG; // missing/to short/to long PBSB
}
if (it == end) {
token = "";
} else {
token = *it++; // [ID] (optional master data)
}
string defaultIdPrefix;
if (useDefaults) {
defaultIdPrefix = getDefault("", defaults, defaultPos);
}
defaultPos++;
vector< vector<symbol_t> > chainIds;
vector<size_t> chainLengths;
istringstream stream(token);
size_t maxLength = MAX_POS;
size_t chainLength = 16;
size_t chainPrefixLength = id.size();
bool first = true, lastChainLengthSpecified = false;
while (getline(stream, token, VALUE_SEPARATOR) || first) {
FileReader::trim(token);
token = defaultIdPrefix+token;
size_t lengthPos = token.find(LENGTH_SEPARATOR);
lastChainLengthSpecified = lengthPos != string::npos;
if (lastChainLengthSpecified) {
chainLength = parseInt(token.substr(lengthPos+1).c_str(), 10, 0, MAX_POS, result);
if (result != RESULT_OK) {
return result;
}
token.resize(lengthPos);
}
vector<symbol_t> chainId = id;
result = parseId(token, chainId);
if (result != RESULT_OK) {
return result;
}
if (!chainIds.empty() && chainId.size() != chainIds.front().size()) {
return RESULT_ERR_INVALID_LIST;
}
chainIds.push_back(chainId);
chainLengths.push_back((symbol_t)chainLength);
if (first) {
chainPrefixLength = chainId.size();
maxLength = 0;
} else if (chainPrefixLength > 2) {
vector<symbol_t>& front = chainIds.front();
for (size_t pos = 2; pos < chainPrefixLength; pos++) {
if (chainId[pos] != front[pos]) {
chainPrefixLength = pos;
break;
}
}
}
if (maxLength+chainLength > 255) {
return RESULT_ERR_INVALID_POS;
}
maxLength += chainLength;
first = false;
}
id = chainIds.front();
if (chainIds.size() > 1) {
if (isPassive) {
return RESULT_ERR_INVALID_LIST;
}
if (id.size() > chainPrefixLength) {
id.resize(chainPrefixLength);
}
if (!lastChainLengthSpecified && chainLength < MAX_POS) {
maxLength += MAX_POS-chainLength;
}
} else if (!lastChainLengthSpecified) {
maxLength = MAX_POS;
}
vector<string>::iterator realEnd = end;
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));
} else {
newTypes.push_back("");
}
defaultPos++;
}
}
if (newTypes.size() > 0) {
while (it != end) {
newTypes.push_back(*it++);
}
it = newTypes.begin();
realEnd = newTypes.end();
}
}
DataField* data = NULL;
if (it == realEnd) {
vector<SingleDataField*> fields;
data = new DataFieldSet("", "", fields);
} else {
result = DataField::create(it, realEnd, templates, data, isWrite, false, isBroadcastOrMasterDestination,
maxLength);
if (result != RESULT_OK) {
return result;
}
}
if (id.size() + data->getLength(pt_masterData, maxLength) > 2 + maxLength
|| data->getLength(pt_slaveData, maxLength) > maxLength) {
// max NN exceeded
delete data;
return RESULT_ERR_INVALID_POS;
}
unsigned int index = 0;
bool multiple = dstAddresses.size() > 1;
char num[10];
for (vector<symbol_t>::iterator it = dstAddresses.begin(); it != dstAddresses.end(); it++, index++) {
symbol_t dstAddress = *it;
string useCircuit = circuit;
if (multiple) {
snprintf(num, sizeof(num), ".%d", index);
useCircuit = useCircuit + num;
}
Message* message;
if (chainIds.size() > 1) {
message = new ChainedMessage(useCircuit, level, name, isWrite, comment, srcAddress, dstAddress, id, chainIds,
chainLengths, data, index == 0, pollPriority, condition);
} else {
message = new Message(useCircuit, level, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data,
index == 0, pollPriority, condition);
}
messages.push_back(message);
}
return RESULT_OK;
}
Message* Message::createScanMessage(bool broadcast) {
return new Message("scan", "", "", 0x07, 0x04, broadcast, DataFieldSet::getIdentFields(), !broadcast);
}
Message* Message::derive(const symbol_t dstAddress, const symbol_t srcAddress, const string circuit) {
Message* result = new Message(circuit.length() == 0 ? m_circuit : circuit, m_level, m_name,
m_isWrite, m_isPassive, m_comment,
srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress,
m_id, m_data, false,
m_pollPriority, m_condition);
if (m_isScanMessage) {
result->setScanMessage();
}
return result;
}
Message* Message::derive(const symbol_t dstAddress, const bool extendCircuit) {
if (extendCircuit) {
ostringstream out;
out << m_circuit << '.' << hex << setw(2) << setfill('0') << static_cast<unsigned>(dstAddress);
return derive(dstAddress, SYN, out.str());
}
return derive(dstAddress, SYN, m_circuit);
}
bool Message::checkLevel(const string level, const string checkLevels) {
if (level.empty()) {
return true;
}
if (checkLevels.empty()) {
return false;
}
if (checkLevels == "*") {
return true;
}
size_t len = level.length();
size_t maxLen = checkLevels.length();
for (size_t pos = checkLevels.find(level); pos != string::npos && pos + len <= maxLen;
pos = checkLevels.find(level, pos)) {
if ((pos == 0 || checkLevels[pos - 1] == VALUE_SEPARATOR)
&& (pos + len == maxLen || checkLevels[pos + len] == VALUE_SEPARATOR)) {
return true;
}
pos += len;
}
return false;
}
bool Message::checkIdPrefix(vector<symbol_t>& id) {
if (id.size() > m_id.size()) {
return false;
}
for (size_t pos = 0; pos < id.size(); pos++) {
if (id[pos] != m_id[pos]) {
return false;
}
}
return true;
}
bool Message::checkId(MasterSymbolString& master, size_t* index) {
size_t idLen = getIdLength();
if (master.getDataSize() < idLen) {
return false;
}
for (size_t pos = 0; pos < idLen; pos++) {
if (m_id[2+pos] != master.dataAt(pos)) {
return false;
}
}
if (index) {
*index = 0;
}
return true;
}
bool Message::checkId(Message& other) {
size_t idLen = getIdLength();
if (idLen != other.getIdLength() || getCount() > 1) { // only equal for non-chained messages
return false;
}
return other.checkIdPrefix(m_id);
}
uint64_t Message::getDerivedKey(const symbol_t dstAddress) {
return (m_key & ~(0xffLL << (8*6))) | (uint64_t)dstAddress << (8*6);
}
bool Message::setPollPriority(size_t priority) {
if (priority == m_pollPriority || m_isPassive || isScanMessage() || m_dstAddress == SYN) {
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 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 symbol_t srcAddress, MasterSymbolString& master,
istringstream& input, char separator,
const symbol_t dstAddress, size_t index) {
if (m_isPassive) {
return RESULT_ERR_INVALID_ARG; // prepare not possible
}
master.clear();
master.push_back(srcAddress);
if (dstAddress == SYN) {
if (m_dstAddress == SYN) {
return RESULT_ERR_INVALID_ADDR;
}
master.push_back(m_dstAddress);
} else {
master.push_back(dstAddress);
}
master.push_back(m_id[0]);
master.push_back(m_id[1]);
result_t result = prepareMasterPart(master, input, separator, index);
if (result != RESULT_OK) {
return result;
}
result = storeLastData(master, index);
if (result < RESULT_OK) {
return result;
}
return RESULT_OK;
}
result_t Message::prepareMasterPart(MasterSymbolString& master, istringstream& input, char separator,
size_t index) {
if (index != 0) {
return RESULT_ERR_NOTFOUND;
}
size_t pos = master.size();
master.push_back(0); // length, will be set later
for (size_t i = 2; i < m_id.size(); i++) {
master.push_back(m_id[i]);
}
result_t result = m_data->write(input, master, getIdLength(), separator);
if (result != RESULT_OK) {
return result;
}
master[pos] = (symbol_t)(master.size()-pos-1);
return result;
}
result_t Message::prepareSlave(istringstream& input, SlaveSymbolString& slave) {
if (m_isWrite) {
return RESULT_ERR_INVALID_ARG; // prepare not possible
}
slave.clear();
slave.push_back(0); // length, will be set later
result_t result = m_data->write(input, slave, 0);
if (result != RESULT_OK) {
return result;
}
slave[0] = (symbol_t)(slave.size()-1);
time(&m_lastUpdateTime);
if (slave != m_lastSlaveData) {
m_lastChangeTime = m_lastUpdateTime;
m_lastSlaveData = slave;
}
return result;
}
result_t Message::storeLastData(MasterSymbolString& master, SlaveSymbolString& slave) {
result_t result = storeLastData(master, 0);
if (result >= RESULT_OK) {
result = storeLastData(slave, 0);
}
return result;
}
result_t Message::storeLastData(MasterSymbolString& data, size_t index) {
if (data.size() > 0
&& (m_isWrite || this->m_dstAddress == BROADCAST || isMaster(this->m_dstAddress))) {
time(&m_lastUpdateTime);
}
switch (data.compareTo(m_lastMasterData)) {
case 1: // completely different
m_lastChangeTime = m_lastUpdateTime;
m_lastMasterData = data;
break;
case 2: // only master address is different
m_lastMasterData = data;
break;
}
return RESULT_OK;
}
result_t Message::storeLastData(SlaveSymbolString& data, size_t index) {
if (data.size() > 0) {
time(&m_lastUpdateTime);
}
if (data != m_lastSlaveData) {
m_lastChangeTime = m_lastUpdateTime;
m_lastSlaveData = data;
}
return RESULT_OK;
}
result_t Message::decodeLastMasterData(ostringstream& output, OutputFormat outputFormat,
bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) {
size_t offset = m_id.size() - 2;
result_t result = m_data->read(m_lastMasterData, offset,
output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex);
if (result < RESULT_OK) {
return result;
}
if (result == RESULT_EMPTY && fieldName != NULL) {
return RESULT_ERR_NOTFOUND;
}
return result;
}
result_t Message::decodeLastSlaveData(ostringstream& output, OutputFormat outputFormat,
bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) {
result_t result = m_data->read(m_lastSlaveData, 0,
output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex);
if (result < RESULT_OK) {
return result;
}
if (result == RESULT_EMPTY && fieldName != NULL) {
return RESULT_ERR_NOTFOUND;
}
return result;
}
result_t Message::decodeLastData(ostringstream& output, OutputFormat outputFormat,
bool leadingSeparator, const char* fieldName, ssize_t fieldIndex) {
size_t startPos = output.str().length();
result_t result = m_data->read(m_lastMasterData, getIdLength(), output, outputFormat, -1,
leadingSeparator, fieldName, fieldIndex);
if (result < RESULT_OK) {
return result;
}
bool empty = result == RESULT_EMPTY;
leadingSeparator |= output.str().length() > startPos;
result = m_data->read(m_lastSlaveData, 0, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex);
if (result < RESULT_OK) {
return result;
}
if (result == RESULT_EMPTY && !empty) {
result = RESULT_OK; // OK if at least one part was non-empty
} else if (result == RESULT_EMPTY && fieldName != NULL) {
return RESULT_ERR_NOTFOUND;
}
return result;
}
result_t Message::decodeLastDataNumField(unsigned int& output, const char* fieldName, ssize_t fieldIndex) {
result_t result = m_data->read(m_lastMasterData, getIdLength(), output, fieldName, fieldIndex);
if (result < RESULT_OK) {
return result;
}
if (result == RESULT_EMPTY) {
result = m_data->read(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) {
size_t tprio = m_pollPriority;
size_t oprio = other->m_pollPriority;
size_t tw = tprio * m_pollCount;
size_t ow = oprio * other->m_pollCount;
if (tw > ow) {
return true;
}
if (tw < ow) {
return false;
}
if (tprio > oprio) {
return true;
}
if (tprio < oprio) {
return false;
}
if (m_lastPollTime > other->m_lastPollTime) {
return true;
}
return false;
}
void Message::dump(ostream& output, vector<column_t>* columns, bool withConditions) {
bool first = true;
if (columns == NULL) {
for (int column = COLUMN_FIRST; column != COLUMN_LAST; column++) {
if (column == COLUMN_LEVEL) {
continue; // access level not included in default dump format
}
if (first) {
first = false;
} else {
output << FIELD_SEPARATOR;
}
dumpColumn(output, static_cast<column_t>(column), withConditions);
}
return;
}
for (auto column : *columns) {
if (first) {
first = false;
} else {
output << FIELD_SEPARATOR;
}
dumpColumn(output, column, withConditions);
}
}
void Message::dumpColumn(ostream& output, column_t column, bool withConditions) {
switch (column) {
case COLUMN_TYPE:
if (withConditions && m_condition != NULL) {
m_condition->dump(output);
}
if (m_isPassive) {
output << "u";
if (m_isWrite) {
output << "w";
}
} else if (m_isWrite) {
output << "w";
} else {
output << "r";
if (m_pollPriority > 0) {
output << static_cast<unsigned>(m_pollPriority);
}
}
break;
case COLUMN_CIRCUIT:
DataField::dumpString(output, m_circuit, false);
break;
case COLUMN_LEVEL:
DataField::dumpString(output, m_level, false);
break;
case COLUMN_NAME:
DataField::dumpString(output, m_name, false);
break;
case COLUMN_COMMENT:
DataField::dumpString(output, m_comment, false);
break;
case COLUMN_QQ:
if (m_srcAddress != SYN) {
output << hex << setw(2) << setfill('0') << static_cast<unsigned>(m_srcAddress);
}
break;
case COLUMN_ZZ:
if (m_dstAddress != SYN) {
output << hex << setw(2) << setfill('0') << static_cast<unsigned>(m_dstAddress);
}
break;
case COLUMN_PBSB:
for (vector<symbol_t>::const_iterator it = m_id.begin(); it < m_id.begin()+2 && it < m_id.end(); it++) {
output << hex << setw(2) << setfill('0') << static_cast<unsigned>(*it);
}
break;
case COLUMN_ID:
for (vector<symbol_t>::const_iterator it = m_id.begin()+2; it < m_id.end(); it++) {
output << hex << setw(2) << setfill('0') << static_cast<unsigned>(*it);
}
break;
case COLUMN_FIELDS:
m_data->dump(output);
break;
case COLUMN_LAST:
default:
break;
}
}
ChainedMessage::ChainedMessage(const string circuit, const string level, const string name,
const bool isWrite, const string comment,
const symbol_t srcAddress, const symbol_t dstAddress,
const vector<symbol_t> id,
vector< vector<symbol_t> > ids, vector<size_t> lengths,
DataField* data, const bool deleteData,
const size_t pollPriority,
Condition* condition)
: Message(circuit, level, name, isWrite, false, comment,
srcAddress, dstAddress, id,
data, deleteData, pollPriority, condition),
m_ids(ids), m_lengths(lengths),
m_maxTimeDiff(m_ids.size()*15) { // 15 seconds per message
size_t cnt = ids.size();
m_lastMasterDatas = reinterpret_cast<MasterSymbolString**>(calloc(cnt, sizeof(MasterSymbolString*)));
m_lastSlaveDatas = reinterpret_cast<SlaveSymbolString**>(calloc(cnt, sizeof(SlaveSymbolString*)));
m_lastMasterUpdateTimes = reinterpret_cast<time_t*>(calloc(cnt, sizeof(time_t)));
m_lastSlaveUpdateTimes = reinterpret_cast<time_t*>(calloc(cnt, sizeof(time_t)));
for (size_t index = 0; index < cnt; index++) {
m_lastMasterDatas[index] = new MasterSymbolString;
m_lastSlaveDatas[index] = new SlaveSymbolString();
}
}
ChainedMessage::~ChainedMessage() {
for (size_t index = 0; index < m_ids.size(); index++) {
delete m_lastMasterDatas[index];
m_lastMasterDatas[index] = NULL;
delete m_lastSlaveDatas[index];
m_lastSlaveDatas[index] = NULL;
}
free(m_lastMasterDatas);
free(m_lastSlaveDatas);
free(m_lastMasterUpdateTimes);
free(m_lastSlaveUpdateTimes);
}
Message* ChainedMessage::derive(const symbol_t dstAddress, const symbol_t srcAddress, const string circuit) {
ChainedMessage* result = new ChainedMessage(circuit.length() == 0 ? m_circuit : circuit, m_level, m_name,
m_isWrite, m_comment,
srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress,
m_id, m_ids, m_lengths, m_data, false,
m_pollPriority, m_condition);
if (m_isScanMessage) {
result->setScanMessage();
}
return result;
}
bool ChainedMessage::checkId(MasterSymbolString& master, size_t* index) {
size_t idLen = getIdLength();
if (master.getDataSize() < idLen) {
return false;
}
size_t chainPrefixLength = Message::getIdLength();
for (size_t pos = 0; pos < chainPrefixLength; pos++) {
if (m_id[2+pos] != master.dataAt(pos)) {
return false; // chain prefix mismatch
}
}
for (size_t checkIndex = 0; checkIndex < m_ids.size(); checkIndex++) { // check suffix for each part
vector<symbol_t> id = m_ids[checkIndex];
bool found = false;
for (size_t pos = chainPrefixLength; pos < idLen; pos++) {
if (id[2+pos] != master.dataAt(pos)) {
found = false;
break;
}
found = true;
}
if (found) {
if (index) {
*index = checkIndex;
}
return true;
}
}
return false;
}
bool ChainedMessage::checkId(Message& other) {
size_t idLen = getIdLength();
if (idLen != other.getIdLength() || other.getCount() == 1) { // only equal for chained messages
return false;
}
if (!other.checkIdPrefix(m_id)) {
return false; // chain prefix mismatch
}
vector< vector<symbol_t> > otherIds = ((ChainedMessage&)other).m_ids;
size_t chainPrefixLength = Message::getIdLength();
for (size_t checkIndex = 0; checkIndex < m_ids.size(); checkIndex++) { // check suffix for each part
vector<symbol_t> id = m_ids[checkIndex];
for (size_t otherIndex = 0; otherIndex < otherIds.size(); otherIndex++) {
vector<symbol_t> otherId = otherIds[otherIndex];
bool found = false;
for (size_t pos = chainPrefixLength; pos < idLen; pos++) {
if (id[2+pos] != otherId[2+pos]) {
found = false;
break;
}
found = true;
}
if (found) {
return true;
}
}
}
return false;
}
result_t ChainedMessage::prepareMasterPart(MasterSymbolString& master, istringstream& input, char separator,
size_t index) {
size_t cnt = getCount();
if (index >= cnt) {
return RESULT_ERR_NOTFOUND;
}
MasterSymbolString allData;
result_t result = m_data->write(input, allData, 0, separator);
if (result != RESULT_OK) {
return result;
}
size_t pos = 0, addData = 0;
if (m_isWrite) {
addData = m_lengths[0];
for (size_t i = 0; i < index; i++) {
pos += addData;
addData = m_lengths[i+1];
}
}
if (pos+addData > allData.getDataSize()) {
return RESULT_ERR_INVALID_POS;
}
vector<symbol_t> id = m_ids[index];
master.push_back((symbol_t)(id.size()-2+addData)); // NN
for (size_t i = 2; i < id.size(); i++) {
master.push_back(id[i]);
}
for (size_t i = 0; i < addData; i++) {
master.push_back(allData.dataAt(pos+i));
}
if (index == 0) {
for (size_t i = 0; i < cnt; i++) {
m_lastMasterUpdateTimes[index] = m_lastSlaveUpdateTimes[index] = 0;
}
}
return result;
}
result_t ChainedMessage::storeLastData(MasterSymbolString& master, SlaveSymbolString& slave) {
// determine index from master ID
size_t index = 0;
if (checkId(master, &index)) {
result_t result = storeLastData(master, index);
if (result >= RESULT_OK) {
result = storeLastData(slave, index);
}
return result;
}
return RESULT_ERR_INVALID_ARG;
}
result_t ChainedMessage::storeLastData(MasterSymbolString& data, size_t index) {
if (index >= m_ids.size()) {
return RESULT_ERR_INVALID_ARG;
}
switch (data.compareTo(*m_lastMasterDatas[index])) {
case 1: // completely different
*m_lastMasterDatas[index] = data;
break;
case 2: // only master address is different
*m_lastMasterDatas[index] = data;
break;
}
time(&m_lastMasterUpdateTimes[index]);
return combineLastParts();
}
result_t ChainedMessage::storeLastData(SlaveSymbolString& data, size_t index) {
if (index >= m_ids.size()) {
return RESULT_ERR_INVALID_ARG;
}
if (data != *m_lastSlaveDatas[index]) {
*m_lastSlaveDatas[index] = data;
}
time(&m_lastSlaveUpdateTimes[index]);
return combineLastParts();
}
result_t ChainedMessage::combineLastParts() {
// check arrival time of all parts
time_t minTime = 0, maxTime = 0;
for (size_t index = 0; index < m_ids.size(); index++) {
if (index == 0) {
minTime = maxTime = m_lastMasterUpdateTimes[index];
} else {
if (m_lastMasterUpdateTimes[index] < minTime) {
minTime = m_lastMasterUpdateTimes[index];
}
if (m_lastMasterUpdateTimes[index] > maxTime) {
maxTime = m_lastMasterUpdateTimes[index];
}
}
if (m_lastSlaveUpdateTimes[index] < minTime) {
minTime = m_lastSlaveUpdateTimes[index];
}
if (m_lastSlaveUpdateTimes[index] > maxTime) {
maxTime = m_lastSlaveUpdateTimes[index];
}
if (minTime == 0 || maxTime == 0 || maxTime-minTime > m_maxTimeDiff) {
return RESULT_CONTINUE;
}
}
// everything was completely retrieved in short time
MasterSymbolString master;
SlaveSymbolString slave;
size_t offset = m_ids[0].size()-2;
SymbolString* add = m_lastMasterDatas[0];
for (size_t pos = 0; pos < 5+offset; pos++) {
master.push_back((*add)[pos]); // copy header
}
slave.push_back(0); // NN, set later
for (size_t index = 0; index < m_ids.size(); index++) {
add = m_lastMasterDatas[index];
size_t end = add->getDataSize();
for (size_t pos = offset; pos < end; pos++) {
master.push_back(add->dataAt(pos));
}
add = m_lastSlaveDatas[index];
end = add->getDataSize();
for (size_t pos = 0; pos < end; pos++) {
slave.push_back(add->dataAt(pos));
}
}
// adjust NN
if (master.size()-5 > 255 || slave.size()-1 > 255) {
return RESULT_ERR_INVALID_POS;
}
master[4] = (symbol_t)(master.size()-5);
slave[0] = (symbol_t)(slave.size()-1);
result_t result = Message::storeLastData(master, 0);
if (result == RESULT_OK) {
result = Message::storeLastData(slave, 0);
}
return result;
}
void ChainedMessage::dumpColumn(ostream& output, column_t column, bool withConditions) {
if (column != COLUMN_ID) {
Message::dumpColumn(output, column, withConditions);
return;
}
bool first = true;
for (size_t index = 0; index < m_ids.size(); index++) {
vector<symbol_t> id = m_ids[index];
for (vector<symbol_t>::const_iterator it = id.begin()+2; it < id.end(); it++) {
if (first) {
first = false;
} else {
output << VALUE_SEPARATOR;
}
output << hex << setw(2) << setfill('0') << static_cast<unsigned>(*it);
}
output << LENGTH_SEPARATOR << dec << setw(0) << static_cast<unsigned>(m_lengths[index]);
}
}
/**
* Get the first available @a Message from the list.
* @param messages the list of @a Message instances to check.
* @param sameIdExtAs the optional @a MasterSymbolString to check for having the same ID.
*/
Message* getFirstAvailable(vector<Message*> &messages, MasterSymbolString* sameIdExtAs) {
for (vector<Message*>::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) {
Message* message = *msgIt;
if (sameIdExtAs && !message->checkId(*sameIdExtAs)) {
continue;
}
if (message->isAvailable()) {
return message;
}
}
return NULL;
}
/**
* Get the first available @a Message from the list.
* @param messages the list of @a Message instances to check.
* @param sameIdExtAs the optional @a Message to check for having the same ID.
*/
Message* getFirstAvailable(vector<Message*> &messages, Message* sameIdExtAs = NULL) {
for (vector<Message*>::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) {
Message* message = *msgIt;
if (sameIdExtAs && !message->checkId(*sameIdExtAs)) {
continue;
}
if (message->isAvailable()) {
return message;
}
}
return NULL;
}
/**
* Split up a list of string values separated by @a VALUE_SEPARATOR.
* @param valueList the input string to split.
* @param values the output value list to append to.
*/
result_t splitValues(string valueList, vector<string>& values) {
istringstream stream(valueList);
string str;
while (getline(stream, str, VALUE_SEPARATOR)) {
if (str.length() > 0 && str[0] == '\'' && str[str.length()-1] == '\'') {
str = str.substr(1, str.length()-2);
}
values.push_back(str);
}
return RESULT_OK;
}
/**
* Split up a list of numeric value ranges separated by @a VALUE_SEPARATOR.
* @param valueList the input string to split.
* @param valueRanges the output list of value ranges to append to (pairs of inclusive from-to values).
*/
result_t splitValues(string valueList, vector<unsigned int>& valueRanges) {
istringstream stream(valueList);
string str;
result_t result;
while (getline(stream, str, VALUE_SEPARATOR)) {
FileReader::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, 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)));
if (!upto) {
valueRanges.push_back(UINT_MAX);
}
} else {
size_t pos = str.find('-');
if (pos != string::npos && 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
}
}
}
return RESULT_OK;
}
result_t Condition::create(const string condName, vector<string>::iterator& it, const vector<string>::iterator end,
string defaultDest, string defaultCircuit, SimpleCondition*& returnValue) {
// name,circuit,messagename,[comment],[fieldname],[ZZ],values (name already skipped by caller)
string circuit = it == end ? "" : *(it++); // circuit[#level]
string level;
size_t pos = circuit.find('#');
if (pos != string::npos) {
level = circuit.substr(pos+1);
circuit.resize(pos);
}
string name = it == end ? "" : *(it++); // messagename
if (it < end) {
it++; // comment
}
string field = it == end ? "" : *(it++); // fieldname
string zz = it == end ? "" : *(it++); // ZZ
symbol_t dstAddress = SYN;
result_t result = RESULT_OK;
if (zz.length() == 0) {
zz = defaultDest;
}
if (zz.length() > 0) {
dstAddress = (symbol_t)parseInt(zz.c_str(), 16, 0, 0xff, result);
if (result != RESULT_OK) {
return result;
}
if (dstAddress != SYN && !isValidAddress(dstAddress, false)) {
return RESULT_ERR_INVALID_ADDR;
}
}
if (name.length() == 0) {
if (!isValidAddress(dstAddress, false) || isMaster(dstAddress)) {
return RESULT_ERR_INVALID_ADDR;
}
} else if (circuit.length() == 0) {
circuit = defaultCircuit;
}
string valueList = it == end ? "" : *(it++);
if (valueList.length() == 0) {
returnValue = new SimpleCondition(condName, condName, circuit, level, name, dstAddress, field);
return RESULT_OK;
}
if (valueList[0] == '\'') {
// strings
vector<string> values;
result = splitValues(valueList, values);
if (result != RESULT_OK) {
return result;
}
returnValue = new SimpleStringCondition(condName, condName, circuit, level, name, dstAddress, field, values);
return RESULT_OK;
}
// numbers
vector<unsigned int> valueRanges;
result = splitValues(valueList, valueRanges);
if (result != RESULT_OK) {
return result;
}
returnValue = new SimpleNumericCondition(condName, condName, circuit, level, name, dstAddress, field, valueRanges);
return RESULT_OK;
}
SimpleCondition* SimpleCondition::derive(string valueList) {
if (valueList.empty()) {
return NULL;
}
string name = m_condName+valueList;
if (valueList[0] == '=') {
valueList.erase(0, 1);
}
result_t result;
if (valueList[0] == '\'') {
// strings
vector<string> values;
result = splitValues(valueList, values);
if (result != RESULT_OK) {
return NULL;
}
return new SimpleStringCondition(name, m_refName, m_circuit, m_level, m_name, m_dstAddress, m_field, values);
}
// numbers
if (!isNumeric()) {
return NULL;
}
vector<unsigned int> valueRanges;
result = splitValues(valueList, valueRanges);
if (result != RESULT_OK) {
return NULL;
}
return new SimpleNumericCondition(name, m_refName, m_circuit, m_level, m_name, m_dstAddress, m_field, valueRanges);
}
void SimpleCondition::dump(ostream& output, bool matched) {
if (matched) {
if (!m_isTrue) {
return;
}
output << "[" << m_refName;
if (m_hasValues) {
output << "=" << m_matchedValue;
}
output << "]";
} else {
output << "[" << m_condName << "]";
}
}
CombinedCondition* SimpleCondition::combineAnd(Condition* other) {
CombinedCondition* ret = new CombinedCondition();
return ret->combineAnd(this)->combineAnd(other);
}
result_t SimpleCondition::resolve(MessageMap* messages, ostringstream& errorMessage,
void (*readMessageFunc)(Message* message)) {
if (m_message == NULL) {
Message* message;
if (m_name.length() == 0) {
message = messages->getScanMessage(m_dstAddress);
errorMessage << "scan condition " << nouppercase << setw(2) << hex << setfill('0')
<< static_cast<unsigned>(m_dstAddress);
} else {
message = messages->find(m_circuit, m_name, m_level, false);
if (!message) {
message = messages->find(m_circuit, m_name, m_level, false, true);
}
errorMessage << "condition " << m_circuit << " " << m_name;
}
if (!message) {
errorMessage << ": message not found";
return RESULT_ERR_NOTFOUND;
}
if (message->getDstAddress() == SYN) {
if (message->isPassive()) {
errorMessage << ": invalid passive message";
return RESULT_ERR_INVALID_ARG;
}
if (m_dstAddress == SYN) {
errorMessage << ": destination address missing";
return RESULT_ERR_INVALID_ADDR;
}
// clone the message with dedicated dstAddress if necessary
uint64_t key = message->getDerivedKey(m_dstAddress);
vector<Message*>* derived = messages->getByKey(key);
if (derived == NULL) {
message = message->derive(m_dstAddress, true);
messages->add(message);
} else {
Message* first = getFirstAvailable(*derived, message);
if (first == NULL) {
errorMessage << ": conditional derived message " << message->getCircuit() << "." << message->getName()
<< " for " << hex << setw(2) << setfill('0') << static_cast<unsigned>(m_dstAddress) << " not found";
return RESULT_ERR_INVALID_ARG;
}
message = first;
}
}
if (m_hasValues) {
if (!message->hasField(m_field.length() > 0 ? m_field.c_str() : NULL, isNumeric())) {
errorMessage << (isNumeric() ? ": numeric field " : ": string field ") << m_field << " not found";
return RESULT_ERR_NOTFOUND;
}
}
m_message = message;
message->setUsedByCondition();
if (m_name.length() > 0 && !message->isScanMessage()) {
messages->addPollMessage(message, true);
}
}
if (m_message->getLastUpdateTime() == 0 && readMessageFunc != NULL) {
(*readMessageFunc)(m_message);
}
return RESULT_OK;
}
bool SimpleCondition::isTrue() {
if (!m_message) {
return false;
}
if (m_message->getLastChangeTime() > m_lastCheckTime) {
bool isTrue = !m_hasValues; // for message seen check
if (!isTrue) {
isTrue = checkValue(m_message, m_field);
}
m_isTrue = isTrue;
m_lastCheckTime = m_message->getLastChangeTime();
}
return m_isTrue;
}
bool SimpleNumericCondition::checkValue(Message* message, string field) {
unsigned int value = 0;
result_t result = message->decodeLastDataNumField(value, field.length() == 0 ? NULL : field.c_str());
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]) {
ostringstream out;
out << static_cast<unsigned>(value);
m_matchedValue = out.str();
return true;
}
}
}
return false;
}
bool SimpleStringCondition::checkValue(Message* message, string field) {
ostringstream output;
result_t result = message->decodeLastData(output, 0, false, field.length() == 0 ? NULL : field.c_str());
if (result == RESULT_OK) {
string value = output.str();
for (size_t i = 0; i < m_values.size(); i++) {
if (m_values[i] == value) {
m_matchedValue = "'"+value+"'";
return true;
}
}
}
return false;
}
void CombinedCondition::dump(ostream& output, bool matched) {
for (vector<Condition*>::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) {
Condition* condition = *it;
condition->dump(output, matched);
}
}
result_t CombinedCondition::resolve(MessageMap* messages, ostringstream& errorMessage,
void (*readMessageFunc)(Message* message)) {
for (vector<Condition*>::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) {
Condition* condition = *it;
ostringstream dummy;
result_t ret = condition->resolve(messages, dummy, readMessageFunc);
if (ret != RESULT_OK) {
errorMessage << dummy.str();
return ret;
}
}
return RESULT_OK;
}
bool CombinedCondition::isTrue() {
for (vector<Condition*>::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) {
if (!(*it)->isTrue()) {
return false;
}
}
return true;
}
result_t Instruction::create(const string contextPath, const string& defaultDest, const string& defaultCircuit,
const string& defaultSuffix, Condition* condition, const string type, vector<string>::iterator& it,
const vector<string>::iterator end, Instruction*& returnValue) {
// type[,argument]* (type already skipped by caller)
bool singleton = false;
if ((singleton=(type == "load")) || type == "include") {
if (it == end || (*it).empty()) {
return RESULT_ERR_INVALID_ARG;
}
size_t pos = contextPath.find_last_of('/');
string path;
if (pos == string::npos) {
path = contextPath;
} else {
path = contextPath.substr(0, pos+1);
}
returnValue = new LoadInstruction(condition, singleton, defaultDest, defaultCircuit, defaultSuffix, path+(*it));
return RESULT_OK;
}
// unknown instruction
return RESULT_ERR_INVALID_ARG;
}
string Instruction::getDestination() {
// ZZ.circuit[.suffix]
string ret;
if (!m_defaultDest.empty()) {
ret = m_defaultDest;
}
if (!m_defaultCircuit.empty() || !m_defaultSuffix.empty()) {
if (!ret.empty()) {
ret += ".";
}
if (m_defaultCircuit.empty()) {
ret += "*";
} else {
ret += m_defaultCircuit;
}
if (!m_defaultSuffix.empty()) {
ret += m_defaultSuffix;
}
}
return ret;
}
result_t LoadInstruction::execute(MessageMap* messages, ostringstream& log, Condition* condition) {
result_t result = messages->readFromFile(m_filename, false, m_defaultDest, m_defaultCircuit, m_defaultSuffix);
if (log.tellp() > 0) {
log << ", ";
}
if (result != RESULT_OK) {
log << "error " << (isSingleton() ? "loading \"" : "including \"") << m_filename << "\" for \""
<< getDestination() << "\": " << getResultCode(result);
return result;
}
log << (isSingleton() ? "loaded \"" : "included \"") << m_filename << "\" for \"" << getDestination() << "\"";
if (isSingleton() && !m_defaultDest.empty()) {
result_t temp;
symbol_t address = (symbol_t)parseInt(m_defaultDest.c_str(), 16, 0, 0xff, temp);
if (temp == RESULT_OK) {
size_t pos = m_filename.find_last_of('/');
string filename;
if (pos == string::npos) {
filename = m_filename;
} else {
filename = m_filename.substr(pos+1);
}
string comment;
if (condition) {
ostringstream out;
condition->dump(out, true);
comment = out.str();
log << " ("+comment+")";
}
messages->addLoadedFile(address, filename, comment);
}
}
return result;
}
result_t MessageMap::add(Message* message, bool storeByName) {
uint64_t key = message->getKey();
bool conditional = message->isConditional();
if (!m_addAll) {
map<uint64_t, vector<Message*> >::iterator keyIt = m_messagesByKey.find(key);
if (keyIt != m_messagesByKey.end()) {
Message* other = getFirstAvailable(keyIt->second, message);
if (other != NULL) {
if (!conditional) {
return RESULT_ERR_DUPLICATE; // duplicate key
}
if (!other->isConditional()) {
return RESULT_ERR_DUPLICATE; // duplicate key
}
}
}
}
bool isPassive = message->isPassive();
if (storeByName) {
bool isWrite = message->isWrite();
string circuit = message->getCircuit();
FileReader::tolower(circuit);
if (circuit == "scan") {
m_additionalScanMessages = true;
}
string name = message->getName();
FileReader::tolower(name);
string nameKey = string(isPassive ? "P" : (isWrite ? "W" : "R")) + circuit + FIELD_SEPARATOR + name;
if (!m_addAll) {
map<string, vector<Message*> >::iterator nameIt = m_messagesByName.find(nameKey);
if (nameIt != m_messagesByName.end()) {
vector<Message*>* messages = &nameIt->second;
if (!message->isConditional() || !messages->front()->isConditional()) {
return RESULT_ERR_DUPLICATE_NAME; // duplicate key
}
}
}
m_messagesByName[nameKey].push_back(message);
nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit
map<string, vector<Message*> >::iterator nameIt = m_messagesByName.find(nameKey);
if (nameIt == m_messagesByName.end()) {
// always store first message without circuit (in order of circuit name)
m_messagesByName[nameKey].push_back(message);
} else {
vector<Message*>* messages = &nameIt->second;
Message* first = messages->front();
if (circuit < first->getCircuit()) {
// always store first message without circuit (in order of circuit name)
m_messagesByName[nameKey].at(0) = message;
} else if (m_addAll || (conditional && first->isConditional())) {
// store further messages only if both are conditional or if storing everything
m_messagesByName[nameKey].push_back(message);
}
}
m_messageCount++;
if (conditional) {
m_conditionalMessageCount++;
}
if (isPassive) {
m_passiveMessageCount++;
}
addPollMessage(message);
}
size_t idLength = message->getIdLength();
if (idLength > m_maxIdLength) {
m_maxIdLength = idLength;
}
m_messagesByKey[key].push_back(message);
return RESULT_OK;
}
result_t MessageMap::addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row,
vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix,
const string& filename, unsigned int lineNo) {
// check for condition in defaults
string type = row[0];
if (type.length() > 0 && type[0] == '[' && type[type.length()-1] == ']') {
// condition
type = type.substr(1, type.length()-2);
if (type.find('[') != string::npos || type.find(']') != string::npos) {
m_lastError = "invalid condition name "+type;
return RESULT_ERR_INVALID_ARG;
}
string key = filename+":"+type;
map<string, Condition*>::iterator it = m_conditions.find(key);
if (it != m_conditions.end()) {
m_lastError = "condition "+type+" already defined";
return RESULT_ERR_DUPLICATE_NAME;
}
SimpleCondition* condition = NULL;
result_t result = Condition::create(type, ++begin, row.end(), defaultDest, defaultCircuit+defaultSuffix, condition);
if (condition == NULL || result != RESULT_OK) {
m_lastError = "invalid condition";
return result;
}
m_conditions[key] = condition;
return RESULT_OK;
}
if (row.size() > 1 && defaultCircuit.length() > 0) {
if (row[1].length() == 0) {
row[1] = defaultCircuit+defaultSuffix; // set default circuit and suffix: "circuit[.suffix]"
} else if (row[1][0] == '#') {
// move access level behind default circuit and suffix: "circuit[.suffix]#level"
row[1] = defaultCircuit+defaultSuffix+row[1];
} else if (defaultSuffix.length() > 0 && row[1].find_last_of('.') == string::npos) {
// circuit suffix not yet present
size_t pos = row[1].find_first_of('#');
if (pos == string::npos) {
row[1] += defaultSuffix; // append default suffix: "circuit.suffix"
} else {
// insert default suffix: "circuit.suffix#level"
row[1] = row[1].substr(0, pos)+defaultSuffix+row[1].substr(pos);
}
}
}
if (row.size() > 5 && defaultDest.length() > 0 && row[5].length() == 0) {
row[5] = defaultDest; // set default destination
}
return FileReader::addDefaultFromFile(defaults, row, begin, defaultDest, defaultCircuit, defaultSuffix, filename,
lineNo);
}
result_t MessageMap::readConditions(string& types, const string& filename, Condition*& condition) {
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<string, Condition*>::iterator it = m_conditions.find(combinedkey);
if (it != m_conditions.end()) {
condition = it->second;
types = types.substr(pos+1);
} else {
bool store = false;
condition = NULL;
while ((pos=types.find(']')) != string::npos) {
// simple condition
string key = filename+":"+types.substr(1, pos-1);
map<string, Condition*>::iterator it = m_conditions.find(key);
Condition* add = NULL;
if (it == m_conditions.end()) {
// check for on-the-fly condition
size_t pos = key.find_first_of("=<>", filename.length()+1);
if (pos != string::npos) {
it = m_conditions.find(key.substr(0, pos));
if (it != m_conditions.end()) {
// derive from another condition
add = it->second->derive(key.substr(pos));
if (add == NULL) {
m_lastError = "derive condition with values "+key.substr(pos)+" failed";
return RESULT_ERR_INVALID_ARG;
}
m_conditions[key] = add; // store derived condition
}
}
if (add == NULL) {
// shared condition not available
m_lastError = "condition "+types.substr(1, pos-1)+" not defined";
return RESULT_ERR_NOTFOUND;
}
} else {
add = it->second;
}
if (condition) {
condition = condition->combineAnd(add);
store = true;
} else {
condition = add;
}
types = types.substr(pos+1);
if (types.length() == 0 || types[0] != '[') {
break;
}
}
if (store) {
m_conditions[combinedkey] = condition; // store combined condition
}
}
}
return RESULT_OK;
}
result_t MessageMap::addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end,
vector< vector<string> >* defaults, const string& defaultDest, const string& defaultCircuit,
const string& defaultSuffix, const string& filename, unsigned int lineNo) {
vector<string>::iterator restart = begin;
string types = *restart;
Condition* condition = NULL;
result_t result = readConditions(types, filename, condition);
if (result != RESULT_OK) {
return result;
}
if (types.length() > 0 && types[0] == '!') {
// instruction
types = types.substr(1);
Instruction* instruction = NULL;
result_t result = Instruction::create(filename, defaultDest, defaultCircuit, defaultSuffix, condition, types,
++begin, end, instruction);
if (instruction == NULL || result != RESULT_OK) {
m_lastError = "invalid instruction";
return result;
}
map<string, vector<Instruction*> >::iterator it = m_instructions.find(filename);
if (it == m_instructions.end()) {
vector<Instruction*> instructions;
instructions.push_back(instruction);
m_instructions[filename] = instructions;
} else {
it->second.push_back(instruction);
}
return RESULT_OK;
}
if (types.length() == 0) {
types.append("r");
} else if (types.find(']') != string::npos) {
return RESULT_ERR_INVALID_ARG;
}
result = RESULT_ERR_EOF;
DataFieldTemplates* templates = getTemplates(filename);
istringstream stream(types);
string type;
vector<Message*> messages;
while (getline(stream, type, VALUE_SEPARATOR)) {
FileReader::trim(type);
*restart = type;
begin = restart;
messages.clear();
result = Message::create(begin, end, defaults, condition, filename, templates, messages);
for (vector<Message*>::iterator it = messages.begin(); it != messages.end(); it++) {
Message* message = *it;
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
}
}
if (result != RESULT_OK) {
return result;
}
}
return result;
}
Message* MessageMap::getScanMessage(const symbol_t dstAddress) {
if (dstAddress == SYN) {
return m_scanMessage;
}
if (dstAddress == BROADCAST) {
return m_broadcastScanMessage;
}
if (!isValidAddress(dstAddress, true) || isMaster(dstAddress)) {
return NULL;
}
uint64_t key = m_scanMessage->getDerivedKey(dstAddress);
vector<Message*>* msgs = getByKey(key);
if (msgs != NULL) {
return msgs->front();
}
Message* message = m_scanMessage->derive(dstAddress, true);
add(message);
return message;
}
result_t MessageMap::resolveConditions(bool verbose) {
m_lastError = "";
result_t overallResult = RESULT_OK;
for (map<string, Condition*>::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) {
Condition* condition = it->second;
result_t result = resolveCondition(condition);
if (result != RESULT_OK) {
overallResult = result;
}
}
return overallResult;
}
result_t MessageMap::resolveCondition(Condition* condition, void (*readMessageFunc)(Message* message)) {
ostringstream error;
result_t result = condition->resolve(this, error, readMessageFunc);
if (result != RESULT_OK) {
string errorMessage = error.str();
if (errorMessage.length() > 0) {
if (m_lastError.length() > 0) {
m_lastError += ", ";
}
m_lastError += errorMessage;
}
}
return result;
}
result_t MessageMap::executeInstructions(ostringstream& log, void (*readMessageFunc)(Message* message)) {
m_lastError = "";
result_t overallResult = RESULT_OK;
vector<string> remove;
for (map<string, vector<Instruction*> >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) {
vector<Instruction*> instructions = it->second;
bool removeSingletons = false;
vector<Instruction*> remain;
for (vector<Instruction*>::iterator lit = instructions.begin(); lit != instructions.end(); lit++) {
Instruction* instruction = *lit;
if (removeSingletons && instruction->isSingleton()) {
delete instruction;
continue;
}
Condition* condition = instruction->getCondition();
bool execute = condition == NULL;
if (!execute) {
result_t result = resolveCondition(condition, instruction->isSingleton()?readMessageFunc:NULL);
if (result != RESULT_OK) {
overallResult = result;
} else if (condition->isTrue()) {
execute = true;
}
}
if (execute) {
if (instruction->isSingleton()) {
removeSingletons = true;
}
result_t result = instruction->execute(this, log, condition);
if (result != RESULT_OK) {
overallResult = result;
}
delete instruction;
} else {
remain.push_back(instruction);
}
}
if (removeSingletons && !remain.empty()) {
instructions = remain;
remain.clear();
for (vector<Instruction*>::iterator lit = instructions.begin(); lit != instructions.end(); lit++) {
Instruction* instruction = *lit;
if (!instruction->isSingleton()) {
remain.push_back(instruction);
continue;
}
delete instruction;
}
}
if (remain.empty()) {
remove.push_back(it->first);
} else {
it->second = remain;
}
}
for (vector<string>::iterator it = remove.begin(); it != remove.end(); it++) {
m_instructions.erase(*it);
}
return overallResult;
}
void MessageMap::addLoadedFile(symbol_t address, string file, string comment) {
if (!file.empty()) {
vector<string>& files = m_loadedFiles[address];
files.push_back(file);
files.push_back(comment);
}
}
vector<string>& MessageMap::getLoadedFiles(symbol_t address) {
return m_loadedFiles[address];
}
vector<Message*>* MessageMap::getByKey(const uint64_t key) {
map<uint64_t, vector<Message*> >::iterator it = m_messagesByKey.find(key);
if (it != m_messagesByKey.end()) {
return &it->second;
}
return NULL;
}
Message* MessageMap::find(const string& circuit, const string& name, const string& levels, const bool isWrite,
const bool isPassive) {
string lcircuit = circuit;
FileReader::tolower(lcircuit);
string lname = name;
FileReader::tolower(lname);
for (int i = 0; i < 2; i++) {
string key;
if (i == 0) {
key = string(isPassive ? "P" : (isWrite ? "W" : "R")) + lcircuit + FIELD_SEPARATOR + lname;
} else if (lcircuit.length() == 0) {
key = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + lname; // second try: without circuit
} else {
continue; // not allowed without circuit
}
map<string, vector<Message*> >::iterator it = m_messagesByName.find(key);
if (it != m_messagesByName.end()) {
Message* message = getFirstAvailable(it->second);
if (message && message->hasLevel(levels)) {
return message;
}
}
}
return NULL;
}
deque<Message*> MessageMap::findAll(const string& circuit, const string& name, const string& levels,
const bool completeMatch, const bool withRead, const bool withWrite, const bool withPassive,
const bool includeEmptyLevel, const bool onlyAvailable,
const time_t since, const time_t until) {
deque<Message*> ret;
string lcircuit = circuit;
FileReader::tolower(lcircuit);
string lname = name;
FileReader::tolower(lname);
bool checkCircuit = lcircuit.length() > 0;
bool checkLevel = levels != "*";
bool checkName = lname.length() > 0;
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 "-"
continue;
}
for (vector<Message*>::iterator msgIt = it->second.begin(); msgIt != it->second.end(); msgIt++) {
Message* message = *msgIt;
if (checkLevel && !message->hasLevel(levels, includeEmptyLevel)) {
continue;
}
if (checkCircuit) {
string check = message->getCircuit();
FileReader::tolower(check);
if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) {
continue;
}
}
if (checkName) {
string check = message->getName();
FileReader::tolower(check);
if (completeMatch ? (check != lname) : (check.find(lname) == check.npos)) {
continue;
}
}
if (message->isPassive()) {
if (!withPassive) {
continue;
}
} else if (message->isWrite()) {
if (!withWrite) {
continue;
}
} else {
if (!withRead) {
continue;
}
}
if (since != 0 || until != 0) {
if (message->getDstAddress() == SYN) {
continue;
}
time_t lastchg = message->getLastChangeTime();
if ((since != 0 && lastchg < since)
|| (until != 0 && lastchg >= until)) {
continue;
}
}
if (!onlyAvailable || message->isAvailable()) {
ret.push_back(*msgIt);
}
}
}
return ret;
}
Message* MessageMap::find(MasterSymbolString& master, bool anyDestination,
const bool withRead, const bool withWrite, const bool withPassive) {
if (master.size() >= 5 && master[4] == 0 && anyDestination && master[2] == 0x07 && master[3] == 0x04) {
return m_scanMessage;
}
uint64_t baseKey = Message::createKey(master, m_maxIdLength, anyDestination);
if (baseKey == INVALID_KEY) {
return NULL;
}
size_t maxIdLength = Message::getKeyLength(baseKey);
for (size_t idLength = maxIdLength; true; idLength--) {
uint64_t key = baseKey;
if (idLength == maxIdLength) {
baseKey &= ~ID_LENGTH_AND_IDS_MASK;
} else {
key |= (uint64_t)idLength << (8 * 7 + 5);
int exp = 3;
for (size_t i = 0; i < idLength; i++) {
key ^= (uint64_t)master.dataAt(i) << (8 * exp--);
if (exp == 0) {
exp = 3;
}
}
}
map<uint64_t , vector<Message*> >::iterator it;
if (withPassive) {
it = m_messagesByKey.find(key);
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master);
if (message) {
return message;
}
}
if ((key & ID_SOURCE_MASK) != 0) {
key &= ~ID_SOURCE_MASK;
it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master);
if (message) {
return message;
}
}
}
} else {
key &= ~ID_SOURCE_MASK;
}
if (withRead) {
it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_READ); // try again with special value for active read
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master);
if (message) {
return message;
}
}
}
if (withWrite) {
it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_WRITE); // try again with special value for active write
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master);
if (message) {
return message;
}
}
}
if (idLength == 0) {
break;
}
}
return NULL;
}
void MessageMap::invalidateCache(Message* message) {
if (message->m_data == DataFieldSet::getIdentFields()) {
return;
}
message->m_lastUpdateTime = 0;
string circuit = message->getCircuit();
string name = message->getName();
deque<Message*> messages = findAll(circuit, name, "*", true, true, true, true);
for (deque<Message*>::iterator it = messages.begin(); it != messages.end(); it++) {
Message* checkMessage = *it;
if (checkMessage != message) {
checkMessage->m_lastUpdateTime = 0;
}
}
}
void MessageMap::addPollMessage(Message* message, bool toFront) {
if (message != NULL && message->getPollPriority() > 0) {
message->m_lastPollTime = toFront ? 0 : m_pollMessages.size();
m_pollMessages.push(message);
}
}
void MessageMap::clear() {
m_loadedFiles.clear();
// clear poll messages
while (!m_pollMessages.empty()) {
m_pollMessages.top();
m_pollMessages.pop();
}
// free message instances by name
for (map<string, vector<Message*> >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) {
vector<Message*> nameMessages = it->second;
if (it->first[0] != '-') { // avoid double free: instances stored multiple times have a key starting with "-"
for (vector<Message*>::iterator nit = nameMessages.begin(); nit != nameMessages.end(); nit++) {
Message* message = *nit;
map<uint64_t, vector<Message*> >::iterator keyIt = m_messagesByKey.find(message->getKey());
if (keyIt != m_messagesByKey.end()) {
vector<Message*>* keyMessages = &keyIt->second;
if (!keyMessages->empty()) {
for (vector<Message*>::iterator kit = keyMessages->begin(); kit != keyMessages->end(); kit++) {
if (*kit == message) {
keyMessages->erase(kit--);
}
}
}
}
delete message;
}
}
nameMessages.clear();
}
// free remaining message instances by key
for (map<uint64_t, vector<Message*> >::iterator it = m_messagesByKey.begin(); it != m_messagesByKey.end(); it++) {
vector<Message*> keyMessages = it->second;
for (vector<Message*>::iterator kit = keyMessages.begin(); kit != keyMessages.end(); kit++) {
Message* message = *kit;
delete message;
}
keyMessages.clear();
}
// free condition instances
for (map<string, Condition*>::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) {
delete it->second;
}
// free instruction instances
for (map<string, vector<Instruction*> >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) {
vector<Instruction*> instructions = it->second;
for (vector<Instruction*>::iterator lit = instructions.begin(); lit != instructions.end(); lit++) {
Instruction* instruction = *lit;
delete instruction;
}
instructions.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_instructions.clear();
m_maxIdLength = 0;
m_additionalScanMessages = false;
}
Message* MessageMap::getNextPoll() {
if (m_pollMessages.empty()) {
return NULL;
}
Message* ret = m_pollMessages.top();
m_pollMessages.pop();
ret->m_pollCount++;
time(&(ret->m_lastPollTime));
m_pollMessages.push(ret); // re-insert at new position
return ret;
}
void MessageMap::dump(ostream& output, bool withConditions) {
bool first = true;
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 "-")
continue;
}
if (m_addAll) {
for (vector<Message*>::iterator mit = it->second.begin(); mit != it->second.end(); mit++) {
Message* message = *mit;
if (!message) {
continue;
}
if (first) {
first = false;
} else {
output << endl;
}
message->dump(output, NULL, withConditions);
}
} else {
Message* message = getFirstAvailable(it->second);
if (!message) {
continue;
}
if (first) {
first = false;
} else {
output << endl;
}
message->dump(output, NULL, withConditions);
}
}
if (!first) {
output << endl;
}
}
} // namespace ebusd