Refactor buildFRUTable and update to Association tree

The previous logic is to obtain the entity of the parent by parsing
the DBus object path. If the obtaining fails it skipped the current
entity
type and continued to obtain the entity of the next parent.

In this commit the code is optimised to not skip the current entity
type.

The intent behind this commit is to refactor the buildFRUTable
method and their pldm entities need to be created in turn according
to the object path and update the association tree.

Tested:
There is one CPU, one DIMM, two powersupplies on my system
~# pldmtool platform getpdr -d 6
{
    "nextRecordHandle": 7,
    "responseCount": 26,
    "recordHandle": 6,
    "PDRHeaderVersion": 1,
    "PDRType": "Entity Association PDR",
    "recordChangeNumber": 0,
    "dataLength": 16,
    "containerID": 1,
    "associationType": "Physical",
    "containerEntityType": "System (logical)",
    "containerEntityInstanceNumber": 1,
    "containerEntityContainerID": 0,
    "containedEntityCount": 1,
    "containedEntityType[1]": "System chassis (main enclosure)",
    "containedEntityInstanceNumber[1]": 1,
    "containedEntityContainerID[1]": 1
}

~# pldmtool platform getpdr -d 7
{
    "nextRecordHandle": 8,
    "responseCount": 26,
    "recordHandle": 7,
    "PDRHeaderVersion": 1,
    "PDRType": "Entity Association PDR",
    "recordChangeNumber": 0,
    "dataLength": 16,
    "containerID": 2,
    "associationType": "Physical",
    "containerEntityType": "System chassis (main enclosure)",
    "containerEntityInstanceNumber": 1,
    "containerEntityContainerID": 1,
    "containedEntityCount": 1,
    "containedEntityType[1]": "System Board",
    "containedEntityInstanceNumber[1]": 1,
    "containedEntityContainerID[1]": 2
}

~# pldmtool platform getpdr -d 8
{
    "nextRecordHandle": 9,
    "responseCount": 44,
    "recordHandle": 8,
    "PDRHeaderVersion": 1,
    "PDRType": "Entity Association PDR",
    "recordChangeNumber": 0,
    "dataLength": 34,
    "containerID": 3,
    "associationType": "Physical",
    "containerEntityType": "System Board",
    "containerEntityInstanceNumber": 1,
    "containerEntityContainerID": 2,
    "containedEntityCount": 4,
    "containedEntityType[1]": "Processor",
    "containedEntityInstanceNumber[1]": 1,
    "containedEntityContainerID[1]": 3,
    "containedEntityType[2]": "Memory chip",
    "containedEntityInstanceNumber[2]": 1,
    "containedEntityContainerID[2]": 3,
    "containedEntityType[3]": "Power Supply",
    "containedEntityInstanceNumber[3]": 1,
    "containedEntityContainerID[3]": 3,
    "containedEntityType[4]": "Power Supply",
    "containedEntityInstanceNumber[4]": 2,
    "containedEntityContainerID[4]": 3
}

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: Ie375ea448d850450101c85fa818498db0b67cc1f
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index 69ca3cb..b135093 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -10,7 +10,9 @@
 #include <sdbusplus/bus.hpp>
 
 #include <iostream>
+#include <optional>
 #include <set>
+#include <stack>
 
 PHOSPHOR_LOG2_USING;
 
@@ -18,6 +20,123 @@
 {
 namespace responder
 {
+
+constexpr auto root = "/xyz/openbmc_project/inventory/";
+
+std::optional<pldm_entity>
+    FruImpl::getEntityByObjectPath(const dbus::InterfaceMap& intfMaps)
+{
+    for (const auto& intfMap : intfMaps)
+    {
+        try
+        {
+            pldm_entity entity{};
+            entity.entity_type = parser.getEntityType(intfMap.first);
+            return entity;
+        }
+        catch (const std::exception& e)
+        {
+            continue;
+        }
+    }
+
+    return std::nullopt;
+}
+
+void FruImpl::updateAssociationTree(const dbus::ObjectValueTree& objects,
+                                    const std::string& path)
+{
+    if (path.find(root) == std::string::npos)
+    {
+        return;
+    }
+
+    std::stack<std::string> tmpObjPaths{};
+    tmpObjPaths.emplace(path);
+
+    auto obj = pldm::utils::findParent(path);
+    while ((obj + '/') != root)
+    {
+        tmpObjPaths.emplace(obj);
+        obj = pldm::utils::findParent(obj);
+    }
+
+    std::stack<std::string> tmpObj = tmpObjPaths;
+    while (!tmpObj.empty())
+    {
+        std::string s = tmpObj.top();
+        std::cout << s << std::endl;
+        tmpObj.pop();
+    }
+    // Update pldm entity to assocition tree
+    std::string prePath = tmpObjPaths.top();
+    while (!tmpObjPaths.empty())
+    {
+        std::string currPath = tmpObjPaths.top();
+        tmpObjPaths.pop();
+
+        do
+        {
+            if (objToEntityNode.contains(currPath))
+            {
+                pldm_entity node =
+                    pldm_entity_extract(objToEntityNode.at(currPath));
+                if (pldm_entity_association_tree_find(entityTree, &node))
+                {
+                    break;
+                }
+            }
+            else
+            {
+                if (!objects.contains(currPath))
+                {
+                    break;
+                }
+
+                auto entityPtr = getEntityByObjectPath(objects.at(currPath));
+                if (!entityPtr)
+                {
+                    break;
+                }
+
+                pldm_entity entity = *entityPtr;
+
+                for (auto& it : objToEntityNode)
+                {
+                    pldm_entity node = pldm_entity_extract(it.second);
+                    if (node.entity_type == entity.entity_type)
+                    {
+                        entity.entity_instance_num = node.entity_instance_num +
+                                                     1;
+                        break;
+                    }
+                }
+
+                if (currPath == prePath)
+                {
+                    auto node = pldm_entity_association_tree_add(
+                        entityTree, &entity, 0xFFFF, nullptr,
+                        PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+                    objToEntityNode[currPath] = node;
+                }
+                else
+                {
+                    if (objToEntityNode.contains(prePath))
+                    {
+                        auto node = pldm_entity_association_tree_add(
+                            entityTree, &entity, 0xFFFF,
+                            objToEntityNode[prePath],
+                            PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+                        objToEntityNode[currPath] = node;
+                    }
+                }
+            }
+        } while (0);
+
+        prePath = currPath;
+    }
+}
+
 void FruImpl::buildFRUTable()
 {
     if (isBuilt)
@@ -69,35 +188,15 @@
                 // not have corresponding config jsons
                 try
                 {
+                    updateAssociationTree(objects, object.first.str);
                     pldm_entity entity{};
-                    entity.entity_type = parser.getEntityType(interface.first);
-                    pldm_entity_node* parent = nullptr;
-                    auto parentObj = pldm::utils::findParent(object.first.str);
-                    // To add a FRU to the entity association tree, we need to
-                    // determine if the FRU has a parent (D-Bus object). For eg
-                    // /system/backplane's parent is /system. /system has no
-                    // parent. Some D-Bus pathnames might just be namespaces
-                    // (not D-Bus objects), so we need to iterate upwards until
-                    // a parent is found, or we reach the root ("/").
-                    // Parents are always added first before children in the
-                    // entity association tree. We're relying on the fact that
-                    // the std::map containing object paths from the
-                    // GetManagedObjects call will have a sorted pathname list.
-                    do
+                    if (objToEntityNode.contains(object.first.str))
                     {
-                        auto iter = objToEntityNode.find(parentObj);
-                        if (iter != objToEntityNode.end())
-                        {
-                            parent = iter->second;
-                            break;
-                        }
-                        parentObj = pldm::utils::findParent(parentObj);
-                    } while (parentObj != "/");
+                        pldm_entity_node* node =
+                            objToEntityNode.at(object.first.str);
 
-                    auto node = pldm_entity_association_tree_add(
-                        entityTree, &entity, 0xFFFF, parent,
-                        PLDM_ENTITY_ASSOCIAION_PHYSICAL);
-                    objToEntityNode[object.first.str] = node;
+                        entity = pldm_entity_extract(node);
+                    }
 
                     auto recordInfos = parser.getRecordInfo(interface.first);
                     populateRecords(interfaces, recordInfos, entity);