fix for mqtt topic including field

This commit is contained in:
john30
2017-06-03 12:17:18 +02:00
parent 6f5f94238d
commit 23d930d653
7 changed files with 87 additions and 46 deletions
+30 -21
View File
@@ -286,13 +286,13 @@ MqttHandler::MqttHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap*
m_topicFields.push_back("name"); m_topicFields.push_back("name");
} else { } else {
for (size_t i = 0; i < m_topicFields.size(); i++) { for (size_t i = 0; i < m_topicFields.size(); i++) {
if (m_topicFields[i] == "fields") { if (m_topicFields[i] == "field") {
m_publishByField = true; m_publishByField = true;
break; break;
} }
} }
} }
m_globalTopic = getTopic(NULL, -1, "global/"); m_globalTopic = getTopic(NULL, "global/");
m_mosquitto = NULL; m_mosquitto = NULL;
if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) { if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) {
logOtherError("mqtt", "unable to initialize"); logOtherError("mqtt", "unable to initialize");
@@ -489,7 +489,7 @@ void MqttHandler::run() {
publishTopic(m_globalTopic+"running", "true"); publishTopic(m_globalTopic+"running", "true");
publishTopic(signalTopic, "false"); publishTopic(signalTopic, "false");
mosquitto_message_callback_set(m_mosquitto, on_message); mosquitto_message_callback_set(m_mosquitto, on_message);
string subTopic = getTopic(NULL, -1, "#"); string subTopic = getTopic(NULL, "#");
mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0); mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0);
while (isRunning()) { while (isRunning()) {
handleTraffic(); handleTraffic();
@@ -557,7 +557,7 @@ void MqttHandler::handleTraffic() {
} }
} }
string MqttHandler::getTopic(const Message* message, ssize_t fieldIndex, const string& suffix) { string MqttHandler::getTopic(const Message* message, const string& suffix, const string& fieldName) {
ostringstream ret; ostringstream ret;
for (size_t i = 0; i < m_topicStrs.size(); i++) { for (size_t i = 0; i < m_topicStrs.size(); i++) {
ret << m_topicStrs[i]; ret << m_topicStrs[i];
@@ -565,8 +565,8 @@ string MqttHandler::getTopic(const Message* message, ssize_t fieldIndex, const s
break; break;
} }
if (i < m_topicFields.size()) { if (i < m_topicFields.size()) {
if (m_topicFields[i] == "fields" && fieldIndex >= 0) { if (m_topicFields[i] == "field") {
ret << message->getFieldName(fieldIndex); // TODO skip ignored fields ret << fieldName;
} else { } else {
message->dumpField(m_topicFields[i], false, &ret); message->dumpField(m_topicFields[i], false, &ret);
} }
@@ -579,24 +579,33 @@ string MqttHandler::getTopic(const Message* message, ssize_t fieldIndex, const s
} }
void MqttHandler::publishMessage(const Message* message, ostringstream* updates) { void MqttHandler::publishMessage(const Message* message, ostringstream* updates) {
result_t result = message->decodeLastData(false, NULL, -1, 0, updates); if (!m_publishByField) {
if (result != RESULT_OK) { result_t result = message->decodeLastData(false, NULL, -1, 0, updates);
logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), if (result != RESULT_OK) {
getResultCode(result)); logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(),
getResultCode(result));
return;
}
publishTopic(getTopic(message), updates->str());
return; return;
} }
if (m_publishByField) { ssize_t index = 0;
ssize_t index = 0; do {
istringstream input(updates->str()); string name = message->getFieldName(index);
string token; if (name.empty()) {
while (getline(input, token, UI_FIELD_SEPARATOR)) { break;
string topic = getTopic(message, index);
publishTopic(topic, token);
index++;
} }
} else { result_t result = message->decodeLastData(false, NULL, index, 0, updates);
publishTopic(getTopic(message), updates->str()); if (result != RESULT_OK) {
} logOtherError("mqtt", "decode %s %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(),
name.c_str(), getResultCode(result));
return;
}
publishTopic(getTopic(message, "", name), updates->str());
updates->str("");
updates->clear();
index++;
} while (index < MAX_LEN);
} }
void MqttHandler::publishTopic(const string& topic, const string& data, bool retain) { void MqttHandler::publishTopic(const string& topic, const string& data, bool retain) {
+2 -2
View File
@@ -97,11 +97,11 @@ class MqttHandler : public DataSink, public DataSource, public Thread {
/** /**
* Build the MQTT topic string for the @a Message. * Build the MQTT topic string for the @a Message.
* @param message the @a Message to build the topic string for. * @param message the @a Message to build the topic string for.
* @param fieldIndex the optional field index for the field column, or -1.
* @param suffix the optional suffix string to append. * @param suffix the optional suffix string to append.
* @param fieldName the name of the singular field, or empty.
* @return the topic string. * @return the topic string.
*/ */
string getTopic(const Message* message, ssize_t fieldIndex = -1, const string& suffix = ""); string getTopic(const Message* message, const string& suffix = "", const string& fieldName = "");
/** /**
* Prepare a @a Message and publish as topic. * Prepare a @a Message and publish as topic.
+20 -8
View File
@@ -494,7 +494,7 @@ result_t SingleDataField::read(const SymbolString& data, size_t offset,
if (offset + (remainder?1:m_length) > data.getDataSize()) { if (offset + (remainder?1:m_length) > data.getDataSize()) {
return RESULT_ERR_INVALID_POS; return RESULT_ERR_INVALID_POS;
} }
if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { if (isIgnored() || (fieldName != NULL && m_name != fieldName) || fieldIndex > 0) {
return RESULT_EMPTY; return RESULT_EMPTY;
} }
result_t res = m_dataType->readRawValue(offset, m_length, data, output); result_t res = m_dataType->readRawValue(offset, m_length, data, output);
@@ -514,7 +514,7 @@ result_t SingleDataField::read(const SymbolString& data, size_t offset,
if (offset + (remainder?1:m_length) > data.getDataSize()) { if (offset + (remainder?1:m_length) > data.getDataSize()) {
return RESULT_ERR_INVALID_POS; return RESULT_ERR_INVALID_POS;
} }
if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { if (isIgnored() || (fieldName != NULL && m_name != fieldName) || fieldIndex > 0) {
return RESULT_EMPTY; return RESULT_EMPTY;
} }
bool shortFormat = outputFormat & OF_SHORT; bool shortFormat = outputFormat & OF_SHORT;
@@ -900,14 +900,26 @@ size_t DataFieldSet::getLength(PartType partType, size_t maxLength) const {
} }
string DataFieldSet::getName(ssize_t fieldIndex) const { string DataFieldSet::getName(ssize_t fieldIndex) const {
if (fieldIndex < 0) { if (fieldIndex < (ssize_t)m_ignoredCount) {
return m_name; return m_name;
} }
if ((size_t)fieldIndex >= m_fields.size()) { if ((size_t)fieldIndex + m_ignoredCount >= m_fields.size()) {
return ""; return "";
} }
if (m_uniqueNames) { if (m_uniqueNames) {
return m_fields[fieldIndex]->getName(-1); if (m_ignoredCount == 0) {
return m_fields[fieldIndex]->getName(-1);
}
ssize_t remain = fieldIndex;
for (const auto field : m_fields) {
if (field->isIgnored()) {
continue;
}
remain--;
if (remain == 0) {
return field->getName(-1);
}
}
} }
ostringstream ostream; ostringstream ostream;
ostream << static_cast<signed>(fieldIndex); ostream << static_cast<signed>(fieldIndex);
@@ -973,7 +985,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
if (result != RESULT_EMPTY) { if (result != RESULT_EMPTY) {
found = true; found = true;
} }
if (findFieldIndex && fieldName == field->getName(-1)) { if (findFieldIndex && !field->isIgnored() && (fieldName == NULL || fieldName == field->getName(-1))) {
if (fieldIndex == 0) { if (fieldIndex == 0) {
if (!found) { if (!found) {
return RESULT_ERR_NOTFOUND; return RESULT_ERR_NOTFOUND;
@@ -994,7 +1006,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
result_t DataFieldSet::read(const SymbolString& data, size_t offset, result_t DataFieldSet::read(const SymbolString& data, size_t offset,
bool leadingSeparator, const char* fieldName, ssize_t fieldIndex, bool leadingSeparator, const char* fieldName, ssize_t fieldIndex,
OutputFormat outputFormat, ssize_t outputIndex, ostream* output) const { OutputFormat outputFormat, ssize_t outputIndex, ostream* output) const {
bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; bool previousFullByteOffset = true, found = false, findFieldIndex = fieldIndex >= 0;
if (outputIndex < 0 && (!m_uniqueNames || ((outputFormat & OF_JSON) && !(outputFormat & OF_NAMES)))) { if (outputIndex < 0 && (!m_uniqueNames || ((outputFormat & OF_JSON) && !(outputFormat & OF_NAMES)))) {
outputIndex = 0; outputIndex = 0;
} }
@@ -1020,7 +1032,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
found = true; found = true;
leadingSeparator = true; leadingSeparator = true;
} }
if (findFieldIndex && fieldName == field->getName(-1)) { if (findFieldIndex && !field->isIgnored() && (fieldName == NULL || fieldName == field->getName(-1))) {
if (fieldIndex == 0) { if (fieldIndex == 0) {
if (!found) { if (!found) {
return RESULT_ERR_NOTFOUND; return RESULT_ERR_NOTFOUND;
+17 -6
View File
@@ -258,10 +258,10 @@ class DataField : public AttributedItem {
/** /**
* Get the specified field name. * Get the specified field name.
* @param fieldIndex the index of the field, or -1 for this. * @param fieldIndex the index of the field (excluding ignored fields), or -1 for this.
* @return the field name, or the index as string if not unique or not available. * @return the field name, or the index as string is not unique or not available.
*/ */
virtual string getName(ssize_t fieldIndex) const { return m_name; } virtual string getName(ssize_t fieldIndex) const = 0;
/** /**
* Dump the field settings to the output. * Dump the field settings to the output.
@@ -282,7 +282,7 @@ class DataField : public AttributedItem {
* @param data the data @a SymbolString for reading binary data. * @param data the data @a SymbolString for reading binary data.
* @param offset the additional offset to add for reading binary data. * @param offset the additional offset to add for reading binary data.
* @param fieldName the name of the field to read, or NULL for the first field. * @param fieldName the name of the field to read, or NULL for the first field.
* @param fieldIndex the optional index of the named field, or -1. * @param fieldIndex the optional index of the field (either named or overall), or -1.
* @param output the variable in which to store the numeric value. * @param output the variable in which to store the numeric value.
* @return @a RESULT_OK on success, * @return @a RESULT_OK on success,
* or @a RESULT_EMPTY if the field was skipped (either if the partType does * or @a RESULT_EMPTY if the field was skipped (either if the partType does
@@ -298,7 +298,7 @@ class DataField : public AttributedItem {
* @param offset the additional offset to add for reading binary data. * @param offset the additional offset to add for reading binary data.
* @param leadingSeparator whether to prepend a separator before the formatted value. * @param leadingSeparator whether to prepend a separator before the formatted value.
* @param fieldName the optional name of a field to limit the output to. * @param fieldName the optional name of a field to limit the output to.
* @param fieldIndex the optional index of the named field to limit the output to, or -1. * @param fieldIndex the optional index of the field to limit the output to (either named or overall), or -1.
* @param outputFormat the @a OutputFormat options to use. * @param outputFormat the @a OutputFormat options to use.
* @param outputIndex the optional index of the field when using an indexed output format, or -1. * @param outputIndex the optional index of the field when using an indexed output format, or -1.
* @param output the @a ostream to append the formatted value to. * @param output the @a ostream to append the formatted value to.
@@ -397,6 +397,11 @@ class SingleDataField : public DataField {
*/ */
bool hasFullByteOffset(bool after) const; bool hasFullByteOffset(bool after) const;
// @copydoc
virtual string getName(ssize_t fieldIndex) const {
return isIgnored() || fieldIndex > 0 ? "" : m_name;
}
/** /**
* Dump the common prefix field settings to the output (name and part type). * Dump the common prefix field settings to the output (name and part type).
* @param output the @a ostream to dump to. * @param output the @a ostream to dump to.
@@ -404,7 +409,7 @@ class SingleDataField : public DataField {
void dumpPrefix(ostream* output) const; void dumpPrefix(ostream* output) const;
/** /**
* Dump the common suffix field settings to the output (optiona unit and comment). * Dump the common suffix field settings to the output (optional unit and comment).
* @param output the @a ostream to dump to. * @param output the @a ostream to dump to.
*/ */
void dumpSuffix(ostream* output) const; void dumpSuffix(ostream* output) const;
@@ -596,9 +601,11 @@ class DataFieldSet : public DataField {
DataFieldSet(const string& name, const vector<const SingleDataField*> fields) DataFieldSet(const string& name, const vector<const SingleDataField*> fields)
: DataField(name), m_fields(fields) { : DataField(name), m_fields(fields) {
bool uniqueNames = true; bool uniqueNames = true;
size_t ignoredCount = 0;
map<string, string> names; map<string, string> names;
for (auto field : fields) { for (auto field : fields) {
if (field->isIgnored()) { if (field->isIgnored()) {
ignoredCount++;
continue; continue;
} }
string name = field->getName(-1); string name = field->getName(-1);
@@ -609,6 +616,7 @@ class DataFieldSet : public DataField {
names[name] = name; names[name] = name;
} }
m_uniqueNames = uniqueNames; m_uniqueNames = uniqueNames;
m_ignoredCount = ignoredCount;
} }
/** /**
@@ -677,6 +685,9 @@ class DataFieldSet : public DataField {
/** whether all fields have a unique name. */ /** whether all fields have a unique name. */
bool m_uniqueNames; bool m_uniqueNames;
/** the number of ignored fields. */
size_t m_ignoredCount;
}; };
+1 -1
View File
@@ -741,7 +741,7 @@ result_t Message::decodeLastData(bool master, bool leadingSeparator, const char*
ssize_t fieldIndex, OutputFormat outputFormat, ostream* output) const { ssize_t fieldIndex, OutputFormat outputFormat, ostream* output) const {
result_t result; result_t result;
if (master) { if (master) {
result = m_data->read(m_lastMasterData, m_id.size() - 2, leadingSeparator, fieldName, fieldIndex, result = m_data->read(m_lastMasterData, getIdLength(), leadingSeparator, fieldName, fieldIndex,
outputFormat, -1, output); outputFormat, -1, output);
} else { } else {
result = m_data->read(m_lastSlaveData, 0, leadingSeparator, fieldName, fieldIndex, result = m_data->read(m_lastSlaveData, 0, leadingSeparator, fieldName, fieldIndex,
+5 -5
View File
@@ -269,7 +269,7 @@ class Message : public AttributedItem {
/** /**
* Get the specified field name. * Get the specified field name.
* @param fieldIndex the index of the field. * @param fieldIndex the index of the field (excluding ignored fields).
* @return the field name, or the index as string if not unique or not available. * @return the field name, or the index as string if not unique or not available.
*/ */
virtual string getFieldName(ssize_t fieldIndex) const { return m_data->getName(fieldIndex); } virtual string getFieldName(ssize_t fieldIndex) const { return m_data->getName(fieldIndex); }
@@ -456,10 +456,10 @@ class Message : public AttributedItem {
/** /**
* Decode the value from the last stored master or slave data. * Decode the value from the last stored master or slave data.
* @param master true for deocding the master data, false for slave. * @param master true for decoding the master data, false for slave.
* @param leadingSeparator whether to prepend a separator before the formatted value. * @param leadingSeparator whether to prepend a separator before the formatted value.
* @param fieldName the optional name of a field to limit the output to. * @param fieldName the optional name of a field to limit the output to.
* @param fieldIndex the optional index of the named field to limit the output to, or -1. * @param fieldIndex the optional index of the field to limit the output to (either named or overall), or -1.
* @param outputFormat the @a OutputFormat options to use. * @param outputFormat the @a OutputFormat options to use.
* @param output the @a ostream to append the formatted value to. * @param output the @a ostream to append the formatted value to.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
@@ -471,7 +471,7 @@ class Message : public AttributedItem {
* Decode the value from the last stored master and slave data. * Decode the value from the last stored master and slave data.
* @param leadingSeparator whether to prepend a separator before the formatted value. * @param leadingSeparator whether to prepend a separator before the formatted value.
* @param fieldName the optional name of a field to limit the output to. * @param fieldName the optional name of a field to limit the output to.
* @param fieldIndex the optional index of the named field to limit the output to, or -1. * @param fieldIndex the optional index of the field to limit the output to (either named or overall), or -1.
* @param outputFormat the @a OutputFormat options to use. * @param outputFormat the @a OutputFormat options to use.
* @param output the @a ostream to append the formatted value to. * @param output the @a ostream to append the formatted value to.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
@@ -482,7 +482,7 @@ class Message : public AttributedItem {
/** /**
* Decode a particular numeric field value from the last stored data. * Decode a particular numeric field value from the last stored data.
* @param fieldName the name of the field to decode, or NULL for the first field. * @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. * @param fieldIndex the optional index of the field (either named or overall), or -1.
* @param output the variable in which to store the value. * @param output the variable in which to store the value.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
+12 -3
View File
@@ -502,6 +502,10 @@ int main() {
{"x,,temp;HEX:2", "18.004;13 14", "10fe07000401121314", "00", ""}, // reference to template and base type {"x,,temp;HEX:2", "18.004;13 14", "10fe07000401121314", "00", ""}, // reference to template and base type
{"x,,temp;HEX:2", "temp=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type {"x,,temp;HEX:2", "temp=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type
{"x,,temp:degrees;HEX:2", "degrees=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type {"x,,temp:degrees;HEX:2", "degrees=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type
{"x,,uch;UCH;IGN;UCH", "41", "1008ffff00", "0426272829", "wi2"},
{"x,,uch,,,,x,,uch,,,,x,,ign,,,,x,,uch,,,,", "41", "1008ffff00", "0426272829", "wi2"},
{"x,,uch,,,,y,,uch,,,,x,,ign,,,,x,,uch,,,,", "41", "1008ffff00", "0426272829", "wIi1"},
{"x,,uch,,,,y,,uch,,,,z,,ign,,,,x,,uch,,,,", "41", "1008ffff00", "0426272829", "wIi1"},
}; };
DataFieldTemplates* templates = new DataFieldTemplates(); DataFieldTemplates* templates = new DataFieldTemplates();
unsigned int lineNo = 0; unsigned int lineNo = 0;
@@ -535,6 +539,11 @@ int main() {
bool failedReadMatch = flags.find('R') != string::npos; bool failedReadMatch = flags.find('R') != string::npos;
bool failedWrite = flags.find('w') != string::npos; bool failedWrite = flags.find('w') != string::npos;
bool failedWriteMatch = flags.find('W') != string::npos; bool failedWriteMatch = flags.find('W') != string::npos;
const char* findName = flags.find('I') == string::npos ? NULL : "x";
ssize_t findIndex = -1;
if (flags.find('i') != string::npos) {
findIndex = parseSignedInt(flags.substr(flags.find('i')+1).c_str(), 10, 0, 9, &result);
}
OutputFormat verbosity = 0; OutputFormat verbosity = 0;
if (flags.find("v") != string::npos) { if (flags.find("v") != string::npos) {
verbosity |= OF_NAMES; verbosity |= OF_NAMES;
@@ -572,7 +581,7 @@ int main() {
dummystr.str("#"); dummystr.str("#");
result = reader.readLineFromStream(__FILE__, false, &dummystr, &lineNo, &row, &errorDescription, NULL, NULL); result = reader.readLineFromStream(__FILE__, false, &dummystr, &lineNo, &row, &errorDescription, NULL, NULL);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": reader header error: " << getResultCode(result) << ", " << errorDescription cout << "\"" << check[0] << "\": read header error: " << getResultCode(result) << ", " << errorDescription
<< endl; << endl;
error = true; error = true;
continue; continue;
@@ -616,9 +625,9 @@ int main() {
cout << " parse \"" << sstr.getStr().substr(0, 2) << "\" error: " << getResultCode(result) << endl; cout << " parse \"" << sstr.getStr().substr(0, 2) << "\" error: " << getResultCode(result) << endl;
error = true; error = true;
} }
result = fields->read(mstr, 0, false, NULL, -1, verbosity|(numeric?OF_NUMERIC:0), -1, &output); result = fields->read(mstr, 0, false, findName, findIndex, verbosity|(numeric?OF_NUMERIC:0), -1, &output);
if (result >= RESULT_OK) { if (result >= RESULT_OK) {
result = fields->read(sstr, 0, !output.str().empty(), NULL, -1, verbosity|(numeric?OF_NUMERIC:0), -1, &output); result = fields->read(sstr, 0, !output.str().empty(), findName, findIndex, verbosity|(numeric?OF_NUMERIC:0), -1, &output);
} }
if (failedRead) { if (failedRead) {
if (result >= RESULT_OK) { if (result >= RESULT_OK) {