diff --git a/analytics/includes/OperatorConfiguratorTemplate.h b/analytics/includes/OperatorConfiguratorTemplate.h index 477b0031a5ecb8c36d0e60eca1908855993350fe..25281d8d01d7e3067e577f3cd39a7437bd8edb0e 100644 --- a/analytics/includes/OperatorConfiguratorTemplate.h +++ b/analytics/includes/OperatorConfiguratorTemplate.h @@ -618,33 +618,45 @@ protected: * @return true if successful, false otherwise */ bool constructSensorTopics(UnitTemplate& u, Operator& op) { - std::string name; // Performing name construction for(auto& s: u.getOutputs()) { - s->setName(s->getMqtt()); - SensorMetadata* sm = s->getMetadata(); - if(sm) { - sm->publicName = s->getMqtt(); - sm->pattern = s->getMqtt(); - sm->isVirtual = false; - if(sm->interval==0) - sm->interval = (unsigned long long)op.getInterval() * 1000000; - } + adjustSensor(s, op, u); } for(auto& subUnit: u.getSubUnits()) for(auto& s : subUnit->getOutputs()) { - s->setName(s->getMqtt()); - SensorMetadata* sm = s->getMetadata(); - if(sm) { - sm->publicName = s->getMqtt(); - sm->pattern = s->getMqtt(); - sm->isVirtual = false; - if(sm->interval==0) - sm->interval = (unsigned long long)op.getInterval() * 1000000; - } + adjustSensor(s, op, u); } return true; } + + /** + * @brief Adjusts a single sensor + * + */ + void adjustSensor(std::shared_ptr s, Operator& op, UnitTemplate& u) { + s->setName(s->getMqtt()); + SensorMetadata* sm = s->getMetadata(); + if(sm) { + if(sm->isOperation) { + s->clearMetadata(); + if(u.getInputs().size() != 1) { + LOG(error) << _operatorName << " " << op.getName() << ": Ambiguous operation field for sensor " << s->getName(); + return; + } + // Replacing the metadata to publish the sensor as an operation of its corresponding input + SensorMetadata smNew; + smNew.publicName = u.getInputs()[0]->getMqtt(); + smNew.addOperation(s->getMqtt()); + s->setMetadata(smNew); + } else { + sm->publicName = s->getMqtt(); + sm->pattern = s->getMqtt(); + sm->isVirtual = false; + if (sm->interval == 0) + sm->interval = (unsigned long long) op.getInterval() * 1000000; + } + } + } /** * @brief Returns true if the input string describes an input block diff --git a/common/include/metadatastore.h b/common/include/metadatastore.h index e5ea80f1ceba95a2278d6863ee38bc28de69e3fb..37d5bc23290928b07813f7105d8dde11dba0dd3a 100644 --- a/common/include/metadatastore.h +++ b/common/include/metadatastore.h @@ -47,6 +47,7 @@ class SensorMetadata { public: SensorMetadata() : + isOperation(false), isVirtual(false), integrable(true), monotonic(false), @@ -59,6 +60,7 @@ public: operations("") {} SensorMetadata(const SensorMetadata& other) { + this->isOperation = other.isOperation; this->isVirtual = other.isVirtual; this->integrable = other.integrable; this->monotonic = other.monotonic; @@ -72,6 +74,7 @@ public: } SensorMetadata& operator=(const SensorMetadata& other) { + this->isOperation = other.isOperation; this->isVirtual = other.isVirtual; this->integrable = other.integrable; this->monotonic = other.monotonic; @@ -85,6 +88,19 @@ public: return *this; } + + bool addOperation(const string& opName) { + if (publicName.length()>0 && opName.length()>publicName.length() && !opName.compare(0, publicName.length(), publicName)) { + if(operations.length()==0) + operations = opName.substr(publicName.length()); + else + operations += "," + opName.substr(publicName.length()); + return true; + } + else + return false; + } + /** * @brief Parses a JSON string and stores the content in this object. * @@ -112,6 +128,8 @@ public: this->monotonic = to_bool(val.second.data()); } else if (boost::iequals(val.first, "isVirtual")) { this->isVirtual = to_bool(val.second.data()); + } else if (boost::iequals(val.first, "isOperation")) { + this->isOperation = to_bool(val.second.data()); } else if (boost::iequals(val.first, "integrable")) { this->integrable = to_bool(val.second.data()); } else if (boost::iequals(val.first, "unit")) { @@ -157,6 +175,7 @@ public: } // Public class members + bool isOperation; bool isVirtual; bool integrable; bool monotonic; @@ -200,6 +219,7 @@ protected: std::ostringstream scaleStream; scaleStream << this->scale; config.clear(); + config.push_back(boost::property_tree::ptree::value_type("isOperation", boost::property_tree::ptree(this->isOperation ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("isVirtual", boost::property_tree::ptree(this->isVirtual ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("monotonic", boost::property_tree::ptree(this->monotonic ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("integrable", boost::property_tree::ptree(this->integrable ? "true" : "false"))); diff --git a/dcdbpusher/README.md b/dcdbpusher/README.md index cb9d9cd0b059011d59b4fc85f64c2e0c6369bada..e92fcb1610337840bd1cc02a055f588fa9242a96 100644 --- a/dcdbpusher/README.md +++ b/dcdbpusher/README.md @@ -748,6 +748,7 @@ Available fields that can be published as metadata are the following: | interval | Sampling interval in milliseconds of the sensor. | | operations | Comma-separated lists of operations available for the sensor, whose values can be retrieved by appending their names to the sensor name. | +An additional _isOperation_ field is available for the output sensors of operators in the Wintermute framework. If these output sensors are generated starting from a single input, this field allows to publish them as _operations_ of the latter, and will be listed in the associated database entry. For this to apply, however, the MQTT topic of the output sensor must be identical to that of the input, plus a suffix that describes the operation. Enabling this option invalidates all other metadata fields. ## Writing own plugins First make sure you read the [plugins](#plugins) section.