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/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index 63697f4..cedc184 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -19,6 +19,39 @@
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
 
+PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
+                         std::uint8_t i2cbus, std::uint16_t i2caddr) :
+    bus(bus),
+    inventoryPath(invpath)
+{
+    if (inventoryPath.empty())
+    {
+        throw std::invalid_argument{"Invalid empty inventoryPath"};
+    }
+
+    // Setup the functions to call when the D-Bus inventory path for the
+    // Present property changes.
+    presentMatch = std::make_unique<sdbusplus::bus::match_t>(
+        bus,
+        sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
+                                                        INVENTORY_IFACE),
+        [this](auto& msg) { this->inventoryChanged(msg); });
+
+    presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
+        bus,
+        sdbusplus::bus::match::rules::interfacesAdded() +
+            sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
+        [this](auto& msg) { this->inventoryAdded(msg); });
+
+    std::ostringstream ss;
+    ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
+    std::string addrStr = ss.str();
+    pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
+
+    // Get the current state of the Present property.
+    updatePresence();
+}
+
 void PowerSupply::updatePresence()
 {
     try
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 24d1df8..fc41635 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -52,32 +52,7 @@
      * @param[in] i2caddr - The 16-bit I2C address of the power supply
      */
     PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
-                std::uint8_t i2cbus, const std::string& i2caddr) :
-        bus(bus),
-        inventoryPath(invpath),
-        pmbusIntf(phosphor::pmbus::createPMBus(i2cbus, i2caddr))
-    {
-        if (inventoryPath.empty())
-        {
-            throw std::invalid_argument{"Invalid empty inventoryPath"};
-        }
-
-        // Setup the functions to call when the D-Bus inventory path for the
-        // Present property changes.
-        presentMatch = std::make_unique<sdbusplus::bus::match_t>(
-            bus,
-            sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
-                                                            INVENTORY_IFACE),
-            [this](auto& msg) { this->inventoryChanged(msg); });
-        presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
-            bus,
-            sdbusplus::bus::match::rules::interfacesAdded() +
-                sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
-            std::bind(&PowerSupply::inventoryAdded, this,
-                      std::placeholders::_1));
-        // Get the current state of the Present property.
-        updatePresence();
-    }
+                std::uint8_t i2cbus, const std::uint16_t i2caddr);
 
     phosphor::pmbus::PMBusBase& getPMBus()
     {
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)
     {
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 0f93279..b950869 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -37,7 +37,7 @@
     PSUManager& operator=(PSUManager&&) = delete;
 
     /**
-     * Constructor
+     * Constructor to read configuration from JSON file.
      *
      * @param[in] bus - D-Bus bus object
      * @param[in] e - event object
@@ -47,12 +47,34 @@
                const std::string& configfile);
 
     /**
+     * Constructor to read configuration from D-Bus.
+     *
+     * @param[in] bus - D-Bus bus object
+     * @param[in] e - event object
+     */
+    PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e);
+
+    /**
      * @brief Initialize the PowerSupply objects from the JSON config file.
      * @param[in] path - Path to the JSON config file
      */
     void getJSONProperties(const std::string& path);
 
     /**
+     * Get PSU properties from D-Bus, use that to build a power supply
+     * object.
+     *
+     * @param[in] properties - A map of property names and values
+     *
+     */
+    void getPSUProperties(util::DbusPropertyMap& properties);
+
+    /**
+     * Get PSU configuration from D-Bus
+     */
+    void getPSUConfiguration();
+
+    /**
      * @brief Initialize the system properties from the Supported Configuration
      *        D-Bus object provided by Entity Manager.
      */
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index 07b4b9e..b028d56 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -50,7 +50,7 @@
     // Try where inventory path is empty, constructor should fail.
     try
     {
-        auto psu = std::make_unique<PowerSupply>(bus, "", 3, "0068");
+        auto psu = std::make_unique<PowerSupply>(bus, "", 3, 0x68);
         ADD_FAILURE() << "Should not have reached this line.";
     }
     catch (const std::invalid_argument& e)
@@ -68,7 +68,7 @@
         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
             .Times(1);
         auto psu =
-            std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, "0068");
+            std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68);
 
         EXPECT_EQ(psu->isPresent(), false);
         EXPECT_EQ(psu->isFaulted(), false);
@@ -87,7 +87,7 @@
     auto bus = sdbusplus::bus::new_default();
 
     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
-    PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
+    PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
     psu.analyze();
     // By default, nothing should change.
     EXPECT_EQ(psu.isPresent(), false);
@@ -101,7 +101,7 @@
     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
         .Times(1)
         .WillOnce(Return(true)); // present
-    PowerSupply psu2{bus, PSUInventoryPath, 5, "006a"};
+    PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a};
     EXPECT_EQ(psu2.isPresent(), true);
 
     // STATUS_WORD 0x0000 is powered on, no faults.
@@ -193,7 +193,7 @@
         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
             .Times(1)
             .WillOnce(Return(false));
-        PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
+        PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         // If it is not present, I should not be trying to write to it.
         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
@@ -209,7 +209,7 @@
         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
             .Times(1)
             .WillOnce(Return(true)); // present
-        PowerSupply psu{bus, PSUInventoryPath, 5, "006a"};
+        PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a};
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         // TODO: ???should I check the filename?
         EXPECT_CALL(mockPMBus,
@@ -228,7 +228,7 @@
     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
         .Times(1)
         .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 13, "0068"};
+    PowerSupply psu{bus, PSUInventoryPath, 13, 0x68};
     EXPECT_EQ(psu.isPresent(), true);
     EXPECT_EQ(psu.isFaulted(), false);
     EXPECT_EQ(psu.hasInputFault(), false);
@@ -265,7 +265,7 @@
         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
             .Times(1)
             .WillOnce(Return(false)); // missing
-        PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+        PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         // If it is not present, I should not be trying to read a string
         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
@@ -281,7 +281,7 @@
         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
             .Times(1)
             .WillOnce(Return(true)); // present
-        PowerSupply psu{bus, PSUInventoryPath, 13, "0069"};
+        PowerSupply psu{bus, PSUInventoryPath, 13, 0x69};
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
         psu.updateInventory();
@@ -306,12 +306,12 @@
 {
     auto bus = sdbusplus::bus::new_default();
     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
-    PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
     EXPECT_EQ(psu.isPresent(), false);
 
     EXPECT_CALL(mockedUtil, getPresence(_, _))
         .WillOnce(Return(true)); // present
-    PowerSupply psu2{bus, PSUInventoryPath, 10, "006b"};
+    PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b};
     EXPECT_EQ(psu2.isPresent(), true);
 }
 
@@ -320,7 +320,7 @@
     auto bus = sdbusplus::bus::new_default();
     EXPECT_CALL(mockedUtil, getPresence(_, _))
         .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 11, "006f"};
+    PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f};
     EXPECT_EQ(psu.isFaulted(), false);
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_CALL(mockPMBus, read(_, _))
@@ -336,7 +336,7 @@
     auto bus = sdbusplus::bus::new_default();
     EXPECT_CALL(mockedUtil, getPresence(_, _))
         .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
@@ -358,7 +358,7 @@
     auto bus = sdbusplus::bus::new_default();
     EXPECT_CALL(mockedUtil, getPresence(_, _))
         .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
@@ -380,7 +380,7 @@
     auto bus = sdbusplus::bus::new_default();
     EXPECT_CALL(mockedUtil, getPresence(_, _))
         .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_EQ(psu.hasVINUVFault(), false);
     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));