add curly braces support to mqtt integration, documentation

This commit is contained in:
John
2022-01-30 12:48:03 +01:00
parent d868c42d57
commit 95eef1e741
2 changed files with 61 additions and 21 deletions
+25 -2
View File
@@ -6,6 +6,7 @@
# where only the value is allowed to contain references to other variables.
# A reference to a variable is escaped by a leading "%" character in front of the variable name consisting of only
# alphabetic characters or the underscore. A double percent character "%%" is replaced by a single "%" in the value.
# Curly braces may be used to separate a variable explicitly from the rest, e.g. as "%{version}" instead of "%version".
# Every variable without an underscore in the name is automatically made available as well with an uppercase name and a
# normalized value composed of only alphanumeric characters or underscore (i.e. suitable for being part of a topic).
# When using "?=" instead of only the equal sign in a variable definition, the variable value is set to empty when one
@@ -93,12 +94,10 @@ filter-direction = r|u
#filter-field =
# the field type mapped from variables named "type_map-%direction-%type" or "type_map-%type" as fallback with the
# field type and the optional direction in the suffix. The variable including the direction is evaluated first and
# if it is missing or the result is empty, the variable excluding the direction is used instead.
# This is also an implicit field type filter as missing or empty mappings are not published at all.
type_map-number = number
type_map-list = string
type_map-string = string
@@ -107,6 +106,30 @@ type_map-time = string
type_map-datetime = string
# field type switch designator, see below.
#type_switch-by = %name%field,%unit
# field type switch variables names to use in addition to %type_switch in case of multiple keys (separated by comma).
#type_switch-names = type_topic,type_class,type_state
# field type switch for each field type and optionally direction (between dash before the field type) available as
# %type_switch (as well as the variable names defined in "type_switch-names" if any).
# The value needs to be a constant list of string pairs with one pair per line. Each pair is separated by "=" and the
# left part is the value set to the type_switch variable (as well as the variable names defined in %type_switch-names)
# when the wildcard string in the right part matched the "type_switch-by" value. The list is traversed from top to
# bottom stopping at the first match. If direction specific definitions exist, these are traversed first. If no line
# matches at all, the variable(s) are set to the empty string.
#type_switch-w-number =
# field type part suffix to use instead of the field type itself, see below.
#type_part-by = %type_topic
# field type part mappings for each field type (or the "type_part-by" variable value) in the suffix (available as
# %type_part).
#type_part-number = ,
# the message definition config topic, payload, and retain setting.
definition-topic = %prefixn/config/%CIRCUIT/%NAME/%FIELD
definition-payload = {
+36 -19
View File
@@ -338,7 +338,7 @@ static const char* knownFieldNames[] = {
/** the number of known field names. */
static const size_t knownFieldCount = sizeof(knownFieldNames) / sizeof(char*);
std::pair<string, int> makeField(const string name, bool isField) {
std::pair<string, int> makeField(const string& name, bool isField) {
if (!isField) {
return {name, -1};
}
@@ -350,38 +350,55 @@ std::pair<string, int> makeField(const string name, bool isField) {
return {name, knownFieldCount};
}
void addPart(ostringstream& stack, int inField, vector<std::pair<string, int>>& parts) {
string str = stack.str();
if (inField == 1 && str == "_") {
inField = 0; // single "%_" pattern to reduce to "_"
} else if (inField == 2) {
str = "%{" + str;
inField = 0;
}
if (inField == 0 && str.empty()) {
return;
}
stack.str("");
if (inField == 0 && !parts.empty() && parts[parts.size()-1].second < 0) {
// append constant to previous constant
parts[parts.size()-1].first += str;
return;
}
parts.push_back(makeField(str, inField>0));
}
bool MqttReplacer::parse(const string& templateStr, bool onlyKnown, bool noKnownDuplicates, bool emptyIfMissing) {
m_parts.clear();
size_t end = templateStr.length();
bool inField = false;
int inField = 0; // 1 after '%', 2 after '%{'
ostringstream stack;
for (size_t pos = 0; pos < end+1; pos++) {
char ch = pos<end ? templateStr[pos] : '\0';
for (auto ch : templateStr) {
bool empty = stack.tellp()<=0;
if (ch=='%' || ch=='\0') {
if (inField && empty) { // %% for plain %
inField = false;
if (ch=='%') {
if (inField==1 && empty) { // %% for plain %
inField = 0;
stack << ch;
} else {
if (!empty) {
m_parts.push_back(makeField(stack.str(), inField));
stack.str("");
}
inField = true;
addPart(stack, inField, m_parts);
inField = 1;
}
} else if (ch=='{' && inField==1 && empty) {
inField = 2;
} else if (ch=='}' && inField==2) {
addPart(stack, 1, m_parts);
inField = 0;
} else {
if (inField && !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')) {
if (inField>0 && !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')) {
// invalid field character
if (stack.tellp()>0) {
m_parts.push_back(makeField(stack.str(), true));
stack.str("");
}
inField = false;
addPart(stack, inField, m_parts);
inField = 0;
}
stack << ch;
}
}
addPart(stack, inField, m_parts);
if (onlyKnown || noKnownDuplicates) {
int foundMask = 0;
int knownCount = knownFieldCount;