1 #include "topology.hpp" 2 3 #include "phosphor-logging/lg2.hpp" 4 5 void Topology::addBoard(const std::string& path, const std::string& boardType, 6 const std::string& boardName, 7 const nlohmann::json& exposesItem) 8 { 9 auto findType = exposesItem.find("Type"); 10 if (findType == exposesItem.end()) 11 { 12 return; 13 } 14 15 boardNames.try_emplace(boardName, path); 16 17 PortType exposesType = findType->get<std::string>(); 18 19 if (exposesType == "DownstreamPort") 20 { 21 addDownstreamPort(path, exposesItem); 22 } 23 else if (exposesType.ends_with("Port")) 24 { 25 upstreamPorts[exposesType].insert(path); 26 } 27 else 28 { 29 return; 30 } 31 boardTypes[path] = boardType; 32 } 33 34 void Topology::addDownstreamPort(const Path& path, 35 const nlohmann::json& exposesItem) 36 { 37 auto findConnectsTo = exposesItem.find("ConnectsToType"); 38 if (findConnectsTo == exposesItem.end()) 39 { 40 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH", 41 path); 42 return; 43 } 44 PortType connectsTo = findConnectsTo->get<std::string>(); 45 46 downstreamPorts[connectsTo].insert(path); 47 auto findPoweredBy = exposesItem.find("PowerPort"); 48 if (findPoweredBy != exposesItem.end()) 49 { 50 powerPaths.insert(path); 51 } 52 } 53 54 std::unordered_map<std::string, std::set<Association>> Topology::getAssocs( 55 BoardPathsView boardPaths) 56 { 57 std::unordered_map<std::string, std::set<Association>> result; 58 59 // look at each upstream port type 60 for (const auto& upstreamPortPair : upstreamPorts) 61 { 62 auto downstreamMatch = downstreamPorts.find(upstreamPortPair.first); 63 64 if (downstreamMatch == downstreamPorts.end()) 65 { 66 // no match 67 continue; 68 } 69 70 fillAssocsForPortId(result, boardPaths, upstreamPortPair.second, 71 downstreamMatch->second); 72 } 73 return result; 74 } 75 76 void Topology::fillAssocsForPortId( 77 std::unordered_map<std::string, std::set<Association>>& result, 78 BoardPathsView boardPaths, const std::set<Path>& upstreamPaths, 79 const std::set<Path>& downstreamPaths) 80 { 81 for (const Path& upstream : upstreamPaths) 82 { 83 for (const Path& downstream : downstreamPaths) 84 { 85 fillAssocForPortId(result, boardPaths, upstream, downstream); 86 } 87 } 88 } 89 90 void Topology::fillAssocForPortId( 91 std::unordered_map<std::string, std::set<Association>>& result, 92 BoardPathsView boardPaths, const Path& upstream, const Path& downstream) 93 { 94 if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board") 95 { 96 return; 97 } 98 // The downstream path must be one we care about. 99 if (!std::ranges::contains(boardPaths, downstream)) 100 { 101 return; 102 } 103 104 std::string assoc = "contained_by"; 105 std::optional<std::string> opposite = getOppositeAssoc(assoc); 106 107 if (!opposite.has_value()) 108 { 109 return; 110 } 111 112 result[downstream].insert({assoc, opposite.value(), upstream}); 113 114 if (powerPaths.contains(downstream)) 115 { 116 assoc = "powering"; 117 opposite = getOppositeAssoc(assoc); 118 if (!opposite.has_value()) 119 { 120 return; 121 } 122 123 result[downstream].insert({assoc, opposite.value(), upstream}); 124 } 125 } 126 127 const std::set<std::pair<std::string, std::string>> assocs = { 128 {"powering", "powered_by"}, {"containing", "contained_by"}, 129 // ... extend as needed 130 }; 131 132 std::optional<std::string> Topology::getOppositeAssoc( 133 const AssocName& assocName) 134 { 135 for (const auto& entry : assocs) 136 { 137 if (entry.first == assocName) 138 { 139 return entry.second; 140 } 141 if (entry.second == assocName) 142 { 143 return entry.first; 144 } 145 } 146 147 return std::nullopt; 148 } 149 150 void Topology::remove(const std::string& boardName) 151 { 152 // Remove the board from boardNames, and then using the path 153 // found in boardNames remove it from upstreamPorts and 154 // downstreamPorts. 155 auto boardFind = boardNames.find(boardName); 156 if (boardFind == boardNames.end()) 157 { 158 return; 159 } 160 161 std::string boardPath = boardFind->second; 162 163 boardNames.erase(boardFind); 164 165 for (auto& upstreamPort : upstreamPorts) 166 { 167 upstreamPort.second.erase(boardPath); 168 } 169 170 for (auto& downstreamPort : downstreamPorts) 171 { 172 downstreamPort.second.erase(boardPath); 173 } 174 } 175