pldm: Implement SetNumericEffecterValue function

Register the setNumericEffecterValue method and get the PDR structure
according to the EffecterId attribute.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I7eb56331813d7777a67d6c50f15b244f57e8492c
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
index df0f2d1..c0fff44 100644
--- a/libpldmresponder/platform.cpp
+++ b/libpldmresponder/platform.cpp
@@ -3,6 +3,7 @@
 
 #include "pdr_numeric_effecter.hpp"
 #include "pdr_state_effecter.hpp"
+#include "platform_numeric_effecter.hpp"
 #include "platform_state_effecter.hpp"
 #include "utils.hpp"
 
@@ -459,6 +460,38 @@
     return PLDM_SUCCESS;
 }
 
+Response Handler::setNumericEffecterValue(const pldm_msg* request,
+                                          size_t payloadLength)
+{
+    Response response(sizeof(pldm_msg_hdr) +
+                      PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES);
+    uint16_t effecterId{};
+    uint8_t effecterDataSize{};
+    uint8_t effecterValue[4] = {};
+
+    if ((payloadLength > sizeof(effecterId) + sizeof(effecterDataSize) +
+                             sizeof(union_effecter_data_size)) ||
+        (payloadLength < sizeof(effecterId) + sizeof(effecterDataSize) + 1))
+    {
+        return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
+    }
+
+    int rc = decode_set_numeric_effecter_value_req(
+        request, payloadLength, &effecterId, &effecterDataSize,
+        reinterpret_cast<uint8_t*>(&effecterValue));
+
+    if (rc == PLDM_SUCCESS)
+    {
+        const pldm::utils::DBusHandler dBusIntf;
+        rc = platform_numeric_effecter::setNumericEffecterValueHandler<
+            pldm::utils::DBusHandler, Handler>(dBusIntf, *this, effecterId,
+                                               effecterDataSize, effecterValue,
+                                               sizeof(effecterValue));
+    }
+
+    return ccOnlyResponse(request, rc);
+}
+
 } // namespace platform
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 94c7c14..1982031 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -65,6 +65,11 @@
                          [this](const pldm_msg* request, size_t payloadLength) {
                              return this->getPDR(request, payloadLength);
                          });
+        handlers.emplace(PLDM_SET_NUMERIC_EFFECTER_VALUE,
+                         [this](const pldm_msg* request, size_t payloadLength) {
+                             return this->setNumericEffecterValue(
+                                 request, payloadLength);
+                         });
         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
                          [this](const pldm_msg* request, size_t payloadLength) {
                              return this->setStateEffecterStates(request,
@@ -174,6 +179,15 @@
      */
     Response getPDR(const pldm_msg* request, size_t payloadLength);
 
+    /** @brief Handler for setNumericEffecterValue
+     *
+     *  @param[in] request - Request message
+     *  @param[in] payloadLength - Request payload length
+     *  @return Response - PLDM Response message
+     */
+    Response setNumericEffecterValue(const pldm_msg* request,
+                                     size_t payloadLength);
+
     /** @brief Handler for setStateEffecterStates
      *
      *  @param[in] request - Request message
diff --git a/libpldmresponder/platform_numeric_effecter.hpp b/libpldmresponder/platform_numeric_effecter.hpp
new file mode 100644
index 0000000..b5d8ef5
--- /dev/null
+++ b/libpldmresponder/platform_numeric_effecter.hpp
@@ -0,0 +1,277 @@
+#pragma once
+
+#include "config.h"
+
+#include "handler.hpp"
+#include "libpldmresponder/pdr.hpp"
+#include "pdr_utils.hpp"
+#include "utils.hpp"
+
+#include <math.h>
+#include <stdint.h>
+
+#include <map>
+#include <optional>
+
+#include "libpldm/platform.h"
+#include "libpldm/states.h"
+
+using namespace pldm::responder::pdr;
+using namespace pldm::utils;
+
+namespace pldm
+{
+namespace responder
+{
+namespace platform_numeric_effecter
+{
+
+/** @brief Function to get the effecter value by PDR factor coefficient, etc.
+ *  @param[in] pdr - The structure of pldm_numeric_effecter_value_pdr.
+ *  @tparam[in] effecterValue - effecter value.
+ *
+ *  @return - std::pair<int, std::optional<PropertyValue>> - rc:Success or
+ *          failure, PropertyValue: The value to be set
+ */
+template <typename T>
+std::pair<int, std::optional<PropertyValue>>
+    getEffecterRawValue(const pldm_numeric_effecter_value_pdr* pdr,
+                        T& effecterValue)
+{
+    // X = Round [ (Y - B) / m ]
+    // refer to DSP0248_1.2.0 27.8
+    int rc = 0;
+    PropertyValue value;
+    switch (pdr->effecter_data_size)
+    {
+        case PLDM_EFFECTER_DATA_SIZE_UINT8:
+        {
+            auto rawValue = static_cast<uint8_t>(
+                round(effecterValue - pdr->offset) / pdr->resolution);
+            if (pdr->min_set_table.value_u8 < pdr->max_set_table.value_u8 &&
+                (rawValue < pdr->min_set_table.value_u8 ||
+                 rawValue > pdr->max_set_table.value_u8))
+            {
+                rc = PLDM_ERROR_INVALID_DATA;
+            }
+            value = rawValue;
+            break;
+        }
+        case PLDM_EFFECTER_DATA_SIZE_SINT8:
+        {
+            auto rawValue = static_cast<int8_t>(
+                round(effecterValue - pdr->offset) / pdr->resolution);
+            if (pdr->min_set_table.value_s8 < pdr->max_set_table.value_s8 &&
+                (rawValue < pdr->min_set_table.value_s8 ||
+                 rawValue > pdr->max_set_table.value_s8))
+            {
+                rc = PLDM_ERROR_INVALID_DATA;
+            }
+            value = rawValue;
+            break;
+        }
+        case PLDM_EFFECTER_DATA_SIZE_UINT16:
+        {
+            auto rawValue = static_cast<uint16_t>(
+                round(effecterValue - pdr->offset) / pdr->resolution);
+            if (pdr->min_set_table.value_u16 < pdr->max_set_table.value_u16 &&
+                (rawValue < pdr->min_set_table.value_u16 ||
+                 rawValue > pdr->max_set_table.value_u16))
+            {
+                rc = PLDM_ERROR_INVALID_DATA;
+            }
+            value = rawValue;
+            break;
+        }
+        case PLDM_EFFECTER_DATA_SIZE_SINT16:
+        {
+            auto rawValue = static_cast<int16_t>(
+                round(effecterValue - pdr->offset) / pdr->resolution);
+            if (pdr->min_set_table.value_s16 < pdr->max_set_table.value_s16 &&
+                (rawValue < pdr->min_set_table.value_s16 ||
+                 rawValue > pdr->max_set_table.value_s16))
+            {
+                rc = PLDM_ERROR_INVALID_DATA;
+            }
+            value = rawValue;
+            break;
+        }
+        case PLDM_EFFECTER_DATA_SIZE_UINT32:
+        {
+            auto rawValue = static_cast<uint32_t>(
+                round(effecterValue - pdr->offset) / pdr->resolution);
+            if (pdr->min_set_table.value_u32 < pdr->max_set_table.value_u32 &&
+                (rawValue < pdr->min_set_table.value_u32 ||
+                 rawValue > pdr->max_set_table.value_u32))
+            {
+                rc = PLDM_ERROR_INVALID_DATA;
+            }
+            value = rawValue;
+            break;
+        }
+        case PLDM_EFFECTER_DATA_SIZE_SINT32:
+        {
+            auto rawValue = static_cast<int32_t>(
+                round(effecterValue - pdr->offset) / pdr->resolution);
+            if (pdr->min_set_table.value_s32 < pdr->max_set_table.value_s32 &&
+                (rawValue < pdr->min_set_table.value_s32 ||
+                 rawValue > pdr->max_set_table.value_s32))
+            {
+                rc = PLDM_ERROR_INVALID_DATA;
+            }
+            value = rawValue;
+            break;
+        }
+    }
+
+    return {rc, std::make_optional(std::move(value))};
+}
+
+/** @brief Function to convert the D-Bus value by PDR factor and effecter value.
+ *  @param[in] pdr - The structure of pldm_numeric_effecter_value_pdr.
+ *  @param[in] effecterDataSize - effecter value size.
+ *  @param[in,out] effecterValue - effecter value.
+ *
+ *  @return std::pair<int, std::optional<PropertyValue>> - rc:Success or
+ *          failure, PropertyValue: The value to be set
+ */
+std::pair<int, std::optional<PropertyValue>>
+    convertToDbusValue(const pldm_numeric_effecter_value_pdr* pdr,
+                       uint8_t effecterDataSize, uint8_t* effecterValue)
+{
+    if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT8)
+    {
+        uint8_t currentValue = *(reinterpret_cast<uint8_t*>(&effecterValue[0]));
+        return getEffecterRawValue<uint8_t>(pdr, currentValue);
+    }
+    else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT8)
+    {
+        int8_t currentValue = *(reinterpret_cast<int8_t*>(&effecterValue[0]));
+        return getEffecterRawValue<int8_t>(pdr, currentValue);
+    }
+    else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT16)
+    {
+        uint16_t currentValue =
+            *(reinterpret_cast<uint16_t*>(&effecterValue[0]));
+        return getEffecterRawValue<uint16_t>(pdr, currentValue);
+    }
+    else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT16)
+    {
+        int16_t currentValue = *(reinterpret_cast<int16_t*>(&effecterValue[0]));
+        return getEffecterRawValue<int16_t>(pdr, currentValue);
+    }
+    else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT32)
+    {
+        uint32_t currentValue =
+            *(reinterpret_cast<uint32_t*>(&effecterValue[0]));
+        return getEffecterRawValue<uint32_t>(pdr, currentValue);
+    }
+    else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT32)
+    {
+        int32_t currentValue = *(reinterpret_cast<int32_t*>(&effecterValue[0]));
+        return getEffecterRawValue<int32_t>(pdr, currentValue);
+    }
+    else
+    {
+        std::cerr << "Wrong field effecterDataSize...\n";
+        return {PLDM_ERROR, {}};
+    }
+}
+
+/** @brief Function to set the effecter value requested by pldm requester
+ *  @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] effecter_value - The setting value of numeric effecter being
+ * 				requested.
+ *  @param[in] effecterValueLength - The setting value length of numeric
+ *              effecter being requested.
+ *  @return - Success or failure in setting the states. Returns failure in
+ * terms of PLDM completion codes if atleast one state fails to be set
+ */
+template <class DBusInterface, class Handler>
+int setNumericEffecterValueHandler(const DBusInterface& dBusIntf,
+                                   Handler& handler, uint16_t effecterId,
+                                   uint8_t effecterDataSize,
+                                   uint8_t* effecterValue,
+                                   size_t effecterValueLength)
+{
+    constexpr auto effecterValueArrayLength = 4;
+    pldm_numeric_effecter_value_pdr* pdr = nullptr;
+
+    std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
+        numericEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
+    Repo numericEffecterPDRs(numericEffecterPdrRepo.get());
+    getRepoByType(handler.getRepo(), numericEffecterPDRs,
+                  PLDM_NUMERIC_EFFECTER_PDR);
+    if (numericEffecterPDRs.empty())
+    {
+        std::cerr << "The Numeric Effecter PDR repo is empty." << std::endl;
+        return PLDM_ERROR;
+    }
+
+    // Get the pdr structure of pldm_numeric_effecter_value_pdr according
+    // to the effecterId
+    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;
+        }
+
+        break;
+    }
+
+    if (!pdr)
+    {
+        return PLDM_PLATFORM_INVALID_EFFECTER_ID;
+    }
+
+    if (effecterValueLength != effecterValueArrayLength)
+    {
+        std::cerr << "effecter data size is incorrect.\n";
+        return PLDM_ERROR_INVALID_DATA;
+    }
+
+    // convert to dbus effectervalue according to the factor
+    auto [rc, dbusValue] =
+        convertToDbusValue(pdr, effecterDataSize, effecterValue);
+    if (rc != PLDM_SUCCESS)
+    {
+        return rc;
+    }
+
+    const auto& [dbusMappings, dbusValMaps] =
+        handler.getDbusObjMaps(effecterId);
+    DBusMapping dbusMapping{
+        dbusMappings[0].objectPath, dbusMappings[0].interface,
+        dbusMappings[0].propertyName, dbusMappings[0].propertyType};
+    try
+    {
+        dBusIntf.setDbusProperty(dbusMapping, dbusValue.value());
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Error setting property, ERROR=" << e.what()
+                  << " PROPERTY=" << dbusMapping.propertyName << " INTERFACE="
+                  << dbusMapping.interface << " PATH=" << dbusMapping.objectPath
+                  << "\n";
+        return PLDM_ERROR;
+    }
+
+    return PLDM_SUCCESS;
+}
+
+} // namespace platform_numeric_effecter
+} // namespace responder
+} // namespace pldm