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/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