Read proc temps and dimm temps

This commit uses openpower-occ-control to monitor the temperature and
power sensors, and create sensors on D-BUS. In the loop, read the
sensor values every 1 seconds.

Tested:
I use virtual data to get sensor values:
busctl tree org.open_power.OCC.Control
|-/org
| `-/org/open_power
|   `-/org/open_power/control
|     |-/org/open_power/control/occ0
|     `-/org/open_power/control/occ1
`-/xyz
  `-/xyz/openbmc_project
    `-/xyz/openbmc_project/sensors
      |-/xyz/openbmc_project/sensors/power
      | |-/xyz/openbmc_project/sensors/power/p0_mem_2_power
      | |-/xyz/openbmc_project/sensors/power/p0_mem_power
      | |-/xyz/openbmc_project/sensors/power/p0_power
      | |-/xyz/openbmc_project/sensors/power/p1_mem_power
      | |-/xyz/openbmc_project/sensors/power/p1_power
      | |-/xyz/openbmc_project/sensors/power/p2_mem_power
      | |-/xyz/openbmc_project/sensors/power/p2_power
      | |-/xyz/openbmc_project/sensors/power/p3_mem_power
      | |-/xyz/openbmc_project/sensors/power/p3_power
      | `-/xyz/openbmc_project/sensors/power/total_power
      `-/xyz/openbmc_project/sensors/temperature
        |-/xyz/openbmc_project/sensors/temperature/dimm5_dram_temp
        |-/xyz/openbmc_project/sensors/temperature/dimm9_dram_temp
        |-/xyz/openbmc_project/sensors/temperature/proc0_core2_temp
        |-/xyz/openbmc_project/sensors/temperature/proc0_core3_temp
        |-/xyz/openbmc_project/sensors/temperature/proc1_core2_temp
        |-/xyz/openbmc_project/sensors/temperature/proc1_core3_temp
        |-/xyz/openbmc_project/sensors/temperature/vrm_vdd0_temp
        `-/xyz/openbmc_project/sensors/temperature/vrm_vdd1_temp

busctl introspect org.open_power.OCC.Control
/xyz/openbmc_project/sensors/temperature/proc0_core3_temp
NAME                                TYPE      SIGNATURE RESULT/VALUE
org.freedesktop.DBus.Introspectable interface -         -
.Introspect                         method    -         s
org.freedesktop.DBus.Peer           interface -         -
.GetMachineId                       method    -         s
.Ping                               method    -         -
org.freedesktop.DBus.Properties     interface -         -
.Get                                method    ss        v
.GetAll                             method    s         a{sv}
.Set                                method    ssv       -
.PropertiesChanged                  signal    sa{sv}as  -
xyz.openbmc_project.Sensor.Value    interface -         -
.MaxValue                           property  d         0
.MinValue                           property  d         0
.Unit                               property  s         "xyz.openbmc...
.Value                              property  d         49
xyz.openbmc_project.State.Decorator.OperationalStatus interface -  -
.Functional                         property  b  true

busctl introspect org.open_power.OCC.Control
/xyz/openbmc_project/sensors/power/total_power
NAME                                TYPE      SIGNATURE RESULT/VALUE
org.freedesktop.DBus.Introspectable interface -         -
.Introspect                         method    -         s
org.freedesktop.DBus.Peer           interface -         -
.GetMachineId                       method    -         s
.Ping                               method    -         -
org.freedesktop.DBus.Properties     interface -         -
.Get                                method    ss        v
.GetAll                             method    s         a
.Set                                method    ssv       -
.PropertiesChanged                  signal    sa{sv}as  -
xyz.openbmc_project.Sensor.Value    interface -         -
.MaxValue                           property  d         0
.MinValue                           property  d         0
.Unit                               property  s         "xyz.openbmc...
.Value                              property  d         83
xyz.openbmc_project.State.Decorator.OperationalStatus interface -  -
.Functional                         property  b  true

Signed-off-by: Chicago Duan <duanzhijia01@inspur.com>
Change-Id: Iff30ab51745dab500fa19aa4c35b07e0052ac665
diff --git a/app.cpp b/app.cpp
index a26fa3c..59c872d 100644
--- a/app.cpp
+++ b/app.cpp
@@ -35,6 +35,9 @@
     bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
 
     sdbusplus::server::manager::manager objManager(bus, OCC_CONTROL_ROOT);
+#ifdef READ_OCC_SENSORS
+    sdbusplus::server::manager::manager objManagerXyz(bus, OCC_SENSORS_ROOT);
+#endif
     open_power::occ::Manager mgr(eventP);
 
     // Claim the bus since all the house keeping is done now
diff --git a/configure.ac b/configure.ac
index b3c62e9..32a6ecc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,17 @@
         AC_SUBST([CPPFLAGS], [$cpp_flags])
     )
 
+    AC_ARG_ENABLE([read-occ-sensors],
+        AS_HELP_STRING([--enable-read-occ-sensors], [Enable read occ sensors support])
+    )
+    AS_IF([test "x$enable_read_occ_sensors" == "xyes"],
+        AC_MSG_NOTICE([Enabling read occ sensors])
+        [
+            cpp_flags="$CPPFLAGS -DREAD_OCC_SENSORS"
+        ]
+        AC_SUBST([CPPFLAGS], [$cpp_flags])
+    )
+
     AC_ARG_WITH([host-communication-protocol],
         AS_HELP_STRING([--with-host-communication-protocol], [To specify the host communication protocol])
     )
@@ -108,6 +119,10 @@
 AS_IF([test "x$OCC_CONTROL_ROOT" == "x"], [OCC_CONTROL_ROOT="/org/open_power/control"])
 AC_DEFINE_UNQUOTED([OCC_CONTROL_ROOT], ["$OCC_CONTROL_ROOT"], [The Dbus root])
 
+AC_ARG_VAR(OCC_SENSORS_ROOT, [The sensors Dbus root])
+AS_IF([test "x$OCC_SENSORS_ROOT" == "x"], [OCC_SENSORS_ROOT="/xyz/openbmc_project/sensors"])
+AC_DEFINE_UNQUOTED([OCC_SENSORS_ROOT], ["$OCC_SENSORS_ROOT"], [The sensors Dbus root])
+
 AC_ARG_VAR(MAX_CPUS, [The max number of CPUs])
 AS_IF([test "x$MAX_CPUS" == "x"], [MAX_CPUS=2])
 AC_DEFINE_UNQUOTED([MAX_CPUS], [$MAX_CPUS], [The max number of CPUs])
@@ -124,6 +139,14 @@
 AS_IF([test "x$OCC_MASTER_NAME" == "x"], [OCC_MASTER_NAME="occ-hwmon.1"])
 AC_DEFINE_UNQUOTED([OCC_MASTER_NAME], ["$OCC_MASTER_NAME"], [The OCC master object name])
 
+AC_ARG_VAR(OCC_CPU_TEMP_SENSOR_TYPE, [The CPU temp sensor type])
+AS_IF([test "x$OCC_CPU_TEMP_SENSOR_TYPE" == "x"], [OCC_CPU_TEMP_SENSOR_TYPE="C0"])
+AC_DEFINE_UNQUOTED([OCC_CPU_TEMP_SENSOR_TYPE], ["$OCC_CPU_TEMP_SENSOR_TYPE"], [The CPU temp sensor type])
+
+AC_ARG_VAR(OCC_DIMM_TEMP_SENSOR_TYPE, [The dimm temp sensor type])
+AS_IF([test "x$OCC_DIMM_TEMP_SENSOR_TYPE" == "x"], [OCC_DIMM_TEMP_SENSOR_TYPE="D0"])
+AC_DEFINE_UNQUOTED([OCC_DIMM_TEMP_SENSOR_TYPE], ["$OCC_DIMM_TEMP_SENSOR_TYPE"], [The dimm temp sensor type])
+
 AC_ARG_VAR(OCC_HWMON_PATH, [The OCC hwmon path])
 AC_ARG_VAR(DEV_PATH, [The device path])
 AC_ARG_VAR(I2C_OCC_DEVICE_NAME, [The device name of i2c occ hwmon])
diff --git a/occ_dbus.hpp b/occ_dbus.hpp
index 7a7c69c..330f140 100644
--- a/occ_dbus.hpp
+++ b/occ_dbus.hpp
@@ -12,9 +12,11 @@
 
 using ObjectPath = std::string;
 
-using SensorIntf = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
-using OperationalStatusIntf = sdbusplus::xyz::openbmc_project::State::
-    Decorator::server::OperationalStatus;
+using SensorIntf = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Sensor::server::Value>;
+using OperationalStatusIntf =
+    sdbusplus::server::object::object<sdbusplus::xyz::openbmc_project::State::
+                                          Decorator::server::OperationalStatus>;
 
 /** @class OccDBusSensors
  *  @brief This is a custom D-Bus object, used to add D-Bus interface and update
diff --git a/occ_manager.cpp b/occ_manager.cpp
index 3ebd90a..4c979df 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -3,11 +3,14 @@
 #include "occ_manager.hpp"
 
 #include "i2c_occ.hpp"
+#include "occ_dbus.hpp"
 #include "utils.hpp"
 
+#include <cmath>
 #include <experimental/filesystem>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
+#include <regex>
 #include <xyz/openbmc_project/Common/error.hpp>
 
 namespace open_power
@@ -175,11 +178,321 @@
     {
         // Read sysfs to force kernel to poll OCC
         obj->readOccState();
+
+#ifdef READ_OCC_SENSORS
+        // Read occ sensor values
+        auto id = obj->getOccInstanceID();
+        if (!obj->occActive())
+        {
+            // Occ not activated
+            setSensorValueToNaN(id);
+            continue;
+        }
+        getSensorValues(id, obj->isMasterOcc());
+#endif
     }
 
     // Restart OCC poll timer
     _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
 }
 
+#ifdef READ_OCC_SENSORS
+void Manager::readTempSensors(const fs::path& path, uint32_t id)
+{
+    const int open_errno = errno;
+    std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
+    for (auto& file : fs::directory_iterator(path))
+    {
+        if (!std::regex_search(file.path().string(), expr))
+        {
+            continue;
+        }
+        std::ifstream fileOpen(file.path(), std::ios::in);
+        if (!fileOpen)
+        {
+            // If not able to read, OCC may be offline
+            log<level::DEBUG>(
+                fmt::format("readTempSensors: open failed(errno = {}) ",
+                            open_errno)
+                    .c_str());
+
+            continue;
+        }
+        std::string labelValue;
+        fileOpen >> labelValue;
+        fileOpen.close();
+
+        const std::string& tempLabel = "label";
+        const std::string filePathString = file.path().string().substr(
+            0, file.path().string().length() - tempLabel.length());
+        std::ifstream fruTypeFile(filePathString + "fru_type", std::ios::in);
+        if (!fruTypeFile)
+        {
+            // If not able to read, OCC may be offline
+            log<level::DEBUG>(
+                fmt::format("readTempSensors: open failed(errno = {}) ",
+                            open_errno)
+                    .c_str());
+            continue;
+        }
+        uint32_t fruTypeValue{0};
+        fruTypeFile >> fruTypeValue;
+        fruTypeFile.close();
+
+        std::string sensorPath =
+            OCC_SENSORS_ROOT + std::string("/temperature/");
+
+        if (fruTypeValue == VRMVdd)
+        {
+            sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp");
+        }
+        else
+        {
+            auto sensorTypeID =
+                open_power::occ::utils::checkLabelValue(labelValue);
+            if (sensorTypeID == std::nullopt)
+            {
+                continue;
+            }
+            auto& [type, instanceID] = *sensorTypeID;
+
+            if (type == OCC_DIMM_TEMP_SENSOR_TYPE)
+            {
+                auto iter = dimmTempSensorName.find(fruTypeValue);
+                if (iter == dimmTempSensorName.end())
+                {
+                    log<level::ERR>(fmt::format("readTempSensors: Fru type "
+                                                "error! fruTypeValue = {}) ",
+                                                fruTypeValue)
+                                        .c_str());
+                    continue;
+                }
+
+                sensorPath.append("dimm" + std::to_string(instanceID) +
+                                  iter->second);
+            }
+            else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
+            {
+                if (fruTypeValue != processorCore)
+                {
+                    log<level::ERR>(fmt::format("readTempSensors: Fru type "
+                                                "error! fruTypeValue = {}) ",
+                                                fruTypeValue)
+                                        .c_str());
+                    continue;
+                }
+
+                sensorPath.append("proc" + std::to_string(id) + "_core" +
+                                  std::to_string(instanceID) + "_temp");
+            }
+            else
+            {
+                continue;
+            }
+        }
+
+        std::ifstream faultPathFile(filePathString + "fault", std::ios::in);
+        if (faultPathFile)
+        {
+            uint32_t faultValue;
+            faultPathFile >> faultValue;
+            faultPathFile.close();
+
+            if (faultValue != 0)
+            {
+                open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
+                    sensorPath, std::numeric_limits<double>::quiet_NaN());
+
+                open_power::occ::dbus::OccDBusSensors::getOccDBus()
+                    .setOperationalStatus(sensorPath, false);
+
+                continue;
+            }
+        }
+
+        std::ifstream inputFile(filePathString + "input", std::ios::in);
+        if (inputFile)
+        {
+            double tempValue;
+            inputFile >> tempValue;
+
+            inputFile.close();
+
+            open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
+                sensorPath, tempValue * std::pow(10, -3));
+
+            open_power::occ::dbus::OccDBusSensors::getOccDBus()
+                .setOperationalStatus(sensorPath, true);
+
+            existingSensors[sensorPath] = id;
+        }
+        else
+        {
+            // If not able to read, OCC may be offline
+            log<level::DEBUG>(
+                fmt::format("readTempSensors: open failed(errno = {}) ",
+                            open_errno)
+                    .c_str());
+        }
+    }
+    return;
+}
+
+std::optional<std::string>
+    Manager::getPowerLabelFunctionID(const std::string& value)
+{
+    // If the value is "system", then the FunctionID is "system".
+    if (value == "system")
+    {
+        return value;
+    }
+
+    // If the value is not "system", then the label value have 3 numbers, of
+    // which we only care about the middle one:
+    // <sensor id>_<function id>_<apss channel>
+    // eg: The value is "0_10_5" , then the FunctionID is "10".
+    if (value.find("_") == std::string::npos)
+    {
+        return std::nullopt;
+    }
+
+    auto powerLabelValue = value.substr((value.find("_") + 1));
+
+    if (powerLabelValue.find("_") == std::string::npos)
+    {
+        return std::nullopt;
+    }
+
+    return powerLabelValue.substr(0, powerLabelValue.find("_"));
+}
+
+void Manager::readPowerSensors(const fs::path& path, uint32_t id)
+{
+    const int open_errno = errno;
+    std::regex expr{"power\\d+_label$"}; // Example: power5_label
+    for (auto& file : fs::directory_iterator(path))
+    {
+        if (!std::regex_search(file.path().string(), expr))
+        {
+            continue;
+        }
+        std::ifstream fileOpen(file.path(), std::ios::in);
+        if (!fileOpen)
+        {
+            // If not able to read, OCC may be offline
+            log<level::DEBUG>(
+                fmt::format("readPowerSensors: open failed(errno = {}) ",
+                            open_errno)
+                    .c_str());
+
+            continue;
+        }
+        std::string labelValue;
+        fileOpen >> labelValue;
+        fileOpen.close();
+
+        auto functionID = getPowerLabelFunctionID(labelValue);
+        if (functionID == std::nullopt)
+        {
+            continue;
+        }
+
+        const std::string& tempLabel = "label";
+        const std::string filePathString = file.path().string().substr(
+            0, file.path().string().length() - tempLabel.length());
+
+        std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/");
+
+        auto iter = powerSensorName.find(*functionID);
+        if (iter == powerSensorName.end())
+        {
+            continue;
+        }
+        sensorPath.append(iter->second);
+
+        std::ifstream faultPathFile(filePathString + "fault", std::ios::in);
+        if (faultPathFile)
+        {
+            uint32_t faultValue{0};
+            faultPathFile >> faultValue;
+            faultPathFile.close();
+
+            if (faultValue != 0)
+            {
+                open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
+                    sensorPath, std::numeric_limits<double>::quiet_NaN());
+
+                open_power::occ::dbus::OccDBusSensors::getOccDBus()
+                    .setOperationalStatus(sensorPath, false);
+
+                continue;
+            }
+        }
+
+        std::ifstream inputFile(filePathString + "input", std::ios::in);
+        if (inputFile)
+        {
+            double tempValue;
+            inputFile >> tempValue;
+            inputFile.close();
+
+            open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
+                sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3));
+
+            open_power::occ::dbus::OccDBusSensors::getOccDBus()
+                .setOperationalStatus(sensorPath, true);
+
+            existingSensors[sensorPath] = id;
+        }
+        else
+        {
+            // If not able to read, OCC may be offline
+            log<level::DEBUG>(
+                fmt::format("readPowerSensors: open failed(errno = {}) ",
+                            open_errno)
+                    .c_str());
+        }
+    }
+    return;
+}
+
+void Manager::setSensorValueToNaN(uint32_t id)
+{
+    for (const auto& [sensorPath, occId] : existingSensors)
+    {
+        if (occId == id)
+        {
+            open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
+                sensorPath, std::numeric_limits<double>::quiet_NaN());
+        }
+    }
+    return;
+}
+
+void Manager::getSensorValues(uint32_t id, bool masterOcc)
+{
+    const auto occ = std::string("occ-hwmon.") + std::to_string(id + 1);
+
+    fs::path fileName{OCC_HWMON_PATH + occ + "/hwmon/"};
+
+    // Need to get the hwmonXX directory name, there better only be 1 dir
+    assert(std::distance(fs::directory_iterator(fileName),
+                         fs::directory_iterator{}) == 1);
+    // Now set our path to this full path, including this hwmonXX directory
+    fileName = fs::path(*fs::directory_iterator(fileName));
+
+    // Read temperature sensors
+    readTempSensors(fileName, id);
+
+    if (masterOcc)
+    {
+        // Read power sensors
+        readPowerSensors(fileName, id);
+    }
+
+    return;
+}
+#endif
+
 } // namespace occ
 } // namespace open_power
diff --git a/occ_manager.hpp b/occ_manager.hpp
index ece7fb7..e6e8cbb 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -24,8 +24,21 @@
 namespace occ
 {
 
+#ifdef READ_OCC_SENSORS
+enum occFruType
+{
+    processorCore = 0,
+    internalMemCtlr = 1,
+    dimm = 2,
+    memCtrlAndDimm = 3,
+    VRMVdd = 6,
+    PMIC = 7,
+    memCtlrExSensor = 8
+};
+#endif
+
 /** @brief Default time, in seconds, between OCC poll commands */
-constexpr unsigned int defaultPollingInterval = 10;
+constexpr unsigned int defaultPollingInterval = 1;
 
 /** @class Manager
  *  @brief Builds and manages OCC objects
@@ -178,6 +191,68 @@
      * OCC. The poll timer will then be restarted.
      * */
     void pollerTimerExpired();
+
+#ifdef READ_OCC_SENSORS
+    /**
+     * @brief Gets the occ sensor values.
+     * @param[in] id - Id of the OCC.
+     * @param[in] masterOcc - Is this OCC the master OCC.
+     * */
+    void getSensorValues(uint32_t id, bool masterOcc);
+
+    /**
+     * @brief Trigger OCC driver to read the temperature sensors.
+     * @param[in] path - path of the OCC sensors.
+     * @param[in] id - Id of the OCC.
+     * */
+    void readTempSensors(const fs::path& path, uint32_t id);
+
+    /**
+     * @brief Trigger OCC driver to read the power sensors.
+     * @param[in] path - path of the OCC sensors.
+     * @param[in] id - Id of the OCC.
+     * */
+    void readPowerSensors(const fs::path& path, uint32_t id);
+
+    /**
+     * @brief Set all sensor values of this OCC to NaN.
+     * @param[in] id - Id of the OCC.
+     * */
+    void setSensorValueToNaN(uint32_t id);
+
+    /** @brief Store the existing OCC sensors on D-BUS */
+    std::map<std::string, uint32_t> existingSensors;
+
+    /** @brief Get FunctionID from the `powerX_label` file.
+     *  @param[in] value - the value of the `powerX_label` file.
+     *  @returns FunctionID of the power sensors.
+     */
+    std::optional<std::string>
+        getPowerLabelFunctionID(const std::string& value);
+
+    /** @brief The power sensor names map */
+    const std::map<std::string, std::string> powerSensorName = {
+        {"system", "total_power"}, {"1", "p0_mem_power"},
+        {"2", "p1_mem_power"},     {"3", "p2_mem_power"},
+        {"4", "p3_mem_power"},     {"5", "p0_power"},
+        {"6", "p1_power"},         {"7", "p2_power"},
+        {"8", "p3_power"},         {"9", "p0_cache_power"},
+        {"10", "p1_cache_power"},  {"11", "p2_cache_power"},
+        {"12", "p3_cache_power"},  {"13", "io_a_power"},
+        {"14", "io_b_power"},      {"15", "io_c_power"},
+        {"16", "fans_a_power"},    {"17", "fans_b_power"},
+        {"18", "storage_a_power"}, {"19", "storage_b_power"},
+        {"23", "mem_cache_power"}, {"25", "p0_mem_0_power"},
+        {"26", "p0_mem_1_power"},  {"27", "p0_mem_2_power"}};
+
+    /** @brief The dimm temperature sensor names map */
+    const std::map<uint32_t, std::string> dimmTempSensorName = {
+        {internalMemCtlr, "_intmb_temp"},
+        {dimm, "_dram_temp"},
+        {memCtrlAndDimm, "_dram_extmb_temp"},
+        {PMIC, "_pmic_temp"},
+        {memCtlrExSensor, "_extmb_temp"}};
+#endif
 };
 
 } // namespace occ
diff --git a/occ_status.hpp b/occ_status.hpp
index 415e0b7..a1cf82e 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -146,6 +146,18 @@
         return device.addPresenceWatchMaster();
     }
 
+    /** @brief Gets the occ instance number */
+    unsigned int getOccInstanceID()
+    {
+        return instance;
+    }
+
+    /** @brief Is this OCC the master OCC */
+    bool isMasterOcc()
+    {
+        return device.master();
+    }
+
     /** @brief Read OCC state (will trigger kernel to poll the OCC) */
     void readOccState();