#include "topology.hpp" #include "phosphor-logging/lg2.hpp" void Topology::addBoard(const std::string& path, const std::string& boardType, const std::string& boardName, const nlohmann::json& exposesItem) { auto findType = exposesItem.find("Type"); if (findType == exposesItem.end()) { return; } boardNames.try_emplace(boardName, path); PortType exposesType = findType->get(); if (exposesType == "DownstreamPort") { addDownstreamPort(path, exposesItem); } else if (exposesType.ends_with("Port")) { upstreamPorts[exposesType].insert(path); } else { return; } boardTypes[path] = boardType; } void Topology::addDownstreamPort(const Path& path, const nlohmann::json& exposesItem) { auto findConnectsTo = exposesItem.find("ConnectsToType"); if (findConnectsTo == exposesItem.end()) { lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH", path); return; } PortType connectsTo = findConnectsTo->get(); downstreamPorts[connectsTo].insert(path); auto findPoweredBy = exposesItem.find("PowerPort"); if (findPoweredBy != exposesItem.end()) { powerPaths.insert(path); } } std::unordered_map> Topology::getAssocs( BoardPathsView boardPaths) { std::unordered_map> result; // look at each upstream port type for (const auto& upstreamPortPair : upstreamPorts) { auto downstreamMatch = downstreamPorts.find(upstreamPortPair.first); if (downstreamMatch == downstreamPorts.end()) { // no match continue; } fillAssocsForPortId(result, boardPaths, upstreamPortPair.second, downstreamMatch->second); } return result; } void Topology::fillAssocsForPortId( std::unordered_map>& result, BoardPathsView boardPaths, const std::set& upstreamPaths, const std::set& downstreamPaths) { for (const Path& upstream : upstreamPaths) { for (const Path& downstream : downstreamPaths) { fillAssocForPortId(result, boardPaths, upstream, downstream); } } } void Topology::fillAssocForPortId( std::unordered_map>& result, BoardPathsView boardPaths, const Path& upstream, const Path& downstream) { if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board") { return; } // The downstream path must be one we care about. if (!std::ranges::contains(boardPaths, downstream)) { return; } std::string assoc = "contained_by"; std::optional opposite = getOppositeAssoc(assoc); if (!opposite.has_value()) { return; } result[downstream].insert({assoc, opposite.value(), upstream}); if (powerPaths.contains(downstream)) { assoc = "powering"; opposite = getOppositeAssoc(assoc); if (!opposite.has_value()) { return; } result[downstream].insert({assoc, opposite.value(), upstream}); } } const std::set> assocs = { {"powering", "powered_by"}, {"containing", "contained_by"}, // ... extend as needed }; std::optional Topology::getOppositeAssoc( const AssocName& assocName) { for (const auto& entry : assocs) { if (entry.first == assocName) { return entry.second; } if (entry.second == assocName) { return entry.first; } } return std::nullopt; } void Topology::remove(const std::string& boardName) { // Remove the board from boardNames, and then using the path // found in boardNames remove it from upstreamPorts and // downstreamPorts. auto boardFind = boardNames.find(boardName); if (boardFind == boardNames.end()) { return; } std::string boardPath = boardFind->second; boardNames.erase(boardFind); for (auto& upstreamPort : upstreamPorts) { upstreamPort.second.erase(boardPath); } for (auto& downstreamPort : downstreamPorts) { downstreamPort.second.erase(boardPath); } }