|  | #pragma once | 
|  |  | 
|  | #include "async_resp.hpp" | 
|  | #include "dbus_utility.hpp" | 
|  | #include "error_messages.hpp" | 
|  | #include "managed_store.hpp" | 
|  | #include "utility.hpp" | 
|  |  | 
|  | #include <array> | 
|  | #include <string_view> | 
|  | #include <unordered_map> | 
|  |  | 
|  | #ifdef UNIT_TEST_BUILD | 
|  | #include "test/g3/mock_managed_store.hpp" // NOLINT | 
|  | #endif | 
|  |  | 
|  | namespace redfish | 
|  | { | 
|  |  | 
|  | namespace fan_utils | 
|  | { | 
|  |  | 
|  | constexpr std::array<std::string_view, 1> fanInterfaces = { | 
|  | "xyz.openbmc_project.Configuration.Fan"}; | 
|  |  | 
|  | inline void fillFanProperties( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& objectPath, | 
|  | const dbus::utility::DBusPropertiesMap& properties, | 
|  | const std::shared_ptr<std::unordered_map<std::string, std::string>>& | 
|  | fanSensorToInventoryPath) | 
|  | { | 
|  | for (const auto& [propKey, propVariant] : properties) | 
|  | { | 
|  | if (propKey == "TachSensor") | 
|  | { | 
|  | const std::string* tachSensor = | 
|  | std::get_if<std::string>(&propVariant); | 
|  | if (tachSensor == nullptr) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | (*fanSensorToInventoryPath)[*tachSensor] = objectPath; | 
|  | } | 
|  | else if (propKey == "PWMSensor") | 
|  | { | 
|  | const std::string* pwmSensor = | 
|  | std::get_if<std::string>(&propVariant); | 
|  | if (pwmSensor == nullptr) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | (*fanSensorToInventoryPath)[*pwmSensor] = objectPath; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename Callback> | 
|  | inline void getFanInventory( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::shared_ptr<std::unordered_map<std::string, std::string>>& | 
|  | fanSensorToInventoryPath, | 
|  | Callback callback) | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTree( | 
|  | "/xyz/openbmc_project/inventory", 0, fanInterfaces, requestContext, | 
|  | [asyncResp, fanSensorToInventoryPath, requestContext, | 
|  | callback{callback}]( | 
|  | const boost::system::error_code ec, | 
|  | const dbus::utility::MapperGetSubTreeResponse& subtree) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (subtree.empty()) | 
|  | { | 
|  | callback(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto fanConfigurationsQueried = std::make_shared<int>(subtree.size()); | 
|  | for (const auto& [objectPath, serviceMap] : subtree) | 
|  | { | 
|  | for (const auto& [service, interfaces] : serviceMap) | 
|  | { | 
|  | for (const auto& interface : interfaces) | 
|  | { | 
|  | if (interface != fanInterfaces[0]) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | managedStore::GetManagedObjectStore()->getAllProperties( | 
|  | service, objectPath, interface, requestContext, | 
|  | [asyncResp, fanSensorToInventoryPath, objectPath, | 
|  | callback, fanConfigurationsQueried]( | 
|  | const boost::system::error_code& ec, | 
|  | const dbus::utility::DBusPropertiesMap& | 
|  | properties) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | fillFanProperties(asyncResp, objectPath, properties, | 
|  | fanSensorToInventoryPath); | 
|  |  | 
|  | if (*fanConfigurationsQueried <= 0) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "Unaccounted fan configuration"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (*fanConfigurationsQueried == 1) | 
|  | { | 
|  | callback(); | 
|  | } | 
|  | --(*fanConfigurationsQueried); | 
|  | }); | 
|  | } | 
|  | } | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void AddThermalSubsystemLink( | 
|  | const sdbusplus::message::object_path& fanInventoryPath, | 
|  | nlohmann::json& sensorJson) | 
|  | { | 
|  | nlohmann::json::object_t obj; | 
|  | obj["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", fanInventoryPath.parent_path().filename(), | 
|  | "ThermalSubsystem", "Fans", fanInventoryPath.filename()); | 
|  | sensorJson["RelatedItem"] = nlohmann::json::array_t{std::move(obj)}; | 
|  | } | 
|  |  | 
|  | inline void AddRelatedItemForFanSensor( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& sensorName) | 
|  | { | 
|  | auto fanSensorToInventoryPath = | 
|  | std::make_shared<std::unordered_map<std::string, std::string>>(); | 
|  | getFanInventory(asyncResp, fanSensorToInventoryPath, | 
|  | [asyncResp, sensorName, fanSensorToInventoryPath]() { | 
|  | auto findFanObjectPath = fanSensorToInventoryPath->find(sensorName); | 
|  | if (findFanObjectPath != fanSensorToInventoryPath->end()) | 
|  | { | 
|  | sdbusplus::message::object_path fanInventoryPath( | 
|  | findFanObjectPath->second); | 
|  | AddThermalSubsystemLink(fanInventoryPath, asyncResp->res.jsonValue); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | } // namespace fan_utils | 
|  | } // namespace redfish |