monitor:JSON: Parse fan definitions

Parse and create the list of fan definitions for monitoring from a JSON
configuration file.

Tested:
    Fan definition list created same as from YAML
    Any required parameters missing cause a runtime exception

Change-Id: Iafa194f01a48ed3445c5da6187db62cb4d2672d7
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/monitor/json_parser.cpp b/monitor/json_parser.cpp
index 61c8e80..34d1995 100644
--- a/monitor/json_parser.cpp
+++ b/monitor/json_parser.cpp
@@ -15,6 +15,7 @@
  */
 #include "json_parser.hpp"
 
+#include "conditions.hpp"
 #include "json_config.hpp"
 #include "nonzero_speed_trust.hpp"
 #include "types.hpp"
@@ -25,6 +26,7 @@
 #include <algorithm>
 #include <map>
 #include <memory>
+#include <optional>
 #include <vector>
 
 namespace phosphor::fan::monitor
@@ -113,4 +115,92 @@
     return grpFuncs;
 }
 
+const std::vector<SensorDefinition> getSensorDefs(const json& sensors)
+{
+    std::vector<SensorDefinition> sensorDefs;
+
+    for (const auto& sensor : sensors)
+    {
+        if (!sensor.contains("name") || !sensor.contains("has_target"))
+        {
+            // Log error on missing required parameters
+            log<level::ERR>(
+                "Missing required fan sensor definition parameters",
+                entry("REQUIRED_PARAMETERS=%s", "{name, has_target}"));
+            throw std::runtime_error(
+                "Missing required fan sensor definition parameters");
+        }
+        // Target interface is optional and defaults to
+        // 'xyz.openbmc_project.Control.FanSpeed'
+        std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed";
+        if (sensor.contains("target_interface"))
+        {
+            targetIntf = sensor["target_interface"].get<std::string>();
+        }
+        // Factor is optional and defaults to 1
+        auto factor = 1.0;
+        if (sensor.contains("factor"))
+        {
+            factor = sensor["factor"].get<double>();
+        }
+        // Offset is optional and defaults to 0
+        auto offset = 0;
+        if (sensor.contains("offset"))
+        {
+            offset = sensor["offset"].get<int64_t>();
+        }
+
+        sensorDefs.emplace_back(std::tuple(sensor["name"].get<std::string>(),
+                                           sensor["has_target"].get<bool>(),
+                                           targetIntf, factor, offset));
+    }
+
+    return sensorDefs;
+}
+
+const std::vector<FanDefinition> getFanDefs(const json& obj)
+{
+    std::vector<FanDefinition> fanDefs;
+
+    for (const auto& fan : obj["fans"])
+    {
+        if (!fan.contains("inventory") ||
+            !fan.contains("allowed_out_of_range_time") ||
+            !fan.contains("deviation") ||
+            !fan.contains("num_sensors_nonfunc_for_fan_nonfunc") ||
+            !fan.contains("sensors"))
+        {
+            // Log error on missing required parameters
+            log<level::ERR>(
+                "Missing required fan monitor definition parameters",
+                entry("REQUIRED_PARAMETERS=%s",
+                      "{inventory, allowed_out_of_range_time, deviation, "
+                      "num_sensors_nonfunc_for_fan_nonfunc, sensors}"));
+            throw std::runtime_error(
+                "Missing required fan monitor definition parameters");
+        }
+        // Construct the sensor definitions for this fan
+        auto sensorDefs = getSensorDefs(fan["sensors"]);
+
+        // Functional delay is optional and defaults to 0
+        size_t funcDelay = 0;
+        if (fan.contains("functional_delay"))
+        {
+            funcDelay = fan["functional_delay"].get<size_t>();
+        }
+
+        // TODO Handle optional conditions
+        auto cond = std::experimental::optional<Condition>();
+
+        fanDefs.emplace_back(
+            std::tuple(fan["inventory"].get<std::string>(), funcDelay,
+                       fan["allowed_out_of_range_time"].get<size_t>(),
+                       fan["deviation"].get<size_t>(),
+                       fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>(),
+                       sensorDefs, cond));
+    }
+
+    return fanDefs;
+}
+
 } // namespace phosphor::fan::monitor