Add occ-dbus object

- The intent behind this commit is to add APIs for dynamically
  createing D-Bus objects.

- When temperature values such as DIMM or Core are monitored, D-Bus
  objects need to be dynamically created and the corresponding
  attribute values updated. If the object path exists, only the
  attribute value needs to be updated.

- Currently supports Sensor and OperationalStatus interfaces.

- Also, added Unit Test to occ_dbus file.

Tested: built openpower-occ-control successfully and UTest pass.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I1eac918d6a7a04d6b72b4e68fff868b04dde9c28
diff --git a/Makefile.am b/Makefile.am
index f3863dd..407815a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,6 +25,7 @@
 	occ_errors.cpp \
 	occ_presence.cpp \
 	occ_command.cpp \
+	occ_dbus.cpp \
 	powercap.cpp \
 	org/open_power/OCC/Device/error.cpp \
 	i2c_occ.cpp \
diff --git a/occ_dbus.cpp b/occ_dbus.cpp
new file mode 100644
index 0000000..7028841
--- /dev/null
+++ b/occ_dbus.cpp
@@ -0,0 +1,138 @@
+#include "occ_dbus.hpp"
+
+#include "utils.hpp"
+
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+
+namespace open_power
+{
+namespace occ
+{
+namespace dbus
+{
+
+using namespace phosphor::logging;
+void OccDBusSensors::setMaxValue(const std::string& path, double value)
+{
+    if (sensors.find(path) == sensors.end())
+    {
+        sensors.emplace(
+            path, std::make_unique<SensorIntf>(utils::getBus(), path.c_str()));
+    }
+
+    sensors.at(path)->maxValue(value);
+}
+
+double OccDBusSensors::getMaxValue(const std::string& path) const
+{
+    if (sensors.find(path) != sensors.end())
+    {
+        return sensors.at(path)->maxValue();
+    }
+
+    throw std::invalid_argument("Failed to get MaxValue property.");
+}
+
+void OccDBusSensors::setMinValue(const std::string& path, double value)
+{
+    if (sensors.find(path) == sensors.end())
+    {
+        sensors.emplace(
+            path, std::make_unique<SensorIntf>(utils::getBus(), path.c_str()));
+    }
+
+    sensors.at(path)->minValue(value);
+}
+
+double OccDBusSensors::getMinValue(const std::string& path) const
+{
+    if (sensors.find(path) != sensors.end())
+    {
+        return sensors.at(path)->minValue();
+    }
+
+    throw std::invalid_argument("Failed to get MinValue property.");
+}
+
+void OccDBusSensors::setValue(const std::string& path, double value)
+{
+    if (sensors.find(path) == sensors.end())
+    {
+        sensors.emplace(
+            path, std::make_unique<SensorIntf>(utils::getBus(), path.c_str()));
+    }
+
+    sensors.at(path)->value(value);
+}
+
+double OccDBusSensors::getValue(const std::string& path) const
+{
+    if (sensors.find(path) != sensors.end())
+    {
+        return sensors.at(path)->value();
+    }
+
+    throw std::invalid_argument("Failed to get Value property.");
+}
+
+void OccDBusSensors::setUnit(const std::string& path, const std::string& value)
+{
+    if (sensors.find(path) == sensors.end())
+    {
+        sensors.emplace(
+            path, std::make_unique<SensorIntf>(utils::getBus(), path.c_str()));
+    }
+
+    try
+    {
+        sensors.at(path)->unit(SensorIntf::convertUnitFromString(value));
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("set Unit propety failed", entry("ERROR=%s", e.what()));
+    }
+}
+
+std::string OccDBusSensors::getUnit(const std::string& path) const
+{
+    if (sensors.find(path) != sensors.end())
+    {
+        try
+        {
+            return SensorIntf::convertUnitToString(sensors.at(path)->unit());
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("get Unit propety failed",
+                            entry("ERROR=%s", e.what()));
+        }
+    }
+
+    throw std::invalid_argument("Failed to get Unit property.");
+}
+
+void OccDBusSensors::setOperationalStatus(const std::string& path, bool value)
+{
+    if (operationalStatus.find(path) == operationalStatus.end())
+    {
+        operationalStatus.emplace(path, std::make_unique<OperationalStatusIntf>(
+                                            utils::getBus(), path.c_str()));
+    }
+
+    operationalStatus.at(path)->functional(value);
+}
+
+bool OccDBusSensors::getOperationalStatus(const std::string& path) const
+{
+    if (operationalStatus.find(path) != operationalStatus.end())
+    {
+        return operationalStatus.at(path)->functional();
+    }
+
+    throw std::invalid_argument("Failed to get OperationalStatus property.");
+}
+
+} // namespace dbus
+} // namespace occ
+} // namespace open_power
diff --git a/occ_dbus.hpp b/occ_dbus.hpp
new file mode 100644
index 0000000..2443a53
--- /dev/null
+++ b/occ_dbus.hpp
@@ -0,0 +1,133 @@
+#pragma once
+
+#include <xyz/openbmc_project/Sensor/Value/server.hpp>
+#include <xyz/openbmc_project/State/Decorator/OperationalStatus/server.hpp>
+
+namespace open_power
+{
+namespace occ
+{
+namespace dbus
+{
+
+using ObjectPath = std::string;
+
+using SensorIntf = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
+using OperationalStatusIntf = 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
+ *         the corresponding properties value.
+ */
+class OccDBusSensors
+{
+  private:
+    OccDBusSensors()
+    {
+    }
+
+  public:
+    OccDBusSensors(const OccDBusSensors&) = delete;
+    OccDBusSensors(OccDBusSensors&&) = delete;
+    OccDBusSensors& operator=(const OccDBusSensors&) = delete;
+    OccDBusSensors& operator=(OccDBusSensors&&) = delete;
+    ~OccDBusSensors() = default;
+
+    static OccDBusSensors& getOccDBus()
+    {
+        static OccDBusSensors customDBus;
+        return customDBus;
+    }
+
+  public:
+    /** @brief Set the max value of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @param[in] value - The value of the MaxValue property
+     */
+    void setMaxValue(const std::string& path, double value);
+
+    /** @brief Get the max value of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @return bool     - The value of the MaxValue property
+     */
+    double getMaxValue(const std::string& path) const;
+
+    /** @brief Set the min value of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @param[in] value - The value of the MinValue property
+     */
+    void setMinValue(const std::string& path, double value);
+
+    /** @brief Get the min value of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @return bool     - The value of the MinValue property
+     */
+    double getMinValue(const std::string& path) const;
+
+    /** @brief Set the value of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @param[in] value - The value of the Value property
+     */
+    void setValue(const std::string& path, double value);
+
+    /** @brief Get the value of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @return bool     - The value of the Value property
+     */
+    double getValue(const std::string& path) const;
+
+    /** @brief Set the unit of the Sensor
+     *
+     *  @param[in] path  - The object path
+     *
+     *  @param[in] value - The value of the Unit property
+     */
+    void setUnit(const std::string& path, const std::string& value);
+
+    /** @brief Get the unit of the Sensor
+     *
+     *  @param[in] path       - The object path
+     *
+     *  @return std::string   - The value of the Unit property
+     */
+    std::string getUnit(const std::string& path) const;
+
+    /** @brief Set the Functional property
+     *
+     *  @param[in] path   - The object path
+     *
+     *  @param[in] value  - PLDM operational fault status
+     */
+    void setOperationalStatus(const std::string& path, bool value);
+
+    /** @brief Get the Functional property
+     *
+     *  @param[in] path   - The object path
+     *
+     *  @return status    - PLDM operational fault status
+     */
+    bool getOperationalStatus(const std::string& path) const;
+
+  private:
+    std::map<ObjectPath, std::unique_ptr<SensorIntf>> sensors;
+
+    std::map<ObjectPath, std::unique_ptr<OperationalStatusIntf>>
+        operationalStatus;
+};
+
+} // namespace dbus
+} // namespace occ
+} // namespace open_power
diff --git a/test/Makefile.am b/test/Makefile.am
index 1fee251..3fbcc7c 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -14,7 +14,8 @@
 
 utest_SOURCES = error_files_tests.cpp \
                 utest.cpp \
-                TestI2cOcc.cpp
+                TestI2cOcc.cpp \
+                occ_dbus_test.cpp
 
 utest_CPPFLAGS = $(GTEST_CPPFLAGS) \
                  $(AM_CPPFLAGS)
diff --git a/test/occ_dbus_test.cpp b/test/occ_dbus_test.cpp
new file mode 100644
index 0000000..d11a373
--- /dev/null
+++ b/test/occ_dbus_test.cpp
@@ -0,0 +1,74 @@
+#include <occ_dbus.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace open_power::occ::dbus;
+
+TEST(OccDBusSensors, MaxValue)
+{
+    std::string tmpPath = "/abc/def";
+    double maxValue = 100.00;
+    double retMaxValue = 0;
+
+    OccDBusSensors::getOccDBus().setMaxValue(tmpPath, maxValue);
+    retMaxValue = OccDBusSensors::getOccDBus().getMaxValue(tmpPath);
+
+    EXPECT_EQ(maxValue, retMaxValue);
+    ASSERT_THROW(OccDBusSensors::getOccDBus().getMaxValue("/abcd/"),
+                 std::invalid_argument);
+}
+
+TEST(OccDBusSensors, MinValue)
+{
+    std::string tmpPath = "/abc/def";
+    double minValue = 10.00;
+    double retMinValue = 0;
+
+    OccDBusSensors::getOccDBus().setMinValue(tmpPath, minValue);
+    retMinValue = OccDBusSensors::getOccDBus().getMinValue(tmpPath);
+
+    EXPECT_EQ(minValue, retMinValue);
+    ASSERT_THROW(OccDBusSensors::getOccDBus().getMinValue("/abcd/"),
+                 std::invalid_argument);
+}
+
+TEST(OccDBusSensors, Value)
+{
+    std::string tmpPath = "/abc/def";
+    double value = 30.00;
+    double retValue = 0;
+
+    OccDBusSensors::getOccDBus().setValue(tmpPath, value);
+    retValue = OccDBusSensors::getOccDBus().getValue(tmpPath);
+
+    EXPECT_EQ(value, retValue);
+    ASSERT_THROW(OccDBusSensors::getOccDBus().getValue("/abcd/"),
+                 std::invalid_argument);
+}
+
+TEST(OccDBusSensors, Unit)
+{
+    std::string tmpPath = "/abc/def";
+    const std::string unit = "xyz.openbmc_project.Sensor.Value.Unit.DegreesC";
+    std::string retUnit = "";
+
+    OccDBusSensors::getOccDBus().setUnit(tmpPath, unit);
+    retUnit = OccDBusSensors::getOccDBus().getUnit(tmpPath);
+
+    EXPECT_EQ(unit, retUnit);
+    ASSERT_THROW(OccDBusSensors::getOccDBus().getUnit("/abcd/"),
+                 std::invalid_argument);
+}
+
+TEST(OccDBusSensors, OperationalStatus)
+{
+    std::string tmpPath = "/abc/def";
+    bool retStatus = false;
+
+    OccDBusSensors::getOccDBus().setOperationalStatus(tmpPath, true);
+    retStatus = OccDBusSensors::getOccDBus().getOperationalStatus(tmpPath);
+
+    EXPECT_EQ(true, retStatus);
+    ASSERT_THROW(OccDBusSensors::getOccDBus().getOperationalStatus("/abcd/"),
+                 std::invalid_argument);
+}
\ No newline at end of file