Implement the FRU implementation class

FRU implementation class implements the logic to build the PLDM FRU table
from the inventory D-Bus objects. This class exposes APIs to read the
FRU table, number of FRU records and the checksum of the table. This will
be consumed by the GetFRURecordTableMetadata command and GetFRURecordTable
command specified in the DMTF specification DSP0257.

Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I3212f59e7e972dc66f57a507ba87b707281ba0b9
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
new file mode 100644
index 0000000..096c4f7
--- /dev/null
+++ b/libpldmresponder/fru.cpp
@@ -0,0 +1,177 @@
+#include "fru.hpp"
+
+#include "utils.hpp"
+
+#include <systemd/sd-journal.h>
+
+#include <boost/crc.hpp>
+#include <iostream>
+#include <sdbusplus/bus.hpp>
+#include <set>
+
+namespace pldm
+{
+
+namespace responder
+{
+
+FruImpl::FruImpl(const std::string& configPath)
+{
+    fru_parser::FruParser handle(configPath);
+
+    auto dbusInfo = handle.inventoryLookup();
+
+    // Read the all the inventory D-Bus objects
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    dbus::ObjectValueTree objects;
+
+    try
+    {
+        auto method = bus.new_method_call(
+            std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
+            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+        auto reply = bus.call(method);
+        reply.read(objects);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Look up of inventory objects failed and PLDM FRU table "
+                     "creation failed";
+        return;
+    }
+
+    // Populate all the interested Item types to a map for easy lookup
+    std::set<dbus::Interface> itemIntfsLookup;
+    auto itemIntfs = std::get<2>(dbusInfo);
+    std::transform(std::begin(itemIntfs), std::end(itemIntfs),
+                   std::inserter(itemIntfsLookup, itemIntfsLookup.end()),
+                   [](dbus::Interface intf) { return intf; });
+
+    for (const auto& object : objects)
+    {
+        const auto& interfaces = object.second;
+
+        for (const auto& interface : interfaces)
+        {
+            if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end())
+            {
+                // An exception will be thrown by getRecordInfo, if the item
+                // D-Bus interface name specified in FRU_Master.json does
+                // not have corresponding config jsons
+                try
+                {
+                    auto recordInfos = handle.getRecordInfo(interface.first);
+                    populateRecords(interfaces, recordInfos);
+                }
+                catch (const std::exception& e)
+                {
+                    std::cout << "Config JSONs missing for the item "
+                                 "interface type, interface = "
+                              << interface.first << "\n";
+                    break;
+                }
+            }
+        }
+    }
+
+    if (table.size())
+    {
+        padBytes = utils::getNumPadBytes(table.size());
+        table.resize(table.size() + padBytes, 0);
+
+        // Calculate the checksum
+        boost::crc_32_type result;
+        result.process_bytes(table.data(), table.size());
+        checksum = result.checksum();
+    }
+}
+
+void FruImpl::populateRecords(
+    const pldm::responder::dbus::InterfaceMap& interfaces,
+    const fru_parser::FruRecordInfos& recordInfos)
+{
+    // recordSetIdentifier for the FRU will be set when the first record gets
+    // added for the FRU
+    uint16_t recordSetIdentifier = 0;
+    auto numRecsCount = numRecs;
+
+    for (auto const& [recType, encType, fieldInfos] : recordInfos)
+    {
+        std::vector<uint8_t> tlvs;
+        uint8_t numFRUFields = 0;
+        for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos)
+        {
+            try
+            {
+                auto propValue = interfaces.at(intf).at(prop);
+                if (propType == "bytearray")
+                {
+                    auto byteArray = std::get<std::vector<uint8_t>>(propValue);
+                    if (!byteArray.size())
+                    {
+                        continue;
+                    }
+
+                    numFRUFields++;
+                    tlvs.emplace_back(fieldTypeNum);
+                    tlvs.emplace_back(byteArray.size());
+                    std::move(std::begin(byteArray), std::end(byteArray),
+                              std::back_inserter(tlvs));
+                }
+                else if (propType == "string")
+                {
+                    auto str = std::get<std::string>(propValue);
+                    if (!str.size())
+                    {
+                        continue;
+                    }
+
+                    numFRUFields++;
+                    tlvs.emplace_back(fieldTypeNum);
+                    tlvs.emplace_back(str.size());
+                    std::move(std::begin(str), std::end(str),
+                              std::back_inserter(tlvs));
+                }
+            }
+            catch (const std::out_of_range& e)
+            {
+                std::cout << "Interface = " << intf
+                          << " , and Property = " << prop
+                          << " , in config json not present in D-Bus"
+                          << "\n";
+                continue;
+            }
+        }
+
+        if (tlvs.size())
+        {
+            if (numRecs == numRecsCount)
+            {
+                recordSetIdentifier = nextRSI();
+            }
+            auto curSize = table.size();
+            table.resize(curSize + recHeaderSize + tlvs.size());
+            encode_fru_record(table.data(), table.size(), &curSize,
+                              recordSetIdentifier, recType, numFRUFields,
+                              encType, tlvs.data(), tlvs.size());
+            numRecs++;
+        }
+    }
+}
+
+void FruImpl::getFRUTable(Response& response)
+{
+    auto hdrSize = response.size();
+
+    response.resize(hdrSize + table.size() + sizeof(checksum), 0);
+    std::copy(table.begin(), table.end(), response.begin() + hdrSize);
+
+    // Copy the checksum to response data
+    auto iter = response.begin() + hdrSize + table.size();
+    std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
+                iter);
+}
+
+} // namespace responder
+
+} // namespace pldm