| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 1 | #include "topology.hpp" | 
|  | 2 |  | 
| Alexander Hansen | 315929a | 2025-08-19 17:34:10 +0200 | [diff] [blame] | 3 | #include "phosphor-logging/lg2.hpp" | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 4 |  | 
|  | 5 | void Topology::addBoard(const std::string& path, const std::string& boardType, | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 6 | const std::string& boardName, | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 7 | const nlohmann::json& exposesItem) | 
|  | 8 | { | 
|  | 9 | auto findType = exposesItem.find("Type"); | 
|  | 10 | if (findType == exposesItem.end()) | 
|  | 11 | { | 
|  | 12 | return; | 
|  | 13 | } | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 14 |  | 
|  | 15 | boardNames.try_emplace(boardName, path); | 
|  | 16 |  | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 17 | PortType exposesType = findType->get<std::string>(); | 
|  | 18 |  | 
|  | 19 | if (exposesType == "DownstreamPort") | 
|  | 20 | { | 
| Alexander Hansen | ec938ad | 2025-08-19 16:48:44 +0200 | [diff] [blame] | 21 | addDownstreamPort(path, exposesItem); | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 22 | } | 
|  | 23 | else if (exposesType.ends_with("Port")) | 
|  | 24 | { | 
| Alexander Hansen | 7cfc043 | 2025-08-20 14:43:04 +0200 | [diff] [blame] | 25 | upstreamPorts[exposesType].insert(path); | 
| Alexander Hansen | ec938ad | 2025-08-19 16:48:44 +0200 | [diff] [blame] | 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 | { | 
| Alexander Hansen | 315929a | 2025-08-19 17:34:10 +0200 | [diff] [blame] | 40 | lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH", | 
|  | 41 | path); | 
| Alexander Hansen | ec938ad | 2025-08-19 16:48:44 +0200 | [diff] [blame] | 42 | return; | 
|  | 43 | } | 
|  | 44 | PortType connectsTo = findConnectsTo->get<std::string>(); | 
|  | 45 |  | 
| Alexander Hansen | 7cfc043 | 2025-08-20 14:43:04 +0200 | [diff] [blame] | 46 | downstreamPorts[connectsTo].insert(path); | 
| Alexander Hansen | ec938ad | 2025-08-19 16:48:44 +0200 | [diff] [blame] | 47 | auto findPoweredBy = exposesItem.find("PowerPort"); | 
|  | 48 | if (findPoweredBy != exposesItem.end()) | 
|  | 49 | { | 
|  | 50 | powerPaths.insert(path); | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 51 | } | 
|  | 52 | } | 
|  | 53 |  | 
| Alexander Hansen | 0519f57 | 2025-08-19 18:11:04 +0200 | [diff] [blame] | 54 | std::unordered_map<std::string, std::set<Association>> Topology::getAssocs( | 
| Alexander Hansen | 32e7418 | 2025-08-20 16:16:00 +0200 | [diff] [blame] | 55 | BoardPathsView boardPaths) | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 56 | { | 
| Alexander Hansen | 0519f57 | 2025-08-19 18:11:04 +0200 | [diff] [blame] | 57 | std::unordered_map<std::string, std::set<Association>> result; | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 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 |  | 
| Alexander Hansen | d7be0ee | 2025-08-20 14:16:15 +0200 | [diff] [blame] | 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 | { | 
| Alexander Hansen | 97a1c93 | 2025-08-20 16:28:52 +0200 | [diff] [blame] | 83 | for (const Path& downstream : downstreamPaths) | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 84 | { | 
| Alexander Hansen | 97a1c93 | 2025-08-20 16:28:52 +0200 | [diff] [blame] | 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 | { | 
| Alexander Hansen | 5036d94 | 2025-08-20 16:38:59 +0200 | [diff] [blame] | 94 | if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board") | 
| Alexander Hansen | 97a1c93 | 2025-08-20 16:28:52 +0200 | [diff] [blame] | 95 | { | 
| Alexander Hansen | 5036d94 | 2025-08-20 16:38:59 +0200 | [diff] [blame] | 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 |  | 
| Alexander Hansen | ca121f5 | 2025-08-21 18:25:19 +0200 | [diff] [blame] | 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}); | 
| Alexander Hansen | 5036d94 | 2025-08-20 16:38:59 +0200 | [diff] [blame] | 113 |  | 
|  | 114 | if (powerPaths.contains(downstream)) | 
|  | 115 | { | 
| Alexander Hansen | ca121f5 | 2025-08-21 18:25:19 +0200 | [diff] [blame] | 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}); | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 124 | } | 
| Benjamin Fair | f2f5b7a | 2022-09-09 19:45:02 +0000 | [diff] [blame] | 125 | } | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 126 |  | 
| Alexander Hansen | ca121f5 | 2025-08-21 18:25:19 +0200 | [diff] [blame] | 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 |  | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 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 |  | 
| Alexander Hansen | f3bb8d9 | 2025-08-19 16:21:43 +0200 | [diff] [blame] | 165 | for (auto& upstreamPort : upstreamPorts) | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 166 | { | 
| Alexander Hansen | 7cfc043 | 2025-08-20 14:43:04 +0200 | [diff] [blame] | 167 | upstreamPort.second.erase(boardPath); | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 168 | } | 
|  | 169 |  | 
| Alexander Hansen | f3bb8d9 | 2025-08-19 16:21:43 +0200 | [diff] [blame] | 170 | for (auto& downstreamPort : downstreamPorts) | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 171 | { | 
| Alexander Hansen | 7cfc043 | 2025-08-20 14:43:04 +0200 | [diff] [blame] | 172 | downstreamPort.second.erase(boardPath); | 
| Matt Spinler | 6eb6097 | 2023-08-14 16:36:20 -0500 | [diff] [blame] | 173 | } | 
|  | 174 | } |