Support numeric effecters in dbus-to-host-effecter

Adds support of the numeric effecter PDR (section `28.11 Numeric
Effecter PDR` DSP0248 V1.3.0) type in dbus-to-host-effecter handler.
This handler will be applied for all PLDM termini but not only host.
The setting for one numeric effecter of one device can be:
{
    "mctp_eid": 20,
    "effecter_info": {
        "effecterPdrType": 9,
        "effecterID": 2,
        "entityType": 32903,
        "entityInstance": 2,
        "containerID": 2,
        "compositeEffecterCount": 1,
        "checkHostState": false
    },
    "effecters": [
        {
            "dbus_info": {
                "object_path": "/xyz/openbmc_project/sensors/power/A",
                "interface": "xyz.openbmc_project.Sensor.Value",
                "property_name": "Value",
                "property_type": "double"
            },
            "effecterDataSize": 5,
            "resolution": 1,
            "offset": 0,
            "unitModifier": 0
        }
    ]
}

Where:
+ effecterPdrType to difference state/numeric effecter type. Default
is state effecter.
+ effecterID should be effecter ID and should not empty.
+ checkHostState can be set to false to bypass checking host state.
+ effecterDataSize, resolution, offset, unitModifier are from numeric
effecter PDR (section `28.11 Numeric Effecter PDR` DSP0248 V1.3.0)

Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I438d7f204643edd4066e8a6ba28d53a97503fc4b
diff --git a/common/test/pldm_utils_test.cpp b/common/test/pldm_utils_test.cpp
index d00bebd..bab64fc 100644
--- a/common/test/pldm_utils_test.cpp
+++ b/common/test/pldm_utils_test.cpp
@@ -1090,3 +1090,69 @@
     result = trimNameForDbus(name);
     EXPECT_EQ(expectedName, result);
 }
+
+TEST(dbusPropValuesToDouble, goodTest)
+{
+    double value = 0;
+    bool ret =
+        dbusPropValuesToDouble("uint8_t", static_cast<uint8_t>(0x12), &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x12, value);
+    ret =
+        dbusPropValuesToDouble("int16_t", static_cast<int16_t>(0x1234), &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x1234, value);
+    ret = dbusPropValuesToDouble("uint16_t", static_cast<uint16_t>(0x8234),
+                                 &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x8234, value);
+    ret = dbusPropValuesToDouble("int32_t", static_cast<int32_t>(0x12345678),
+                                 &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x12345678, value);
+    ret = dbusPropValuesToDouble("uint32_t", static_cast<uint32_t>(0x82345678),
+                                 &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x82345678, value);
+    ret = dbusPropValuesToDouble(
+        "int64_t", static_cast<int64_t>(0x1234567898765432), &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x1234567898765432, value);
+    ret = dbusPropValuesToDouble(
+        "uint64_t", static_cast<uint64_t>(0x8234567898765432), &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(0x8234567898765432, value);
+    ret = dbusPropValuesToDouble("double", static_cast<double>(1234.5678),
+                                 &value);
+    EXPECT_EQ(true, ret);
+    EXPECT_EQ(1234.5678, value);
+}
+
+TEST(dbusPropValuesToDouble, badTest)
+{
+    double value = std::numeric_limits<double>::quiet_NaN();
+    /* Type and Data variant are different */
+    bool ret =
+        dbusPropValuesToDouble("uint8_t", static_cast<uint16_t>(0x12), &value);
+    EXPECT_EQ(false, ret);
+    /* Unsupported Types */
+    ret = dbusPropValuesToDouble("string", static_cast<std::string>("hello"),
+                                 &value);
+    EXPECT_EQ(false, ret);
+    ret = dbusPropValuesToDouble("bool", static_cast<bool>(true), &value);
+    EXPECT_EQ(false, ret);
+    ret = dbusPropValuesToDouble("vector<uint8_t>",
+                                 static_cast<std::string>("hello"), &value);
+    EXPECT_EQ(false, ret);
+    ret = dbusPropValuesToDouble("vector<string>",
+                                 static_cast<std::string>("hello"), &value);
+    EXPECT_EQ(false, ret);
+    /* Support Type but Data Type is unsupported */
+    ret = dbusPropValuesToDouble("double", static_cast<std::string>("hello"),
+                                 &value);
+    EXPECT_EQ(false, ret);
+    /* Null pointer */
+    ret = dbusPropValuesToDouble("double", static_cast<std::string>("hello"),
+                                 nullptr);
+    EXPECT_EQ(false, ret);
+}
diff --git a/common/utils.cpp b/common/utils.cpp
index e652021..6f39321 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -677,5 +677,66 @@
     }
     return name;
 }
+
+bool dbusPropValuesToDouble(const std::string_view& type,
+                            const pldm::utils::PropertyValue& value,
+                            double* doubleValue)
+{
+    if (!dbusValueNumericTypeNames.contains(type))
+    {
+        return false;
+    }
+
+    if (!doubleValue)
+    {
+        return false;
+    }
+
+    try
+    {
+        if (type == "uint8_t")
+        {
+            *doubleValue = static_cast<double>(std::get<uint8_t>(value));
+        }
+        else if (type == "int16_t")
+        {
+            *doubleValue = static_cast<double>(std::get<int16_t>(value));
+        }
+        else if (type == "uint16_t")
+        {
+            *doubleValue = static_cast<double>(std::get<uint16_t>(value));
+        }
+        else if (type == "int32_t")
+        {
+            *doubleValue = static_cast<double>(std::get<int32_t>(value));
+        }
+        else if (type == "uint32_t")
+        {
+            *doubleValue = static_cast<double>(std::get<uint32_t>(value));
+        }
+        else if (type == "int64_t")
+        {
+            *doubleValue = static_cast<double>(std::get<int64_t>(value));
+        }
+        else if (type == "uint64_t")
+        {
+            *doubleValue = static_cast<double>(std::get<uint64_t>(value));
+        }
+        else if (type == "double")
+        {
+            *doubleValue = static_cast<double>(std::get<double>(value));
+        }
+        else
+        {
+            return false;
+        }
+    }
+    catch (const std::exception& e)
+    {
+        return false;
+    }
+
+    return true;
+}
 } // namespace utils
 } // namespace pldm
diff --git a/common/utils.hpp b/common/utils.hpp
index 5561399..6c17191 100644
--- a/common/utils.hpp
+++ b/common/utils.hpp
@@ -36,6 +36,15 @@
 {
 namespace utils
 {
+
+const std::set<std::string_view> dbusValueTypeNames = {
+    "bool",    "uint8_t",  "int16_t",         "uint16_t",
+    "int32_t", "uint32_t", "int64_t",         "uint64_t",
+    "double",  "string",   "vector<uint8_t>", "vector<string>"};
+const std::set<std::string_view> dbusValueNumericTypeNames = {
+    "uint8_t",  "int16_t", "uint16_t", "int32_t",
+    "uint32_t", "int64_t", "uint64_t", "double"};
+
 namespace fs = std::filesystem;
 using Json = nlohmann::json;
 constexpr bool Tx = true;
@@ -531,5 +540,18 @@
  */
 std::string_view trimNameForDbus(std::string& name);
 
+/** @brief Convert the number type D-Bus Value to the double
+ *
+ *  @param[in] type - string type should in dbusValueNumericTypeNames list
+ *  @param[in] value - DBus PropertyValue variant
+ *  @param[out] doubleValue - response value
+ *
+ *  @return true if data type is corrected and converting is successful
+ *          otherwise return false.
+ */
+bool dbusPropValuesToDouble(const std::string_view& type,
+                            const pldm::utils::PropertyValue& value,
+                            double* doubleValue);
+
 } // namespace utils
 } // namespace pldm