chassis: Monitor the PowerSystemInputs for power status

In addition to the uPower interface, monitor the PowerSystemInputs
interface to detect a Brownout condition. Reference:
https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/48015

Tested: In simulation, trigger a power supply brownout and clear it,
verify that phosphor-chassis-check-power-status detects the condition:

Mar 29 19:02:21 p10bmc phosphor-chassis-state-manager[1101]: Power
System Inputs status changed to
xyz.openbmc_project.State.Decorator.PowerSystemInputs.Status.Fault
Mar 29 19:02:21 p10bmc phosphor-chassis-state-manager[1101]: Power
System Inputs status is in Fault state

root@p10bmc:~# /usr/bin/phosphor-chassis-check-power-status --chassis 0
<3> Chassis power status is not good:
xyz.openbmc_project.State.Chassis.PowerStatus.BrownOut
Mar 29 19:08:20 p10bmc phosphor-chassis-check-power-status[1133]:
Chassis power status is not good:
xyz.openbmc_project.State.Chassis.PowerStatus.BrownOut
Mar 29 19:08:20 p10bmc phosphor-log-manager[319]: Created PEL 0x50000004
(BMC ID 4) with SRC BDA13402
Mar 29 19:08:20 p10bmc phosphor-ledmanager[447]: SAI: FRU path:
/xyz/openbmc_project/inventory/system

Mar 29 19:02:21 p10bmc phosphor-chassis-state-manager[1101]: Power
System Inputs status changed to
xyz.openbmc_project.State.Decorator.PowerSystemInputs.Status.Good
Mar 29 19:02:21 p10bmc phosphor-chassis-state-manager[1101]:
determineStatusOfPSUPower(): Good

root@p10bmc:~# /usr/bin/phosphor-chassis-check-power-status --chassis 0
<6> Chassis power status good, start power on

Change-Id: I6adbb6474155e50219b6a48dd9b2cf872934f4a6
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/chassis_state_manager.cpp b/chassis_state_manager.cpp
index fc1abac..f3e8389 100644
--- a/chassis_state_manager.cpp
+++ b/chassis_state_manager.cpp
@@ -15,6 +15,7 @@
 #include <sdbusplus/exception.hpp>
 #include <sdeventplus/event.hpp>
 #include <sdeventplus/exception.hpp>
+#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
 
 #include <filesystem>
 #include <fstream>
@@ -30,6 +31,8 @@
 
 // When you see server:: you know we're referencing our base class
 namespace server = sdbusplus::xyz::openbmc_project::State::server;
+namespace decoratorServer =
+    sdbusplus::xyz::openbmc_project::State::Decorator::server;
 
 using namespace phosphor::logging;
 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
@@ -61,6 +64,8 @@
 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
 constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
+constexpr auto POWERSYSINPUTS_INTERFACE =
+    "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
 
 void Chassis::subscribeToSystemdSignals()
@@ -101,6 +106,15 @@
             "/org/freedesktop/UPower", UPOWER_INTERFACE),
         [this](auto& msg) { this->uPowerChangeEvent(msg); });
 
+    // Monitor for any properties changed signals on PowerSystemInputs
+    powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
+        bus,
+        sdbusplus::bus::match::rules::propertiesChangedNamespace(
+            fmt::format(
+                "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
+            POWERSYSINPUTS_INTERFACE),
+        [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
+
     determineStatusOfPower();
 
     std::variant<int> pgood = -1;
@@ -192,6 +206,17 @@
     // Default PowerStatus to good
     server::Chassis::currentPowerStatus(PowerStatus::Good);
 
+    determineStatusOfUPSPower();
+    if (server::Chassis::currentPowerStatus() != PowerStatus::Good)
+    {
+        return;
+    }
+
+    determineStatusOfPSUPower();
+}
+
+void Chassis::determineStatusOfUPSPower()
+{
     // Find all implementations of the UPower interface
     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
                                       MAPPER_INTERFACE, "GetSubTree");
@@ -296,6 +321,73 @@
     return;
 }
 
+void Chassis::determineStatusOfPSUPower()
+{
+    // Find all implementations of the PowerSystemInputs interface
+    auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
+                                      MAPPER_INTERFACE, "GetSubTree");
+
+    mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
+
+    std::map<std::string, std::map<std::string, std::vector<std::string>>>
+        mapperResponse;
+
+    try
+    {
+        auto mapperResponseMsg = bus.call(mapper);
+        mapperResponseMsg.read(mapperResponse);
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
+              "ERROR", e);
+        throw;
+    }
+
+    for (const auto& [path, services] : mapperResponse)
+    {
+        for (const auto& serviceIter : services)
+        {
+            const std::string& service = serviceIter.first;
+
+            try
+            {
+                auto method = bus.new_method_call(service.c_str(), path.c_str(),
+                                                  PROPERTY_INTERFACE, "GetAll");
+                method.append(POWERSYSINPUTS_INTERFACE);
+
+                auto response = bus.call(method);
+                using Property = std::string;
+                using Value = std::variant<std::string>;
+                using PropertyMap = std::map<Property, Value>;
+                PropertyMap properties;
+                response.read(properties);
+
+                auto statusStr = std::get<std::string>(properties["Status"]);
+                auto status =
+                    decoratorServer::PowerSystemInputs::convertStatusFromString(
+                        statusStr);
+
+                if (status == decoratorServer::PowerSystemInputs::Status::Fault)
+                {
+                    info("Power System Inputs status is in Fault state");
+                    server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
+                    return;
+                }
+            }
+            catch (const sdbusplus::exception::exception& e)
+            {
+                error(
+                    "Error reading Power System Inputs property, error: {ERROR}, "
+                    "service: {SERVICE} path: {PATH}",
+                    "ERROR", e, "SERVICE", service, "PATH", path);
+                throw;
+            }
+        }
+    }
+    return;
+}
+
 void Chassis::uPowerChangeEvent(sdbusplus::message::message& msg)
 {
     debug("UPS Property Change Event Triggered");
@@ -303,9 +395,9 @@
     std::map<std::string, std::variant<uint, bool>> msgData;
     msg.read(statusInterface, msgData);
 
-    // If the change is to any of the three properties we are interested in
-    // then call determineStatusOfPower() to see if a power status change
-    // is needed
+    // If the change is to any of the properties we are interested in, then call
+    // determineStatusOfPower(), which looks at all the power-related
+    // interfaces, to see if a power status change is needed
     auto propertyMap = msgData.find("IsPresent");
     if (propertyMap != msgData.end())
     {
@@ -335,6 +427,28 @@
     return;
 }
 
+void Chassis::powerSysInputsChangeEvent(sdbusplus::message::message& msg)
+{
+    debug("Power System Inputs Property Change Event Triggered");
+    std::string statusInterface;
+    std::map<std::string, std::variant<std::string>> msgData;
+    msg.read(statusInterface, msgData);
+
+    // If the change is to any of the properties we are interested in, then call
+    // determineStatusOfPower(), which looks at all the power-related
+    // interfaces, to see if a power status change is needed
+    auto propertyMap = msgData.find("Status");
+    if (propertyMap != msgData.end())
+    {
+        info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
+             "POWER_SYS_INPUT_STATUS",
+             std::get<std::string>(propertyMap->second));
+        determineStatusOfPower();
+        return;
+    }
+    return;
+}
+
 void Chassis::startUnit(const std::string& sysdUnit)
 {
     auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,