psu-ng: Add method to get PSU conf from D-Bus.

Add a getPSUProperties method that will get the power supply unit
configuration information from the D-Bus properties populated by
entity-manager. The IBM common form factor power supplies will have
these properties under the interface called
xyz.openbmc_project.Configuration.IBMCFFPSConnector. See
I45b724238cffe6fb7f1f8f9947fa1c2c9e9e8015 for changes to add the
necessary updates to entity-manager.

The D-Bus property for I2CAddress is a uint64_t type, while the JSON
configuration file had a string. Move the PowerSupply constructor to the
cpp file, updated to take in uint16_t for the i2caddr.

Update PSUManager::getJSONProperties() to convert Address from string to
uint16_t.

Add in a PSUManager constructor to use the getPSUProperties() function.

Add code to handle interfacesAdded from entity-manager prior to
getPSUProperties() or getSystemProperties().

Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
Change-Id: I553e8982cf008828823cea2c0f017ff23c9070ab
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index fa1294a..5bb0807 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -11,6 +11,12 @@
 namespace phosphor::power::manager
 {
 
+constexpr auto IBMCFFPSInterface =
+    "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
+constexpr auto i2cBusProp = "I2CBus";
+constexpr auto i2cAddressProp = "I2CAddress";
+constexpr auto psuNameProp = "Name";
+
 constexpr auto supportedConfIntf =
     "xyz.openbmc_project.Configuration.SupportedConfiguration";
 constexpr auto maxCountProp = "MaxCount";
@@ -19,8 +25,8 @@
                        const std::string& configfile) :
     bus(bus)
 {
-    // Parse out the JSON properties
     sysProperties = {0};
+    // Parse out the JSON properties
     getJSONProperties(configfile);
     // Subscribe to InterfacesAdded before doing a property read, otherwise
     // the interface could be created after the read attempt but before the
@@ -50,6 +56,39 @@
     initialize();
 }
 
+PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
+    bus(bus)
+{
+    sysProperties = {0};
+    // Subscribe to InterfacesAdded before doing a property read, otherwise
+    // the interface could be created after the read attempt but before the
+    // match is created.
+    entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
+        bus,
+        sdbusplus::bus::match::rules::interfacesAdded() +
+            sdbusplus::bus::match::rules::sender(
+                "xyz.openbmc_project.EntityManager"),
+        std::bind(&PSUManager::entityManagerIfaceAdded, this,
+                  std::placeholders::_1));
+    getPSUConfiguration();
+    getSystemProperties();
+
+    using namespace sdeventplus;
+    auto interval = std::chrono::milliseconds(1000);
+    timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
+        e, std::bind(&PSUManager::analyze, this), interval);
+
+    // 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)
 {
     nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
@@ -71,7 +110,7 @@
         {
             std::string invpath = psuJSON["Inventory"];
             std::uint8_t i2cbus = psuJSON["Bus"];
-            std::string i2caddr = psuJSON["Address"];
+            std::uint16_t i2caddr = static_cast<uint16_t>(psuJSON["Address"]);
             auto psu =
                 std::make_unique<PowerSupply>(bus, invpath, i2cbus, i2caddr);
             psus.emplace_back(std::move(psu));
@@ -88,6 +127,93 @@
     }
 }
 
+void PSUManager::getPSUConfiguration()
+{
+    using namespace phosphor::power::util;
+    auto depth = 0;
+    auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
+
+    psus.clear();
+
+    // I should get a map of objects back.
+    // Each object will have a path, a service, and an interface.
+    // The interface should match the one passed into this function.
+    for (const auto& [path, services] : objects)
+    {
+        auto service = services.begin()->first;
+
+        if (path.empty() || service.empty())
+        {
+            continue;
+        }
+
+        // For each object in the array of objects, I want to get properties
+        // from the service, path, and interface.
+        auto properties =
+            getAllProperties(bus, path, IBMCFFPSInterface, service);
+
+        getPSUProperties(properties);
+    }
+
+    if (psus.empty())
+    {
+        // Interface or properties not found. Let the Interfaces Added callback
+        // process the information once the interfaces are added to D-Bus.
+        log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
+    }
+}
+
+void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
+{
+    // From passed in properties, I want to get: I2CBus, I2CAddress,
+    // and Name. Create a power supply object, using Name to build the inventory
+    // path.
+    const auto basePSUInvPath =
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
+    uint64_t* i2cbus = nullptr;
+    uint64_t* i2caddr = nullptr;
+    std::string* psuname = nullptr;
+
+    for (const auto& property : properties)
+    {
+        try
+        {
+            if (property.first == i2cBusProp)
+            {
+                i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
+            }
+            else if (property.first == i2cAddressProp)
+            {
+                i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
+            }
+            else if (property.first == psuNameProp)
+            {
+                psuname = std::get_if<std::string>(&properties[psuNameProp]);
+            }
+        }
+        catch (std::exception& e)
+        {
+        }
+    }
+
+    if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
+    {
+        std::string invpath = basePSUInvPath;
+        invpath.push_back(psuname->back());
+
+        log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
+
+        auto psu =
+            std::make_unique<PowerSupply>(bus, invpath, *i2cbus, *i2caddr);
+        psus.emplace_back(std::move(psu));
+    }
+
+    if (psus.empty())
+    {
+        log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
+    }
+}
+
 void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
 {
     try
@@ -148,12 +274,19 @@
         msg.read(objPath, interfaces);
 
         auto itIntf = interfaces.find(supportedConfIntf);
-        if (itIntf == interfaces.cend())
+        if (itIntf != interfaces.cend())
         {
-            return;
+            populateSysProperties(itIntf->second);
         }
 
-        populateSysProperties(itIntf->second);
+        itIntf = interfaces.find(IBMCFFPSInterface);
+        if (itIntf != interfaces.cend())
+        {
+            log<level::INFO>(
+                fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
+                    .c_str());
+            getPSUProperties(itIntf->second);
+        }
     }
     catch (std::exception& e)
     {