libpldmresponder:Support GetNumericEffecterValue responder

The GetNumericEffecterValue command is used to return the present
numeric setting of a PLDM Numeric Effecter.
This commit adds responder support for GetNumericEffecterValue Cmd
as mentioned in Section22.3 of DSP0248_1.2.0.
Currently "effecterOperationalState" field in the response msg is
set to "enabled-noUpdatePending". Actual value can be fetched once
the support for EffecterEvent/SetNumericEffecterEnable is added.

Test:
    Using pldmtool raw command
    Success Case:
        pldmtool raw -d 0x80 0x02 0x32 0x23 00
        pldmtool: Tx: 08 01 80 02 32 23 00
        pldmtool: Rx: 08 01 00 02 32 00 00 01 03 03
   Failure Case:
        pldmtool raw -d 0x80 0x02 0x32 0x25 0x00
        pldmtool: Tx: 08 01 80 02 32 25 00
        pldmtool: Rx: 08 01 00 02 32 80

Change-Id: I64a7205d8f69e3925b75b02558a0857b63c70ebf
Signed-off-by: Archana Kakani <archana.kakani@ibm.com>
diff --git a/libpldmresponder/pdr_utils.cpp b/libpldmresponder/pdr_utils.cpp
index acaedd7..4a7d016 100644
--- a/libpldmresponder/pdr_utils.cpp
+++ b/libpldmresponder/pdr_utils.cpp
@@ -259,6 +259,28 @@
 
     return frus;
 }
+
+size_t getEffecterDataSize(uint8_t effecterDataSize)
+{
+    switch (effecterDataSize)
+    {
+        case PLDM_EFFECTER_DATA_SIZE_UINT8:
+            return sizeof(uint8_t);
+        case PLDM_EFFECTER_DATA_SIZE_SINT8:
+            return sizeof(int8_t);
+        case PLDM_EFFECTER_DATA_SIZE_UINT16:
+            return sizeof(uint16_t);
+        case PLDM_EFFECTER_DATA_SIZE_SINT16:
+            return sizeof(int16_t);
+        case PLDM_EFFECTER_DATA_SIZE_UINT32:
+            return sizeof(uint32_t);
+        case PLDM_EFFECTER_DATA_SIZE_SINT32:
+            return sizeof(int32_t);
+        default:
+            return 0;
+    }
+}
+
 } // namespace pdr_utils
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/pdr_utils.hpp b/libpldmresponder/pdr_utils.hpp
index b44120a..4a95ebe 100644
--- a/libpldmresponder/pdr_utils.hpp
+++ b/libpldmresponder/pdr_utils.hpp
@@ -242,6 +242,14 @@
 std::vector<FruRecordDataFormat> parseFruRecordTable(const uint8_t* fruData,
                                                      size_t fruLen);
 
+/** @brief Return the size of data type based on the effecterDataSize enum value
+ *
+ *  @param[in] effecterDataSize - Bitwidth and format of setting effecter value
+ *  @return[out] Map the effecterDataSize enum value to datatype and return the
+ *               size of dataType
+ */
+size_t getEffecterDataSize(uint8_t effecterDataSize);
+
 } // namespace pdr_utils
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
index 5f71252..67264c8 100644
--- a/libpldmresponder/platform.cpp
+++ b/libpldmresponder/platform.cpp
@@ -593,6 +593,67 @@
     return PLDM_SUCCESS;
 }
 
+Response Handler::getNumericEffecterValue(const pldm_msg* request,
+                                          size_t payloadLength)
+{
+    if (payloadLength != PLDM_GET_NUMERIC_EFFECTER_VALUE_REQ_BYTES)
+    {
+        return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
+    }
+
+    uint16_t effecterId{};
+    auto rc = decode_get_numeric_effecter_value_req(request, payloadLength,
+                                                    &effecterId);
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    const pldm::utils::DBusHandler dBusIntf;
+    uint8_t effecterDataSize{};
+    pldm::utils::PropertyValue dbusValue;
+    std::string propertyType;
+    using effecterOperationalState = uint8_t;
+    using completionCode = uint8_t;
+
+    rc = platform_numeric_effecter::getNumericEffecterData<
+        pldm::utils::DBusHandler, Handler>(
+        dBusIntf, *this, effecterId, effecterDataSize, propertyType, dbusValue);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    // Refer DSP0248_1.2.0.pdf (section 22.3, Table 48)
+    // Completion Code (uint8), Effecter Data Size(uint8), Effecter Operational
+    // State(uint8), PendingValue (uint8|sint8|uint16|sint16|uint32|sint32 )
+    // PresentValue (uint8|sint8|uint16|sint16|uint32|sint32 )
+    // Size of PendingValue and PresentValue calculated based on size is
+    // provided in effecter data size
+    size_t responsePayloadLength = sizeof(completionCode) +
+                                   sizeof(effecterDataSize) +
+                                   sizeof(effecterOperationalState) +
+                                   getEffecterDataSize(effecterDataSize) +
+                                   getEffecterDataSize(effecterDataSize);
+
+    Response response(responsePayloadLength + sizeof(pldm_msg_hdr));
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    rc = platform_numeric_effecter::getNumericEffecterValueHandler(
+        propertyType, dbusValue, effecterDataSize, responsePtr,
+        responsePayloadLength, request->hdr.instance_id);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        error(
+            "Reponse to GetNumericEffecterValue failed RC={RC} for EffectorId={EFFECTER_ID} ",
+            "RC", rc, "EFFECTER_ID", effecterId);
+        return ccOnlyResponse(request, rc);
+    }
+    return response;
+}
+
 Response Handler::setNumericEffecterValue(const pldm_msg* request,
                                           size_t payloadLength)
 {
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 8d2c74e..8df9625 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -77,6 +77,10 @@
                          [this](const pldm_msg* request, size_t payloadLength) {
             return this->setNumericEffecterValue(request, payloadLength);
         });
+        handlers.emplace(PLDM_GET_NUMERIC_EFFECTER_VALUE,
+                         [this](const pldm_msg* request, size_t payloadLength) {
+            return this->getNumericEffecterValue(request, payloadLength);
+        });
         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
                          [this](const pldm_msg* request, size_t payloadLength) {
             return this->setStateEffecterStates(request, payloadLength);
@@ -212,6 +216,15 @@
     Response setNumericEffecterValue(const pldm_msg* request,
                                      size_t payloadLength);
 
+    /** @brief Handler for getNumericEffecterValue
+     *
+     *  @param[in] request - Request message
+     *  @param[in] payloadLength - Request payload length
+     *  @return Response - PLDM Response message
+     */
+    Response getNumericEffecterValue(const pldm_msg* request,
+                                     size_t payloadLength);
+
     /** @brief Handler for getStateSensorReadings
      *
      *  @param[in] request - Request message
diff --git a/libpldmresponder/platform_numeric_effecter.hpp b/libpldmresponder/platform_numeric_effecter.hpp
index 46330e6..44b5aab 100644
--- a/libpldmresponder/platform_numeric_effecter.hpp
+++ b/libpldmresponder/platform_numeric_effecter.hpp
@@ -338,6 +338,236 @@
     return PLDM_SUCCESS;
 }
 
+/** @brief Function to convert the D-Bus value based on effecter data size
+ *         and create the response for getNumericEffecterValue request.
+ *  @param[in] PropertyValue - D-Bus Value
+ *  @param[in] effecterDataSize - effecter value size.
+ *  @param[in,out] responsePtr - Response of getNumericEffecterValue.
+ *  @param[in] responsePayloadLength - reponse length.
+ *  @param[in] instanceId - instance id for response
+ *
+ *  @return PLDM_SUCCESS/PLDM_ERROR
+ */
+template <typename T>
+int getEffecterValue(T propertyValue, uint8_t effecterDataSize,
+                     pldm_msg* responsePtr, size_t responsePayloadLength,
+                     uint8_t instanceId)
+{
+    switch (effecterDataSize)
+    {
+        case PLDM_EFFECTER_DATA_SIZE_UINT8:
+        {
+            uint8_t value = static_cast<uint8_t>(propertyValue);
+            return (encode_get_numeric_effecter_value_resp(
+                instanceId, PLDM_SUCCESS, effecterDataSize,
+                EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING, &value, &value,
+                responsePtr, responsePayloadLength));
+        }
+        case PLDM_EFFECTER_DATA_SIZE_SINT8:
+        {
+            int8_t value = static_cast<int8_t>(propertyValue);
+            return (encode_get_numeric_effecter_value_resp(
+                instanceId, PLDM_SUCCESS, effecterDataSize,
+                EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
+                reinterpret_cast<uint8_t*>(&value),
+                reinterpret_cast<uint8_t*>(&value), responsePtr,
+                responsePayloadLength));
+        }
+        case PLDM_EFFECTER_DATA_SIZE_UINT16:
+        {
+            uint16_t value = static_cast<uint16_t>(propertyValue);
+            return (encode_get_numeric_effecter_value_resp(
+                instanceId, PLDM_SUCCESS, effecterDataSize,
+                EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
+                reinterpret_cast<uint8_t*>(&value),
+                reinterpret_cast<uint8_t*>(&value), responsePtr,
+                responsePayloadLength));
+        }
+        case PLDM_EFFECTER_DATA_SIZE_SINT16:
+        {
+            int16_t value = static_cast<int16_t>(propertyValue);
+            return (encode_get_numeric_effecter_value_resp(
+                instanceId, PLDM_SUCCESS, effecterDataSize,
+                EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
+                reinterpret_cast<uint8_t*>(&value),
+                reinterpret_cast<uint8_t*>(&value), responsePtr,
+                responsePayloadLength));
+        }
+        case PLDM_EFFECTER_DATA_SIZE_UINT32:
+        {
+            uint32_t value = static_cast<uint32_t>(propertyValue);
+            return (encode_get_numeric_effecter_value_resp(
+                instanceId, PLDM_SUCCESS, effecterDataSize,
+                EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
+                reinterpret_cast<uint8_t*>(&value),
+                reinterpret_cast<uint8_t*>(&value), responsePtr,
+                responsePayloadLength));
+        }
+        case PLDM_EFFECTER_DATA_SIZE_SINT32:
+        {
+            int32_t value = static_cast<int32_t>(propertyValue);
+            return (encode_get_numeric_effecter_value_resp(
+                instanceId, PLDM_SUCCESS, effecterDataSize,
+                EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
+                reinterpret_cast<uint8_t*>(&value),
+                reinterpret_cast<uint8_t*>(&value), responsePtr,
+                responsePayloadLength));
+        }
+        default:
+        {
+            error("Unknown Effecter Size");
+            return PLDM_ERROR;
+        }
+    }
+}
+
+/** @brief Function to convert the D-Bus value to the effector data size value
+ *  @param[in] PropertyType - String contains the dataType of the Dbus value.
+ *  @param[in] PropertyValue - Variant contains the D-Bus Value
+ *  @param[in] effecterDataSize - effecter value size.
+ *  @param[in,out] responsePtr - Response of getNumericEffecterValue.
+ *  @param[in] responsePayloadLength - reponse length.
+ *  @param[in] instanceId - instance id for response
+ *
+ *  @return PLDM_SUCCESS/PLDM_ERROR
+ */
+int getNumericEffecterValueHandler(const std::string& propertyType,
+                                   pldm::utils::PropertyValue propertyValue,
+                                   uint8_t effecterDataSize,
+                                   pldm_msg* responsePtr,
+                                   size_t responsePayloadLength,
+                                   uint8_t instanceId)
+{
+    if (propertyType == "uint8_t")
+    {
+        uint8_t propVal = std::get<uint8_t>(propertyValue);
+        return getEffecterValue<uint8_t>(propVal, effecterDataSize, responsePtr,
+                                         responsePayloadLength, instanceId);
+    }
+    else if (propertyType == "uint16_t")
+    {
+        uint16_t propVal = std::get<uint16_t>(propertyValue);
+        return getEffecterValue<uint16_t>(propVal, effecterDataSize,
+                                          responsePtr, responsePayloadLength,
+                                          instanceId);
+    }
+    else if (propertyType == "uint32_t")
+    {
+        uint32_t propVal = std::get<uint32_t>(propertyValue);
+        return getEffecterValue<uint32_t>(propVal, effecterDataSize,
+                                          responsePtr, responsePayloadLength,
+                                          instanceId);
+    }
+    else if (propertyType == "uint64_t")
+    {
+        uint64_t propVal = std::get<uint64_t>(propertyValue);
+        return getEffecterValue<uint64_t>(propVal, effecterDataSize,
+                                          responsePtr, responsePayloadLength,
+                                          instanceId);
+    }
+    else
+    {
+        error("Property Type [{PROPERTYTYPE}] not supported", "PROPERTYTYPE",
+              propertyType);
+    }
+    return PLDM_ERROR;
+}
+
+/** @brief Function to get the effecter details as data size, D-Bus property
+ *         type, D-Bus Value
+ *  @tparam[in] DBusInterface - DBus interface type
+ *  @tparam[in] Handler - pldm::responder::platform::Handler
+ *  @param[in] dBusIntf - The interface object of DBusInterface
+ *  @param[in] handler - The interface object of
+ *             pldm::responder::platform::Handler
+ *  @param[in] effecterId - Effecter ID sent by the requester to act on
+ *  @param[in] effecterDataSize - The bit width and format of the setting
+ *             value for the effecter
+ *  @param[in] propertyType - The data type of the D-Bus value
+ *  @param[in] propertyValue - The value of numeric effecter being
+ *                             requested.
+ *  @return - Success or failure in getting the D-Bus property or the
+ *  effecterId not found in the PDR repo
+ */
+template <class DBusInterface, class Handler>
+int getNumericEffecterData(const DBusInterface& dBusIntf, Handler& handler,
+                           uint16_t effecterId, uint8_t& effecterDataSize,
+                           std::string& propertyType,
+                           pldm::utils::PropertyValue& propertyValue)
+{
+    pldm_numeric_effecter_value_pdr* pdr = nullptr;
+
+    std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
+        numericEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
+    pldm::responder::pdr_utils::Repo numericEffecterPDRs(
+        numericEffecterPdrRepo.get());
+    pldm::responder::pdr::getRepoByType(handler.getRepo(), numericEffecterPDRs,
+                                        PLDM_NUMERIC_EFFECTER_PDR);
+    if (numericEffecterPDRs.empty())
+    {
+        error("The Numeric Effecter PDR repo is empty.");
+        return PLDM_ERROR;
+    }
+
+    // Get the pdr structure of pldm_numeric_effecter_value_pdr according
+    // to the effecterId
+    pldm::responder::pdr_utils::PdrEntry pdrEntry{};
+    auto pdrRecord = numericEffecterPDRs.getFirstRecord(pdrEntry);
+
+    while (pdrRecord)
+    {
+        pdr = reinterpret_cast<pldm_numeric_effecter_value_pdr*>(pdrEntry.data);
+        if (pdr->effecter_id != effecterId)
+        {
+            pdr = nullptr;
+            pdrRecord = numericEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
+            continue;
+        }
+        effecterDataSize = pdr->effecter_data_size;
+        break;
+    }
+
+    if (!pdr)
+    {
+        error("The Numeric Effecter not found EFFECTERID={EFFECTERID}",
+              "EFFECTERID", effecterId);
+        return PLDM_PLATFORM_INVALID_EFFECTER_ID;
+    }
+
+    pldm::utils::DBusMapping dbusMapping{};
+    try
+    {
+        const auto& [dbusMappings,
+                     dbusValMaps] = handler.getDbusObjMaps(effecterId);
+        if (dbusMappings.size() > 0)
+        {
+            dbusMapping = {
+                dbusMappings[0].objectPath, dbusMappings[0].interface,
+                dbusMappings[0].propertyName, dbusMappings[0].propertyType};
+
+            propertyValue = dBusIntf.getDbusPropertyVariant(
+                dbusMapping.objectPath.c_str(),
+                dbusMapping.propertyName.c_str(),
+                dbusMapping.interface.c_str());
+            propertyType = dbusMappings[0].propertyType;
+        }
+    }
+    catch (const std::exception& e)
+    {
+        error(
+            "Dbus Mapping or the Dbus query for the Effecter failed for effecter id: {EFFECTER_ID}, {ERR_EXCEP}",
+            "EFFECTER_ID", effecterId, "ERR_EXCEP", e.what());
+        error(
+            "Dbus Details objPath : [{OBJ_PATH}] interface : [{INTF}], property : [{PROPERTY}]",
+            "OBJ_PATH", dbusMapping.objectPath.c_str(), "INTF",
+            dbusMapping.interface.c_str(), "PROPERTY",
+            dbusMapping.propertyName.c_str());
+        return PLDM_ERROR;
+    }
+
+    return PLDM_SUCCESS;
+}
+
 } // namespace platform_numeric_effecter
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/test/libpldmresponder_platform_test.cpp b/libpldmresponder/test/libpldmresponder_platform_test.cpp
index a646f9d..2d52987 100644
--- a/libpldmresponder/test/libpldmresponder_platform_test.cpp
+++ b/libpldmresponder/test/libpldmresponder_platform_test.cpp
@@ -395,6 +395,123 @@
     pldm_pdr_destroy(numericEffecterPdrRepo);
 }
 
+TEST(getNumericEffecterValueHandler, testGoodRequest)
+{
+    MockdBusHandler mockedUtils;
+    EXPECT_CALL(mockedUtils, getService(StrEq("/foo/bar"), _))
+        .Times(5)
+        .WillRepeatedly(Return("foo.bar"));
+
+    auto inPDRRepo = pldm_pdr_init();
+    auto numericEffecterPdrRepo = pldm_pdr_init();
+    Repo numericEffecterPDRs(numericEffecterPdrRepo);
+    auto event = sdeventplus::Event::get_default();
+    Handler handler(&mockedUtils, "./pdr_jsons/state_effecter/good", inPDRRepo,
+                    nullptr, nullptr, nullptr, nullptr, event);
+    Repo inRepo(inPDRRepo);
+    getRepoByType(inRepo, numericEffecterPDRs, PLDM_NUMERIC_EFFECTER_PDR);
+
+    pdr_utils::PdrEntry e;
+    auto record4 = pdr::getRecordByHandle(numericEffecterPDRs, 4, e);
+    ASSERT_NE(record4, nullptr);
+
+    pldm_numeric_effecter_value_pdr* pdr =
+        reinterpret_cast<pldm_numeric_effecter_value_pdr*>(e.data);
+    EXPECT_EQ(pdr->hdr.type, PLDM_NUMERIC_EFFECTER_PDR);
+
+    uint16_t effecterId = 3;
+
+    uint8_t effecterDataSize{};
+    pldm::utils::PropertyValue dbusValue;
+    std::string propertyType;
+
+    // effecterValue return the present numeric setting
+    uint32_t effecterValue = 2100000000;
+    using effecterOperationalState = uint8_t;
+    using completionCode = uint8_t;
+
+    EXPECT_CALL(mockedUtils,
+                getDbusPropertyVariant(StrEq("/foo/bar"), StrEq("propertyName"),
+                                       StrEq("xyz.openbmc_project.Foo.Bar")))
+        .WillOnce(Return(PropertyValue(static_cast<uint64_t>(effecterValue))));
+
+    auto rc = platform_numeric_effecter::getNumericEffecterData<MockdBusHandler,
+                                                                Handler>(
+        mockedUtils, handler, effecterId, effecterDataSize, propertyType,
+        dbusValue);
+
+    ASSERT_EQ(rc, 0);
+
+    size_t responsePayloadLength = sizeof(completionCode) +
+                                   sizeof(effecterDataSize) +
+                                   sizeof(effecterOperationalState) +
+                                   getEffecterDataSize(effecterDataSize) +
+                                   getEffecterDataSize(effecterDataSize);
+
+    Response response(responsePayloadLength + sizeof(pldm_msg_hdr));
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    rc = platform_numeric_effecter::getNumericEffecterValueHandler(
+        propertyType, dbusValue, effecterDataSize, responsePtr,
+        responsePayloadLength, 1);
+
+    ASSERT_EQ(rc, 0);
+
+    struct pldm_get_numeric_effecter_value_resp* resp =
+        reinterpret_cast<struct pldm_get_numeric_effecter_value_resp*>(
+            responsePtr->payload);
+    ASSERT_EQ(PLDM_SUCCESS, resp->completion_code);
+    uint32_t valPresent;
+    memcpy(&valPresent, &resp->pending_and_present_values[4],
+           sizeof(valPresent));
+
+    ASSERT_EQ(effecterValue, valPresent);
+
+    pldm_pdr_destroy(inPDRRepo);
+    pldm_pdr_destroy(numericEffecterPdrRepo);
+}
+
+TEST(getNumericEffecterValueHandler, testBadRequest)
+{
+    MockdBusHandler mockedUtils;
+    EXPECT_CALL(mockedUtils, getService(StrEq("/foo/bar"), _))
+        .Times(5)
+        .WillRepeatedly(Return("foo.bar"));
+
+    auto inPDRRepo = pldm_pdr_init();
+    auto numericEffecterPdrRepo = pldm_pdr_init();
+    Repo numericEffecterPDRs(numericEffecterPdrRepo);
+    auto event = sdeventplus::Event::get_default();
+    Handler handler(&mockedUtils, "./pdr_jsons/state_effecter/good", inPDRRepo,
+                    nullptr, nullptr, nullptr, nullptr, event);
+    Repo inRepo(inPDRRepo);
+    getRepoByType(inRepo, numericEffecterPDRs, PLDM_NUMERIC_EFFECTER_PDR);
+
+    pdr_utils::PdrEntry e;
+    auto record4 = pdr::getRecordByHandle(numericEffecterPDRs, 4, e);
+    ASSERT_NE(record4, nullptr);
+
+    pldm_numeric_effecter_value_pdr* pdr =
+        reinterpret_cast<pldm_numeric_effecter_value_pdr*>(e.data);
+    EXPECT_EQ(pdr->hdr.type, PLDM_NUMERIC_EFFECTER_PDR);
+
+    uint16_t effecterId = 4;
+
+    uint8_t effecterDataSize{};
+    pldm::utils::PropertyValue dbusValue;
+    std::string propertyType;
+
+    auto rc = platform_numeric_effecter::getNumericEffecterData<MockdBusHandler,
+                                                                Handler>(
+        mockedUtils, handler, effecterId, effecterDataSize, propertyType,
+        dbusValue);
+
+    ASSERT_EQ(rc, 128);
+
+    pldm_pdr_destroy(inPDRRepo);
+    pldm_pdr_destroy(numericEffecterPdrRepo);
+}
+
 TEST(parseStateSensor, allScenarios)
 {
     // Sample state sensor with SensorID - 1, EntityType - Processor Module(67)