From 95eef1e741af7e10984e28ea51b454a1d7a2aeba Mon Sep 17 00:00:00 2001 From: John Date: Sun, 30 Jan 2022 12:48:03 +0100 Subject: [PATCH] add curly braces support to mqtt integration, documentation --- contrib/etc/ebusd/mqtt-integration.cfg | 27 ++++++++++++- src/ebusd/mqtthandler.cpp | 55 +++++++++++++++++--------- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/contrib/etc/ebusd/mqtt-integration.cfg b/contrib/etc/ebusd/mqtt-integration.cfg index b18dc122..d4afd567 100644 --- a/contrib/etc/ebusd/mqtt-integration.cfg +++ b/contrib/etc/ebusd/mqtt-integration.cfg @@ -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 = { diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp index 6243349d..1a55c582 100755 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -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 makeField(const string name, bool isField) { +std::pair makeField(const string& name, bool isField) { if (!isField) { return {name, -1}; } @@ -350,38 +350,55 @@ std::pair makeField(const string name, bool isField) { return {name, knownFieldCount}; } +void addPart(ostringstream& stack, int inField, vector>& 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= '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;