Support optional conditions on creating fans

This adds the functional infrastructure to optionally attach a condition
function to a fan definition. When a condition is defined on a fan, it
must be true for a fan's associated functional properties to be created.
When the given condition fails, that fan's functional properties will
not be created by fan monitor. A fan without a defined condition will
have all of its associated functional properties created.

Example of generated condition (generation commit to follow):

make_condition(condition::propertiesMatch(
    std::vector<PropertyState>{
        PropertyState{
            PropertyIdentity{
                "/xyz/openbmc_project/inventory/system/chassis",
                "xyz.openbmc_project.Inventory.Decorator.CoolingType",
                "WaterCooled"
            },
            static_cast<bool>(false)
        }
    }
)),

Tested:
    Fan functional properties are not created when a condition fails
    Fan functional properties are created when condition passes
    Fan functional properties are created when no condition exists

Change-Id: I9ced2e520d2f97e6655c9417970b3e976d78fef4
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/monitor/Makefile.am b/monitor/Makefile.am
index 8eda1e0..bea0933 100644
--- a/monitor/Makefile.am
+++ b/monitor/Makefile.am
@@ -8,7 +8,8 @@
 	argument.cpp \
 	fan.cpp \
 	main.cpp \
-	tach_sensor.cpp
+	tach_sensor.cpp \
+	conditions.cpp
 
 nodist_phosphor_fan_monitor_SOURCES = \
 	fan_monitor_defs.cpp
diff --git a/monitor/conditions.cpp b/monitor/conditions.cpp
new file mode 100644
index 0000000..c046f0c
--- /dev/null
+++ b/monitor/conditions.cpp
@@ -0,0 +1,35 @@
+#include <algorithm>
+#include "conditions.hpp"
+#include "sdbusplus.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace monitor
+{
+namespace condition
+{
+
+Condition propertiesMatch(std::vector<PropertyState>&& propStates)
+{
+    return [pStates = std::move(propStates)](sdbusplus::bus::bus& bus)
+    {
+        return std::all_of(
+            pStates.begin(),
+            pStates.end(),
+            [&bus](const auto& p)
+        {
+            return util::SDBusPlus::getPropertyVariant<PropertyValue>(
+                bus,
+                std::get<propObj>(p.first),
+                std::get<propIface>(p.first),
+                std::get<propName>(p.first)) == p.second;
+        });
+    };
+}
+
+} // namespace condition
+} // namespace monitor
+} // namespace fan
+} // namespace phosphor
diff --git a/monitor/conditions.hpp b/monitor/conditions.hpp
new file mode 100644
index 0000000..76aa0de
--- /dev/null
+++ b/monitor/conditions.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "types.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace monitor
+{
+
+/**
+ * @brief Create a condition function object
+ *
+ * @param[in] condition - The condition being created
+ *
+ * @return - The created condition function object
+ */
+template <typename T>
+auto make_condition(T&& condition)
+{
+    return Condition(std::forward<T>(condition));
+}
+
+namespace condition
+{
+
+/**
+ * @brief A condition that checks all properties match the given values
+ * @details Checks each property entry against its given value where all
+ * property values must match their given value for the condition to pass
+ *
+ * @param[in] propStates - List of property identifiers and their value
+ *
+ * @return Condition lambda function
+ *     A Condition function that checks all properties match
+ */
+Condition propertiesMatch(std::vector<PropertyState>&& propStates);
+
+} // namespace condition
+} // namespace monitor
+} // namespace fan
+} // namespace phosphor
diff --git a/monitor/main.cpp b/monitor/main.cpp
index 060f27a..84dd7d1 100644
--- a/monitor/main.cpp
+++ b/monitor/main.cpp
@@ -72,6 +72,16 @@
 
     for (const auto& fanDef : fanDefinitions)
     {
+        // Check if a condition exists on the fan
+        auto condition = std::get<conditionField>(fanDef);
+        if (condition)
+        {
+            // Condition exists, skip adding fan if it fails
+            if (!(*condition)(bus))
+            {
+                continue;
+            }
+        }
         fans.emplace_back(std::make_unique<Fan>(
                 mode, bus, eventPtr, trust, fanDef));
     }
diff --git a/monitor/types.hpp b/monitor/types.hpp
index 3d8306d..9a7b1fa 100644
--- a/monitor/types.hpp
+++ b/monitor/types.hpp
@@ -4,6 +4,7 @@
 #include <string>
 #include <tuple>
 #include <vector>
+#include <experimental/optional>
 #include "trust_group.hpp"
 
 namespace phosphor
@@ -13,6 +14,23 @@
 namespace monitor
 {
 
+constexpr auto propObj = 0;
+constexpr auto propIface = 1;
+constexpr auto propName = 2;
+using PropertyIdentity = std::tuple<std::string,
+                                    std::string,
+                                    std::string>;
+
+using PropertyValue = sdbusplus::message::variant<bool,
+                                                  int64_t,
+                                                  std::string>;
+constexpr auto propIdentity = 0;
+constexpr auto propValue = 1;
+using PropertyState = std::pair<PropertyIdentity,
+                                PropertyValue>;
+
+using Condition = std::function<bool(sdbusplus::bus::bus&)>;
+
 using CreateGroupFunction =
         std::function<std::unique_ptr<trust::Group>()>;
 
@@ -34,13 +52,15 @@
 constexpr auto fanDeviationField = 3;
 constexpr auto numSensorFailsForNonfuncField = 4;
 constexpr auto sensorListField = 5;
+constexpr auto conditionField = 6;
 
 using FanDefinition = std::tuple<std::string,
                                  size_t,
                                  size_t,
                                  size_t,
                                  size_t,
-                                 std::vector<SensorDefinition>>;
+                                 std::vector<SensorDefinition>,
+                                 std::experimental::optional<Condition>>;
 
 }
 }