Create Filters class to store filter functions
Enhanced the generated code to now store the filter functions within an
array contained in a struct similar to other configured parameters.
This allows the generated property watches to use an index within that
array to access a pointer to a Filters class containing a given set of
filter functions. The set of filter functions would then be applied
against all the properties being watched.
Tested:
Generated code provides structure needed to access filter functions
Filter functions filter property values that fail the filter(s)
Change-Id: I8f1f882704de521f2ab393530ad7ef096314975d
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/src/filters.hpp b/src/filters.hpp
new file mode 100644
index 0000000..c7a5bcf
--- /dev/null
+++ b/src/filters.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "data_types.hpp"
+
+#include <algorithm>
+#include <functional>
+
+namespace phosphor
+{
+namespace dbus
+{
+namespace monitoring
+{
+
+/** @class Filters
+ * @brief Filter interface
+ *
+ * Filters of any type can be applied to property value changes.
+ */
+class Filters
+{
+ public:
+ Filters() = default;
+ Filters(const Filters&) = delete;
+ Filters(Filters&&) = default;
+ Filters& operator=(const Filters&) = delete;
+ Filters& operator=(Filters&&) = default;
+ virtual ~Filters() = default;
+
+ /** @brief Apply filter operations to a property value. */
+ virtual bool operator()(const any_ns::any& value) = 0;
+};
+
+/** @class OperandFilters
+ * @brief Filter property values utilzing operand based functions.
+ *
+ * When configured, an operand filter is applied to a property value each
+ * time it changes to determine if that property value should be filtered
+ * from being stored or used within a given callback function.
+ */
+template <typename T>
+class OperandFilters : public Filters
+{
+ public:
+ OperandFilters() = delete;
+ OperandFilters(const OperandFilters&) = delete;
+ OperandFilters(OperandFilters&&) = default;
+ OperandFilters& operator=(const OperandFilters&) = delete;
+ OperandFilters& operator=(OperandFilters&&) = default;
+ virtual ~OperandFilters() = default;
+ explicit OperandFilters(const std::vector<std::function<bool(T)>>& _ops) :
+ Filters(), ops(std::move(_ops))
+ {
+ }
+
+ bool operator()(const any_ns::any& value) override
+ {
+ for (const auto& filterOps : ops)
+ {
+ try
+ {
+ // Apply filter operand to property value
+ if (!filterOps(any_ns::any_cast<T>(value)))
+ {
+ // Property value should be filtered
+ return true;
+ }
+ }
+ catch (const any_ns::bad_any_cast& bac)
+ {
+ // Unable to cast property value to filter value type
+ // to check filter, continue to next filter op
+ continue;
+ }
+ }
+
+ // Property value should not be filtered
+ return false;
+ }
+
+ private:
+ /** @brief List of operand based filter functions. */
+ const std::vector<std::function<bool(T)>> ops;
+};
+
+} // namespace monitoring
+} // namespace dbus
+} // namespace phosphor
diff --git a/src/pdmgen.py b/src/pdmgen.py
index dc1c9b7..ef1b790 100755
--- a/src/pdmgen.py
+++ b/src/pdmgen.py
@@ -636,16 +636,86 @@
config=self.configfile)
super(HasPathIndex, self).setup(objs)
+class GroupOfFilters(ConfigEntry):
+ '''Handle config file directives that require an index for filters.'''
+
+ def __init__(self, *a, **kw):
+ # Pop filters data for adding to the available filters array
+ self.type = kw.pop('type')
+ self.datatype = kw.pop('datatype', None)
+ self.filters = kw.pop('filters', None)
+
+ super(GroupOfFilters, self).__init__(**kw)
+
+ def factory(self, objs):
+ '''Modify filters to add the property value type and
+ make them of operation argument type.'''
+ if self.filters:
+ # 'type' used within OpArgument to generate filter
+ # argument values so add to each filter
+ for f in self.filters:
+ f['type'] = self.type
+ self.filters = [OpArgument(**x) for x in self.filters]
+
+ super(GroupOfFilters, self).factory(objs)
+
class PropertyWatch(HasPropertyIndex):
'''Handle the property watch config file directive.'''
def __init__(self, *a, **kw):
- self.filters = [OpArgument(**x) for x in kw.pop('filters', {})]
+ # Pop optional filters for the properties being watched
+ self.filters = kw.pop('filters', None)
self.callback = kw.pop('callback', None)
super(PropertyWatch, self).__init__(**kw)
+ def factory(self, objs):
+ '''Create any filters for this property watch.'''
+
+ if self.filters:
+ # Get the datatype(i.e. "int64_t") of the properties in this watch
+ # (Made available after all `super` classes init'd)
+ datatype = objs['propertygroup'][get_index(
+ objs,
+ 'propertygroup',
+ self.properties,
+ config=self.configfile)].datatype
+ # Get the type(i.e. "int64") of the properties in this watch
+ # (Made available after all `super` classes init'd)
+ type = objs['propertygroup'][get_index(
+ objs,
+ 'propertygroup',
+ self.properties,
+ config=self.configfile)].type
+ # Construct the data needed to make the filters for
+ # this watch available.
+ # *Note: 'class', 'subclass', 'name' are required for
+ # storing the filter data(i.e. 'type', 'datatype', & 'filters')
+ args = {
+ 'type': type,
+ 'datatype': datatype,
+ 'filters': self.filters,
+ 'class': 'filtersgroup',
+ 'filtersgroup': 'filters',
+ 'name': self.name,
+ }
+ # Init GroupOfFilters class with this watch's filters' arguments
+ group = GroupOfFilters(configfile=self.configfile, **args)
+ # Store this group of filters so it can be indexed later
+ add_unique(group, objs, config=self.configfile)
+ group.factory(objs)
+
+ super(PropertyWatch, self).factory(objs)
+
def setup(self, objs):
- '''Resolve optional callback.'''
+ '''Resolve optional filters and callback.'''
+
+ if self.filters:
+ # Watch has filters, provide array index to access them
+ self.filters = get_index(
+ objs,
+ 'filtersgroup',
+ self.name,
+ config=self.configfile)
if self.callback:
self.callback = get_index(
@@ -673,7 +743,6 @@
config=self.configfile)
super(PathWatch, self).setup(objs)
-
class Callback(HasPropertyIndex):
'''Interface and common logic for callbacks.'''
@@ -1261,6 +1330,7 @@
self.callbackgroups = kw.pop('callbackgroup', [])
self.pathcallbackgroups = kw.pop('pathcallbackgroup', [])
self.conditions = kw.pop('condition', [])
+ self.filters = kw.pop('filtersgroup', [])
super(Everything, self).__init__(**kw)
@@ -1293,6 +1363,7 @@
callbackgroups=self.callbackgroups,
pathcallbackgroups=self.pathcallbackgroups,
conditions=self.conditions,
+ filters=self.filters,
indent=Indent()))
if __name__ == '__main__':
diff --git a/src/propertywatch.hpp b/src/propertywatch.hpp
index 3168abe..ecb8e9c 100644
--- a/src/propertywatch.hpp
+++ b/src/propertywatch.hpp
@@ -8,6 +8,7 @@
#pragma once
#include "data_types.hpp"
+#include "filters.hpp"
#include "watch.hpp"
namespace phosphor
@@ -116,14 +117,14 @@
PropertyWatchOfType& operator=(const PropertyWatchOfType&) = delete;
PropertyWatchOfType& operator=(PropertyWatchOfType&&) = default;
~PropertyWatchOfType() = default;
- PropertyWatchOfType(const std::vector<std::function<bool(T)>>& filterOps,
- const PropertyIndex& watchIndex, Callback& callback) :
+ PropertyWatchOfType(const PropertyIndex& watchIndex, Callback& callback,
+ Filters* filterOps = nullptr) :
PropertyWatch<DBusInterfaceType>(watchIndex, &callback),
filterOps(filterOps)
{
}
- PropertyWatchOfType(const std::vector<std::function<bool(T)>>& filterOps,
- const PropertyIndex& watchIndex) :
+ PropertyWatchOfType(const PropertyIndex& watchIndex,
+ Filters* filterOps = nullptr) :
PropertyWatch<DBusInterfaceType>(watchIndex, nullptr),
filterOps(filterOps)
{
@@ -176,8 +177,8 @@
const InterfacesAdded<T>& interfaces);
private:
- /** @brief List of filter operations to perform on property changes. */
- const std::vector<std::function<bool(T)>> filterOps;
+ /** @brief Optional filter operations to perform on property changes. */
+ Filters* const filterOps;
};
} // namespace monitoring
diff --git a/src/propertywatchimpl.hpp b/src/propertywatchimpl.hpp
index c639210..a1b8d68 100644
--- a/src/propertywatchimpl.hpp
+++ b/src/propertywatchimpl.hpp
@@ -158,15 +158,15 @@
auto isFiltered = false;
const auto& storage = std::get<storageIndex>(item->second);
auto value = sdbusplus::message::variant_ns::get<T>(p.second);
- for (auto& filterOp : filterOps)
+ if (filterOps)
{
- if (!filterOp(value))
+ any_ns::any anyValue = value;
+ if ((*filterOps)(anyValue))
{
// Property value filtered, clear it from storage so
// callback functions do not use it
isFiltered = true;
std::get<valueIndex>(storage.get()).clear();
- break;
}
}
if (!isFiltered)
diff --git a/src/templates/generated.mako.hpp b/src/templates/generated.mako.hpp
index a6c33bf..7b1bf69 100644
--- a/src/templates/generated.mako.hpp
+++ b/src/templates/generated.mako.hpp
@@ -120,6 +120,30 @@
}
};
+struct ConfigPropertyFilters
+{
+ using PropertyFilters = std::array<std::unique_ptr<Filters>, ${len(filters)}>;
+
+ static auto& get()
+ {
+ static const PropertyFilters propertyFilters =
+ {
+% for f in filters:
+ std::make_unique<OperandFilters<${f.datatype}>>(
+ std::vector<std::function<bool(${f.datatype})>>{
+ % for o in f.filters:
+ [](const auto& val){
+ return val ${o.op} ${o.argument(loader, indent=indent +1)};
+ },
+ % endfor
+ }
+ ),
+% endfor
+ };
+ return propertyFilters;
+ }
+};
+
struct ConfigPropertyIndicies
{
using PropertyIndicies = std::array<PropertyIndex, ${len(instancegroups)}>;
@@ -230,22 +254,22 @@
{
% for w in watches:
std::make_unique<PropertyWatchOfType<${w.datatype}, SDBusPlus>>(
- % if w.filters:
- std::vector<std::function<bool(${w.datatype})>>{
- % for f in w.filters:
- [](const auto& val){
- return val ${f.op} ${f.argument(loader, indent=indent +1)};
- },
- % endfor
- },
- % else:
- std::vector<std::function<bool(${w.datatype})>>{},
- % endif
% if w.callback is None:
+ % if w.filters is None:
ConfigPropertyIndicies::get()[${w.instances}]),
+ % else:
+ ConfigPropertyIndicies::get()[${w.instances}],
+ ConfigPropertyFilters::get()[${w.filters}].get()),
+ % endif
% else:
+ % if w.filters is None:
ConfigPropertyIndicies::get()[${w.instances}],
*ConfigPropertyCallbacks::get()[${w.callback}]),
+ % else:
+ ConfigPropertyIndicies::get()[${w.instances}],
+ *ConfigPropertyCallbacks::get()[${w.callback}],
+ ConfigPropertyFilters::get()[${w.filters}].get()),
+ % endif
% endif
% endfor
};
diff --git a/src/test/propertywatchtest.cpp b/src/test/propertywatchtest.cpp
index 5a0c121..1c56658 100644
--- a/src/test/propertywatchtest.cpp
+++ b/src/test/propertywatchtest.cpp
@@ -123,7 +123,7 @@
MockDBusInterface::instance(dbus);
const std::vector<std::string> expectedMapperInterfaces;
- PropertyWatchOfType<T, MockDBusInterface> watch({}, watchIndex);
+ PropertyWatchOfType<T, MockDBusInterface> watch(watchIndex);
auto ndx = static_cast<size_t>(0);
for (const auto& o : convert(watchIndex))