Use Idle Power Saver parameters from DBus

Allows users to enable and update the IPS parameters instead of
using hardcoded values.

Change-Id: I9010c4b4d3dbdf130a4a778f71c87279681a9f1a
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/occ_status.cpp b/occ_status.cpp
index ab6b9d1..b1893fd 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -172,11 +172,6 @@
         fs::path(DEV_PATH) /
         fs::path(sysfsName + "." + std::to_string(instance + 1)) / "occ_state";
 
-    log<level::DEBUG>(
-        fmt::format("Status::readOccState: reading OCC{} state from {}",
-                    instance, filename.c_str())
-            .c_str());
-
     std::ifstream file(filename, std::ios::in);
     const int open_errno = errno;
     if (file)
@@ -272,6 +267,124 @@
     return pmode;
 }
 
+// Get the requested power mode
+bool Status::getIPSParms(uint8_t& enterUtil, uint16_t& enterTime,
+                         uint8_t& exitUtil, uint16_t& exitTime)
+{
+    using namespace open_power::occ::powermode;
+    // Defaults:
+    bool ipsEnabled = false; // Disabled
+    enterUtil = 8;           // Enter Utilization (8%)
+    enterTime = 240;         // Enter Delay Time (240s)
+    exitUtil = 12;           // Exit Utilization (12%)
+    exitTime = 10;           // Exit Delay Time (10s)
+
+    std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
+        ipsProperties{};
+
+    // Get all IPS properties from DBus
+    try
+    {
+        auto& bus = utils::getBus();
+        auto service = utils::getService(PIPS_PATH, PIPS_INTERFACE);
+        auto method =
+            bus.new_method_call(service.c_str(), PIPS_PATH,
+                                "org.freedesktop.DBus.Properties", "GetAll");
+        method.append(PIPS_INTERFACE);
+        auto reply = bus.call(method);
+        reply.read(ipsProperties);
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        log<level::ERR>(
+            fmt::format(
+                "Unable to read Idle Power Saver parameters so it will be disabled: {}",
+                e.what())
+                .c_str());
+        return ipsEnabled;
+    }
+
+    auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
+    if (ipsEntry != ipsProperties.end())
+    {
+        ipsEnabled = std::get<bool>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            fmt::format("Status::getIPSParms could not find property: {}",
+                        IPS_ENABLED_PROP)
+                .c_str());
+    }
+
+    ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
+    if (ipsEntry != ipsProperties.end())
+    {
+        enterUtil = std::get<uint8_t>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            fmt::format("Status::getIPSParms could not find property: {}",
+                        IPS_ENTER_UTIL)
+                .c_str());
+    }
+
+    ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
+    if (ipsEntry != ipsProperties.end())
+    {
+        std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
+        enterTime =
+            std::chrono::duration_cast<std::chrono::seconds>(ms).count();
+    }
+    else
+    {
+        log<level::ERR>(
+            fmt::format("Status::getIPSParms could not find property: {}",
+                        IPS_ENTER_TIME)
+                .c_str());
+    }
+
+    ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
+    if (ipsEntry != ipsProperties.end())
+    {
+        exitUtil = std::get<uint8_t>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            fmt::format("Status::getIPSParms could not find property: {}",
+                        IPS_EXIT_UTIL)
+                .c_str());
+    }
+
+    ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
+    if (ipsEntry != ipsProperties.end())
+    {
+        std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
+        exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
+    }
+    else
+    {
+        log<level::ERR>(
+            fmt::format("Status::getIPSParms could not find property: {}",
+                        IPS_EXIT_TIME)
+                .c_str());
+    }
+
+    if (enterUtil > exitUtil)
+    {
+        log<level::ERR>(
+            fmt::format(
+                "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
+                enterUtil, exitUtil)
+                .c_str());
+        enterUtil = exitUtil;
+    }
+
+    return ipsEnabled;
+}
+
 // Special processing that needs to happen once the OCCs change to ACTIVE state
 void Status::occsWentActive()
 {
@@ -325,6 +438,7 @@
     if (VALID_POWER_MODE_SETTING(newMode))
     {
         std::vector<std::uint8_t> cmd, rsp;
+        cmd.reserve(9);
         cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
         cmd.push_back(0x00); // Data Length (2 bytes)
         cmd.push_back(0x06);
@@ -344,18 +458,15 @@
         {
             if (rsp.size() == 5)
             {
-                if (RspStatus::SUCCESS == RspStatus(rsp[2]))
-                {
-                    log<level::DEBUG>(
-                        "Status::sendModeChange: - Mode change completed successfully");
-                }
-                else
+                if (RspStatus::SUCCESS != RspStatus(rsp[2]))
                 {
                     log<level::ERR>(
                         fmt::format(
                             "Status::sendModeChange: SET MODE failed with status 0x{:02X}",
                             rsp[2])
                             .c_str());
+                    dump_hex(rsp);
+                    status = CmdStatus::FAILURE;
                 }
             }
             else
@@ -363,14 +474,15 @@
                 log<level::ERR>(
                     "Status::sendModeChange: INVALID SET MODE response");
                 dump_hex(rsp);
+                status = CmdStatus::FAILURE;
             }
         }
         else
         {
             if (status == CmdStatus::OPEN_FAILURE)
             {
-                log<level::WARNING>(
-                    "Status::sendModeChange: OCC not active yet");
+                log<level::INFO>("Status::sendModeChange: OCC not active yet");
+                status = CmdStatus::SUCCESS;
             }
             else
             {
@@ -385,6 +497,7 @@
                 "Status::sendModeChange: Unable to set power mode to {}",
                 newMode)
                 .c_str());
+        status = CmdStatus::FAILURE;
     }
 
     return status;
@@ -412,42 +525,49 @@
         return CmdStatus::SUCCESS;
     }
 
-    std::vector<std::uint8_t> cmd, rsp;
-    cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
-    cmd.push_back(0x00); // Data Length (2 bytes)
-    cmd.push_back(0x09);
-    // Data:
-    cmd.push_back(0x11); // Config Format: IPS Settings
-    cmd.push_back(0x00); // Version
-    cmd.push_back(0x00); // IPS Enable: disabled
-    cmd.push_back(0x00); // Enter Delay Time (240s)
-    cmd.push_back(0xF0); //
-    cmd.push_back(0x08); // Enter Utilization (8%)
-    cmd.push_back(0x00); // Exit Delay Time (10s)
-    cmd.push_back(0x0A); //
-    cmd.push_back(0x0C); // Exit Utilization (12%)
+    uint8_t enterUtil, exitUtil;
+    uint16_t enterTime, exitTime;
+    const bool ipsEnabled =
+        getIPSParms(enterUtil, enterTime, exitUtil, exitTime);
+
     log<level::INFO>(
         fmt::format(
-            "Status::sendIpsData: SET_CFG_DATA[IPS] command to OCC{} ({} bytes)",
-            instance, cmd.size())
+            "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
+            ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
             .c_str());
+
+    std::vector<std::uint8_t> cmd, rsp;
+    cmd.reserve(12);
+    cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
+    cmd.push_back(0x00);               // Data Length (2 bytes)
+    cmd.push_back(0x09);               //
+    cmd.push_back(0x11);               // Config Format: IPS Settings
+    cmd.push_back(0x00);               // Version
+    cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
+    cmd.push_back(enterTime >> 8);     // Enter Delay Time
+    cmd.push_back(enterTime & 0xFF);   //
+    cmd.push_back(enterUtil);          // Enter Utilization
+    cmd.push_back(exitTime >> 8);      // Exit Delay Time
+    cmd.push_back(exitTime & 0xFF);    //
+    cmd.push_back(exitUtil);           // Exit Utilization
+    log<level::INFO>(fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] "
+                                 "command to OCC{} ({} bytes)",
+                                 instance, cmd.size())
+                         .c_str());
     status = occCmd.send(cmd, rsp);
     if (status == CmdStatus::SUCCESS)
     {
         if (rsp.size() == 5)
         {
-            if (RspStatus::SUCCESS == RspStatus(rsp[2]))
-            {
-                log<level::DEBUG>(
-                    "Status::sendIpsData: - SET_CFG_DATA[IPS] completed successfully");
-            }
-            else
+            if (RspStatus::SUCCESS != RspStatus(rsp[2]))
             {
                 log<level::ERR>(
                     fmt::format(
                         "Status::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
                         rsp[2])
                         .c_str());
+                dump_hex(rsp);
+                status = CmdStatus::FAILURE;
             }
         }
         else
@@ -455,13 +575,15 @@
             log<level::ERR>(
                 "Status::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
             dump_hex(rsp);
+            status = CmdStatus::FAILURE;
         }
     }
     else
     {
         if (status == CmdStatus::OPEN_FAILURE)
         {
-            log<level::WARNING>("Status::sendIpsData: OCC not active yet");
+            log<level::INFO>("Status::sendIpsData: OCC not active yet");
+            status = CmdStatus::SUCCESS;
         }
         else
         {