psu: Updates for power supply presence

Update the JSON file to indicate the minimum and maximum number of power
supplies this system can have.

Update the JSON file to have an array of power supply objects, each with
their various own properties. Each power supply will have:
1) An associated D-Bus path for the inventory. This will have the
   Present property.
2) An associated I2C bus number.
3) An associated I2C bus adddress.

Update PowerSupply class to have a present member variable, an
isPresent() accessor function, and two functions for updating the
presence. One function will be the call back function for either
inventory change events or interface added events, which will use
associated match member variables. The second function being one that
attempts to read the inventory Present property on startup. If reading
the Present property on startup fails, the property changed or interface
added events should work to get the value when that property is
available.

Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
Change-Id: Ib34510a6da66ba1b8f1e927093b3d10b09beb410
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 54b20d8..922d555 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -2,6 +2,8 @@
 
 #include "utility.hpp"
 
+using namespace phosphor::logging;
+
 namespace phosphor
 {
 namespace power
@@ -9,6 +11,97 @@
 namespace manager
 {
 
+PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
+                       const std::string& configfile) :
+    bus(bus)
+{
+    // Parse out the JSON properties
+    sys_properties properties;
+    getJSONProperties(configfile, bus, properties, psus);
+
+    using namespace sdeventplus;
+    auto interval = std::chrono::milliseconds(properties.pollInterval);
+    timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
+        e, std::bind(&PSUManager::analyze, this), interval);
+
+    minPSUs = {properties.minPowerSupplies};
+    maxPSUs = {properties.maxPowerSupplies};
+
+    // Subscribe to power state changes
+    powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
+    powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
+        bus,
+        sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
+                                                        POWER_IFACE),
+        [this](auto& msg) { this->powerStateChanged(msg); });
+
+    initialize();
+}
+
+void PSUManager::getJSONProperties(
+    const std::string& path, sdbusplus::bus::bus& bus, sys_properties& p,
+    std::vector<std::unique_ptr<PowerSupply>>& psus)
+{
+    nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
+
+    if (configFileJSON == nullptr)
+    {
+        throw std::runtime_error("Failed to load JSON configuration file");
+    }
+
+    if (!configFileJSON.contains("SystemProperties"))
+    {
+        throw std::runtime_error("Missing required SystemProperties");
+    }
+
+    if (!configFileJSON.contains("PowerSupplies"))
+    {
+        throw std::runtime_error("Missing required PowerSupplies");
+    }
+
+    auto sysProps = configFileJSON["SystemProperties"];
+
+    if (!sysProps.contains("pollInterval"))
+    {
+        throw std::runtime_error("Missing required pollInterval property");
+    }
+
+    p.pollInterval = sysProps["pollInterval"];
+
+    if (sysProps.contains("MinPowerSupplies"))
+    {
+        p.minPowerSupplies = sysProps["MinPowerSupplies"];
+    }
+    else
+    {
+        p.minPowerSupplies = 0;
+    }
+
+    if (sysProps.contains("MaxPowerSupplies"))
+    {
+        p.maxPowerSupplies = sysProps["MaxPowerSupplies"];
+    }
+    else
+    {
+        p.maxPowerSupplies = 0;
+    }
+
+    for (auto psuJSON : configFileJSON["PowerSupplies"])
+    {
+        if (psuJSON.contains("Inventory"))
+        {
+            std::string invpath = psuJSON["Inventory"];
+            auto psu = std::make_unique<PowerSupply>(bus, invpath);
+            psus.emplace_back(std::move(psu));
+        }
+    }
+
+    if (psus.empty())
+    {
+        throw std::runtime_error("No power supplies to monitor");
+    }
+}
+
 void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
 {
     int32_t state = 0;