11516c21bSJanet Adkins #pragma once 21516c21bSJanet Adkins 3c9563608SJanet Adkins #include "dbus_utility.hpp" 4c9563608SJanet Adkins #include "generated/enums/resource.hpp" 5c9563608SJanet Adkins #include "generated/enums/sensor.hpp" 6c9563608SJanet Adkins #include "generated/enums/thermal.hpp" 7c9563608SJanet Adkins #include "str_utility.hpp" 8c9563608SJanet Adkins #include "utils/dbus_utils.hpp" 9c9563608SJanet Adkins #include "utils/json_utils.hpp" 10c9563608SJanet Adkins 11c9563608SJanet Adkins #include <sdbusplus/unpack_properties.hpp> 12c9563608SJanet Adkins 131516c21bSJanet Adkins #include <algorithm> 141516c21bSJanet Adkins #include <format> 151516c21bSJanet Adkins #include <ranges> 161516c21bSJanet Adkins #include <string> 171516c21bSJanet Adkins #include <string_view> 18c9563608SJanet Adkins #include <tuple> 191516c21bSJanet Adkins #include <utility> 201516c21bSJanet Adkins #include <vector> 211516c21bSJanet Adkins 221516c21bSJanet Adkins namespace redfish 231516c21bSJanet Adkins { 241516c21bSJanet Adkins namespace sensor_utils 251516c21bSJanet Adkins { 261516c21bSJanet Adkins 27*0c728b42SJanet Adkins enum class ChassisSubNode 28*0c728b42SJanet Adkins { 29*0c728b42SJanet Adkins powerNode, 30*0c728b42SJanet Adkins sensorsNode, 31*0c728b42SJanet Adkins thermalNode, 32*0c728b42SJanet Adkins unknownNode, 33*0c728b42SJanet Adkins }; 34*0c728b42SJanet Adkins 35*0c728b42SJanet Adkins constexpr std::string_view chassisSubNodeToString(ChassisSubNode subNode) 36*0c728b42SJanet Adkins { 37*0c728b42SJanet Adkins switch (subNode) 38*0c728b42SJanet Adkins { 39*0c728b42SJanet Adkins case ChassisSubNode::powerNode: 40*0c728b42SJanet Adkins return "Power"; 41*0c728b42SJanet Adkins case ChassisSubNode::sensorsNode: 42*0c728b42SJanet Adkins return "Sensors"; 43*0c728b42SJanet Adkins case ChassisSubNode::thermalNode: 44*0c728b42SJanet Adkins return "Thermal"; 45*0c728b42SJanet Adkins case ChassisSubNode::unknownNode: 46*0c728b42SJanet Adkins default: 47*0c728b42SJanet Adkins return ""; 48*0c728b42SJanet Adkins } 49*0c728b42SJanet Adkins } 50*0c728b42SJanet Adkins 51*0c728b42SJanet Adkins inline ChassisSubNode chassisSubNodeFromString(const std::string& subNodeStr) 52*0c728b42SJanet Adkins { 53*0c728b42SJanet Adkins // If none match unknownNode is returned 54*0c728b42SJanet Adkins ChassisSubNode subNode = ChassisSubNode::unknownNode; 55*0c728b42SJanet Adkins 56*0c728b42SJanet Adkins if (subNodeStr == "Power") 57*0c728b42SJanet Adkins { 58*0c728b42SJanet Adkins subNode = ChassisSubNode::powerNode; 59*0c728b42SJanet Adkins } 60*0c728b42SJanet Adkins else if (subNodeStr == "Sensors") 61*0c728b42SJanet Adkins { 62*0c728b42SJanet Adkins subNode = ChassisSubNode::sensorsNode; 63*0c728b42SJanet Adkins } 64*0c728b42SJanet Adkins else if (subNodeStr == "Thermal") 65*0c728b42SJanet Adkins { 66*0c728b42SJanet Adkins subNode = ChassisSubNode::thermalNode; 67*0c728b42SJanet Adkins } 68*0c728b42SJanet Adkins 69*0c728b42SJanet Adkins return subNode; 70*0c728b42SJanet Adkins } 71c9563608SJanet Adkins 72c9563608SJanet Adkins /** 73c9563608SJanet Adkins * Possible states for physical inventory leds 74c9563608SJanet Adkins */ 75c9563608SJanet Adkins enum class LedState 76c9563608SJanet Adkins { 77c9563608SJanet Adkins OFF, 78c9563608SJanet Adkins ON, 79c9563608SJanet Adkins BLINK, 80c9563608SJanet Adkins UNKNOWN 81c9563608SJanet Adkins }; 82c9563608SJanet Adkins 83c9563608SJanet Adkins /** 84c9563608SJanet Adkins * D-Bus inventory item associated with one or more sensors. 85c9563608SJanet Adkins */ 86c9563608SJanet Adkins class InventoryItem 87c9563608SJanet Adkins { 88c9563608SJanet Adkins public: 89c9563608SJanet Adkins explicit InventoryItem(const std::string& objPath) : objectPath(objPath) 90c9563608SJanet Adkins { 91c9563608SJanet Adkins // Set inventory item name to last node of object path 92c9563608SJanet Adkins sdbusplus::message::object_path path(objectPath); 93c9563608SJanet Adkins name = path.filename(); 94c9563608SJanet Adkins if (name.empty()) 95c9563608SJanet Adkins { 96c9563608SJanet Adkins BMCWEB_LOG_ERROR("Failed to find '/' in {}", objectPath); 97c9563608SJanet Adkins } 98c9563608SJanet Adkins } 99c9563608SJanet Adkins 100c9563608SJanet Adkins std::string objectPath; 101c9563608SJanet Adkins std::string name; 102c9563608SJanet Adkins bool isPresent = true; 103c9563608SJanet Adkins bool isFunctional = true; 104c9563608SJanet Adkins bool isPowerSupply = false; 105c9563608SJanet Adkins int powerSupplyEfficiencyPercent = -1; 106c9563608SJanet Adkins std::string manufacturer; 107c9563608SJanet Adkins std::string model; 108c9563608SJanet Adkins std::string partNumber; 109c9563608SJanet Adkins std::string serialNumber; 110c9563608SJanet Adkins std::set<std::string> sensors; 111c9563608SJanet Adkins std::string ledObjectPath; 112c9563608SJanet Adkins LedState ledState = LedState::UNKNOWN; 113c9563608SJanet Adkins }; 114c9563608SJanet Adkins 1151516c21bSJanet Adkins inline std::string getSensorId(std::string_view sensorName, 1161516c21bSJanet Adkins std::string_view sensorType) 1171516c21bSJanet Adkins { 1181516c21bSJanet Adkins std::string normalizedType(sensorType); 1191516c21bSJanet Adkins auto remove = std::ranges::remove(normalizedType, '_'); 1201516c21bSJanet Adkins normalizedType.erase(std::ranges::begin(remove), normalizedType.end()); 1211516c21bSJanet Adkins 1221516c21bSJanet Adkins return std::format("{}_{}", normalizedType, sensorName); 1231516c21bSJanet Adkins } 1241516c21bSJanet Adkins 1251516c21bSJanet Adkins inline std::pair<std::string, std::string> 1261516c21bSJanet Adkins splitSensorNameAndType(std::string_view sensorId) 1271516c21bSJanet Adkins { 1281516c21bSJanet Adkins size_t index = sensorId.find('_'); 1291516c21bSJanet Adkins if (index == std::string::npos) 1301516c21bSJanet Adkins { 1311516c21bSJanet Adkins return std::make_pair<std::string, std::string>("", ""); 1321516c21bSJanet Adkins } 1331516c21bSJanet Adkins std::string sensorType{sensorId.substr(0, index)}; 1341516c21bSJanet Adkins std::string sensorName{sensorId.substr(index + 1)}; 1351516c21bSJanet Adkins // fan_pwm and fan_tach need special handling 1361516c21bSJanet Adkins if (sensorType == "fantach" || sensorType == "fanpwm") 1371516c21bSJanet Adkins { 1381516c21bSJanet Adkins sensorType.insert(3, 1, '_'); 1391516c21bSJanet Adkins } 1401516c21bSJanet Adkins return std::make_pair(sensorType, sensorName); 1411516c21bSJanet Adkins } 1421516c21bSJanet Adkins 143c9563608SJanet Adkins namespace sensors 144c9563608SJanet Adkins { 145c9563608SJanet Adkins inline std::string_view toReadingUnits(std::string_view sensorType) 146c9563608SJanet Adkins { 147c9563608SJanet Adkins if (sensorType == "voltage") 148c9563608SJanet Adkins { 149c9563608SJanet Adkins return "V"; 150c9563608SJanet Adkins } 151c9563608SJanet Adkins if (sensorType == "power") 152c9563608SJanet Adkins { 153c9563608SJanet Adkins return "W"; 154c9563608SJanet Adkins } 155c9563608SJanet Adkins if (sensorType == "current") 156c9563608SJanet Adkins { 157c9563608SJanet Adkins return "A"; 158c9563608SJanet Adkins } 159c9563608SJanet Adkins if (sensorType == "fan_tach") 160c9563608SJanet Adkins { 161c9563608SJanet Adkins return "RPM"; 162c9563608SJanet Adkins } 163c9563608SJanet Adkins if (sensorType == "temperature") 164c9563608SJanet Adkins { 165c9563608SJanet Adkins return "Cel"; 166c9563608SJanet Adkins } 167c9563608SJanet Adkins if (sensorType == "fan_pwm" || sensorType == "utilization" || 168c9563608SJanet Adkins sensorType == "humidity") 169c9563608SJanet Adkins { 170c9563608SJanet Adkins return "%"; 171c9563608SJanet Adkins } 172c9563608SJanet Adkins if (sensorType == "altitude") 173c9563608SJanet Adkins { 174c9563608SJanet Adkins return "m"; 175c9563608SJanet Adkins } 176c9563608SJanet Adkins if (sensorType == "airflow") 177c9563608SJanet Adkins { 178c9563608SJanet Adkins return "cft_i/min"; 179c9563608SJanet Adkins } 180c9563608SJanet Adkins if (sensorType == "energy") 181c9563608SJanet Adkins { 182c9563608SJanet Adkins return "J"; 183c9563608SJanet Adkins } 184c9563608SJanet Adkins return ""; 185c9563608SJanet Adkins } 186c9563608SJanet Adkins 187c9563608SJanet Adkins inline sensor::ReadingType toReadingType(std::string_view sensorType) 188c9563608SJanet Adkins { 189c9563608SJanet Adkins if (sensorType == "voltage") 190c9563608SJanet Adkins { 191c9563608SJanet Adkins return sensor::ReadingType::Voltage; 192c9563608SJanet Adkins } 193c9563608SJanet Adkins if (sensorType == "power") 194c9563608SJanet Adkins { 195c9563608SJanet Adkins return sensor::ReadingType::Power; 196c9563608SJanet Adkins } 197c9563608SJanet Adkins if (sensorType == "current") 198c9563608SJanet Adkins { 199c9563608SJanet Adkins return sensor::ReadingType::Current; 200c9563608SJanet Adkins } 201c9563608SJanet Adkins if (sensorType == "fan_tach") 202c9563608SJanet Adkins { 203c9563608SJanet Adkins return sensor::ReadingType::Rotational; 204c9563608SJanet Adkins } 205c9563608SJanet Adkins if (sensorType == "temperature") 206c9563608SJanet Adkins { 207c9563608SJanet Adkins return sensor::ReadingType::Temperature; 208c9563608SJanet Adkins } 209c9563608SJanet Adkins if (sensorType == "fan_pwm" || sensorType == "utilization") 210c9563608SJanet Adkins { 211c9563608SJanet Adkins return sensor::ReadingType::Percent; 212c9563608SJanet Adkins } 213c9563608SJanet Adkins if (sensorType == "humidity") 214c9563608SJanet Adkins { 215c9563608SJanet Adkins return sensor::ReadingType::Humidity; 216c9563608SJanet Adkins } 217c9563608SJanet Adkins if (sensorType == "altitude") 218c9563608SJanet Adkins { 219c9563608SJanet Adkins return sensor::ReadingType::Altitude; 220c9563608SJanet Adkins } 221c9563608SJanet Adkins if (sensorType == "airflow") 222c9563608SJanet Adkins { 223c9563608SJanet Adkins return sensor::ReadingType::AirFlow; 224c9563608SJanet Adkins } 225c9563608SJanet Adkins if (sensorType == "energy") 226c9563608SJanet Adkins { 227c9563608SJanet Adkins return sensor::ReadingType::EnergyJoules; 228c9563608SJanet Adkins } 229c9563608SJanet Adkins return sensor::ReadingType::Invalid; 230c9563608SJanet Adkins } 231c9563608SJanet Adkins 232c9563608SJanet Adkins } // namespace sensors 233c9563608SJanet Adkins 234c9563608SJanet Adkins /** 235c9563608SJanet Adkins * @brief Returns the Redfish State value for the specified inventory item. 236c9563608SJanet Adkins * @param inventoryItem D-Bus inventory item associated with a sensor. 237c9563608SJanet Adkins * @param sensorAvailable Boolean representing if D-Bus sensor is marked as 238c9563608SJanet Adkins * available. 239c9563608SJanet Adkins * @return State value for inventory item. 240c9563608SJanet Adkins */ 241c9563608SJanet Adkins inline resource::State getState(const InventoryItem* inventoryItem, 242c9563608SJanet Adkins const bool sensorAvailable) 243c9563608SJanet Adkins { 244c9563608SJanet Adkins if ((inventoryItem != nullptr) && !(inventoryItem->isPresent)) 245c9563608SJanet Adkins { 246c9563608SJanet Adkins return resource::State::Absent; 247c9563608SJanet Adkins } 248c9563608SJanet Adkins 249c9563608SJanet Adkins if (!sensorAvailable) 250c9563608SJanet Adkins { 251c9563608SJanet Adkins return resource::State::UnavailableOffline; 252c9563608SJanet Adkins } 253c9563608SJanet Adkins 254c9563608SJanet Adkins return resource::State::Enabled; 255c9563608SJanet Adkins } 256c9563608SJanet Adkins 257c9563608SJanet Adkins /** 258c9563608SJanet Adkins * @brief Returns the Redfish Health value for the specified sensor. 259c9563608SJanet Adkins * @param sensorJson Sensor JSON object. 260c9563608SJanet Adkins * @param valuesDict Map of all sensor DBus values. 261c9563608SJanet Adkins * @param inventoryItem D-Bus inventory item associated with the sensor. Will 262c9563608SJanet Adkins * be nullptr if no associated inventory item was found. 263c9563608SJanet Adkins * @return Health value for sensor. 264c9563608SJanet Adkins */ 265c9563608SJanet Adkins inline std::string getHealth(nlohmann::json& sensorJson, 266c9563608SJanet Adkins const dbus::utility::DBusPropertiesMap& valuesDict, 267c9563608SJanet Adkins const InventoryItem* inventoryItem) 268c9563608SJanet Adkins { 269c9563608SJanet Adkins // Get current health value (if any) in the sensor JSON object. Some JSON 270c9563608SJanet Adkins // objects contain multiple sensors (such as PowerSupplies). We want to set 271c9563608SJanet Adkins // the overall health to be the most severe of any of the sensors. 272c9563608SJanet Adkins std::string currentHealth; 273c9563608SJanet Adkins auto statusIt = sensorJson.find("Status"); 274c9563608SJanet Adkins if (statusIt != sensorJson.end()) 275c9563608SJanet Adkins { 276c9563608SJanet Adkins auto healthIt = statusIt->find("Health"); 277c9563608SJanet Adkins if (healthIt != statusIt->end()) 278c9563608SJanet Adkins { 279c9563608SJanet Adkins std::string* health = healthIt->get_ptr<std::string*>(); 280c9563608SJanet Adkins if (health != nullptr) 281c9563608SJanet Adkins { 282c9563608SJanet Adkins currentHealth = *health; 283c9563608SJanet Adkins } 284c9563608SJanet Adkins } 285c9563608SJanet Adkins } 286c9563608SJanet Adkins 287c9563608SJanet Adkins // If current health in JSON object is already Critical, return that. This 288c9563608SJanet Adkins // should override the sensor health, which might be less severe. 289c9563608SJanet Adkins if (currentHealth == "Critical") 290c9563608SJanet Adkins { 291c9563608SJanet Adkins return "Critical"; 292c9563608SJanet Adkins } 293c9563608SJanet Adkins 294c9563608SJanet Adkins const bool* criticalAlarmHigh = nullptr; 295c9563608SJanet Adkins const bool* criticalAlarmLow = nullptr; 296c9563608SJanet Adkins const bool* warningAlarmHigh = nullptr; 297c9563608SJanet Adkins const bool* warningAlarmLow = nullptr; 298c9563608SJanet Adkins 299c9563608SJanet Adkins const bool success = sdbusplus::unpackPropertiesNoThrow( 300c9563608SJanet Adkins dbus_utils::UnpackErrorPrinter(), valuesDict, "CriticalAlarmHigh", 301c9563608SJanet Adkins criticalAlarmHigh, "CriticalAlarmLow", criticalAlarmLow, 302c9563608SJanet Adkins "WarningAlarmHigh", warningAlarmHigh, "WarningAlarmLow", 303c9563608SJanet Adkins warningAlarmLow); 304c9563608SJanet Adkins 305c9563608SJanet Adkins if (success) 306c9563608SJanet Adkins { 307c9563608SJanet Adkins // Check if sensor has critical threshold alarm 308c9563608SJanet Adkins if ((criticalAlarmHigh != nullptr && *criticalAlarmHigh) || 309c9563608SJanet Adkins (criticalAlarmLow != nullptr && *criticalAlarmLow)) 310c9563608SJanet Adkins { 311c9563608SJanet Adkins return "Critical"; 312c9563608SJanet Adkins } 313c9563608SJanet Adkins } 314c9563608SJanet Adkins 315c9563608SJanet Adkins // Check if associated inventory item is not functional 316c9563608SJanet Adkins if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional)) 317c9563608SJanet Adkins { 318c9563608SJanet Adkins return "Critical"; 319c9563608SJanet Adkins } 320c9563608SJanet Adkins 321c9563608SJanet Adkins // If current health in JSON object is already Warning, return that. This 322c9563608SJanet Adkins // should override the sensor status, which might be less severe. 323c9563608SJanet Adkins if (currentHealth == "Warning") 324c9563608SJanet Adkins { 325c9563608SJanet Adkins return "Warning"; 326c9563608SJanet Adkins } 327c9563608SJanet Adkins 328c9563608SJanet Adkins if (success) 329c9563608SJanet Adkins { 330c9563608SJanet Adkins // Check if sensor has warning threshold alarm 331c9563608SJanet Adkins if ((warningAlarmHigh != nullptr && *warningAlarmHigh) || 332c9563608SJanet Adkins (warningAlarmLow != nullptr && *warningAlarmLow)) 333c9563608SJanet Adkins { 334c9563608SJanet Adkins return "Warning"; 335c9563608SJanet Adkins } 336c9563608SJanet Adkins } 337c9563608SJanet Adkins 338c9563608SJanet Adkins return "OK"; 339c9563608SJanet Adkins } 340c9563608SJanet Adkins 341c9563608SJanet Adkins inline void setLedState(nlohmann::json& sensorJson, 342c9563608SJanet Adkins const InventoryItem* inventoryItem) 343c9563608SJanet Adkins { 344c9563608SJanet Adkins if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty()) 345c9563608SJanet Adkins { 346c9563608SJanet Adkins switch (inventoryItem->ledState) 347c9563608SJanet Adkins { 348c9563608SJanet Adkins case LedState::OFF: 349c9563608SJanet Adkins sensorJson["IndicatorLED"] = resource::IndicatorLED::Off; 350c9563608SJanet Adkins break; 351c9563608SJanet Adkins case LedState::ON: 352c9563608SJanet Adkins sensorJson["IndicatorLED"] = resource::IndicatorLED::Lit; 353c9563608SJanet Adkins break; 354c9563608SJanet Adkins case LedState::BLINK: 355c9563608SJanet Adkins sensorJson["IndicatorLED"] = resource::IndicatorLED::Blinking; 356c9563608SJanet Adkins break; 357c9563608SJanet Adkins default: 358c9563608SJanet Adkins break; 359c9563608SJanet Adkins } 360c9563608SJanet Adkins } 361c9563608SJanet Adkins } 362c9563608SJanet Adkins 363c9563608SJanet Adkins /** 364c9563608SJanet Adkins * @brief Builds a json sensor representation of a sensor. 365c9563608SJanet Adkins * @param sensorName The name of the sensor to be built 366c9563608SJanet Adkins * @param sensorType The type (temperature, fan_tach, etc) of the sensor to 367c9563608SJanet Adkins * build 368c9563608SJanet Adkins * @param chassisSubNode The subnode (thermal, sensor, etc) of the sensor 369c9563608SJanet Adkins * @param propertiesDict A dictionary of the properties to build the sensor 370c9563608SJanet Adkins * from. 371c9563608SJanet Adkins * @param sensorJson The json object to fill 372c9563608SJanet Adkins * @param inventoryItem D-Bus inventory item associated with the sensor. Will 373c9563608SJanet Adkins * be nullptr if no associated inventory item was found. 374c9563608SJanet Adkins */ 375c9563608SJanet Adkins inline void objectPropertiesToJson( 376c9563608SJanet Adkins std::string_view sensorName, std::string_view sensorType, 377*0c728b42SJanet Adkins ChassisSubNode chassisSubNode, 378c9563608SJanet Adkins const dbus::utility::DBusPropertiesMap& propertiesDict, 379c9563608SJanet Adkins nlohmann::json& sensorJson, InventoryItem* inventoryItem) 380c9563608SJanet Adkins { 381*0c728b42SJanet Adkins if (chassisSubNode == ChassisSubNode::sensorsNode) 382c9563608SJanet Adkins { 383c9563608SJanet Adkins std::string subNodeEscaped = getSensorId(sensorName, sensorType); 384c9563608SJanet Adkins // For sensors in SensorCollection we set Id instead of MemberId, 385c9563608SJanet Adkins // including power sensors. 386c9563608SJanet Adkins sensorJson["Id"] = std::move(subNodeEscaped); 387c9563608SJanet Adkins 388c9563608SJanet Adkins std::string sensorNameEs(sensorName); 389c9563608SJanet Adkins std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); 390c9563608SJanet Adkins sensorJson["Name"] = std::move(sensorNameEs); 391c9563608SJanet Adkins } 392c9563608SJanet Adkins else if (sensorType != "power") 393c9563608SJanet Adkins { 394c9563608SJanet Adkins // Set MemberId and Name for non-power sensors. For PowerSupplies and 395c9563608SJanet Adkins // PowerControl, those properties have more general values because 396c9563608SJanet Adkins // multiple sensors can be stored in the same JSON object. 397c9563608SJanet Adkins std::string sensorNameEs(sensorName); 398c9563608SJanet Adkins std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); 399c9563608SJanet Adkins sensorJson["Name"] = std::move(sensorNameEs); 400c9563608SJanet Adkins } 401c9563608SJanet Adkins 402c9563608SJanet Adkins const bool* checkAvailable = nullptr; 403c9563608SJanet Adkins bool available = true; 404c9563608SJanet Adkins const bool success = sdbusplus::unpackPropertiesNoThrow( 405c9563608SJanet Adkins dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", 406c9563608SJanet Adkins checkAvailable); 407c9563608SJanet Adkins if (!success) 408c9563608SJanet Adkins { 409c9563608SJanet Adkins messages::internalError(); 410c9563608SJanet Adkins } 411c9563608SJanet Adkins if (checkAvailable != nullptr) 412c9563608SJanet Adkins { 413c9563608SJanet Adkins available = *checkAvailable; 414c9563608SJanet Adkins } 415c9563608SJanet Adkins 416c9563608SJanet Adkins sensorJson["Status"]["State"] = getState(inventoryItem, available); 417c9563608SJanet Adkins sensorJson["Status"]["Health"] = 418c9563608SJanet Adkins getHealth(sensorJson, propertiesDict, inventoryItem); 419c9563608SJanet Adkins 420c9563608SJanet Adkins // Parameter to set to override the type we get from dbus, and force it to 421c9563608SJanet Adkins // int, regardless of what is available. This is used for schemas like fan, 422c9563608SJanet Adkins // that require integers, not floats. 423c9563608SJanet Adkins bool forceToInt = false; 424c9563608SJanet Adkins 425c9563608SJanet Adkins nlohmann::json::json_pointer unit("/Reading"); 426*0c728b42SJanet Adkins if (chassisSubNode == ChassisSubNode::sensorsNode) 427c9563608SJanet Adkins { 428c9563608SJanet Adkins sensorJson["@odata.type"] = "#Sensor.v1_2_0.Sensor"; 429c9563608SJanet Adkins 430c9563608SJanet Adkins sensor::ReadingType readingType = sensors::toReadingType(sensorType); 431c9563608SJanet Adkins if (readingType == sensor::ReadingType::Invalid) 432c9563608SJanet Adkins { 433c9563608SJanet Adkins BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}", 434c9563608SJanet Adkins sensorType); 435c9563608SJanet Adkins } 436c9563608SJanet Adkins else 437c9563608SJanet Adkins { 438c9563608SJanet Adkins sensorJson["ReadingType"] = readingType; 439c9563608SJanet Adkins } 440c9563608SJanet Adkins 441c9563608SJanet Adkins std::string_view readingUnits = sensors::toReadingUnits(sensorType); 442c9563608SJanet Adkins if (readingUnits.empty()) 443c9563608SJanet Adkins { 444c9563608SJanet Adkins BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}", 445c9563608SJanet Adkins sensorType); 446c9563608SJanet Adkins } 447c9563608SJanet Adkins else 448c9563608SJanet Adkins { 449c9563608SJanet Adkins sensorJson["ReadingUnits"] = readingUnits; 450c9563608SJanet Adkins } 451c9563608SJanet Adkins } 452c9563608SJanet Adkins else if (sensorType == "temperature") 453c9563608SJanet Adkins { 454c9563608SJanet Adkins unit = "/ReadingCelsius"_json_pointer; 455c9563608SJanet Adkins sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; 456c9563608SJanet Adkins // TODO(ed) Documentation says that path should be type fan_tach, 457c9563608SJanet Adkins // implementation seems to implement fan 458c9563608SJanet Adkins } 459c9563608SJanet Adkins else if (sensorType == "fan" || sensorType == "fan_tach") 460c9563608SJanet Adkins { 461c9563608SJanet Adkins unit = "/Reading"_json_pointer; 462c9563608SJanet Adkins sensorJson["ReadingUnits"] = thermal::ReadingUnits::RPM; 463c9563608SJanet Adkins sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; 464c9563608SJanet Adkins setLedState(sensorJson, inventoryItem); 465c9563608SJanet Adkins forceToInt = true; 466c9563608SJanet Adkins } 467c9563608SJanet Adkins else if (sensorType == "fan_pwm") 468c9563608SJanet Adkins { 469c9563608SJanet Adkins unit = "/Reading"_json_pointer; 470c9563608SJanet Adkins sensorJson["ReadingUnits"] = thermal::ReadingUnits::Percent; 471c9563608SJanet Adkins sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; 472c9563608SJanet Adkins setLedState(sensorJson, inventoryItem); 473c9563608SJanet Adkins forceToInt = true; 474c9563608SJanet Adkins } 475c9563608SJanet Adkins else if (sensorType == "voltage") 476c9563608SJanet Adkins { 477c9563608SJanet Adkins unit = "/ReadingVolts"_json_pointer; 478c9563608SJanet Adkins sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; 479c9563608SJanet Adkins } 480c9563608SJanet Adkins else if (sensorType == "power") 481c9563608SJanet Adkins { 482c9563608SJanet Adkins std::string lower; 483c9563608SJanet Adkins std::ranges::transform(sensorName, std::back_inserter(lower), 484c9563608SJanet Adkins bmcweb::asciiToLower); 485c9563608SJanet Adkins if (lower == "total_power") 486c9563608SJanet Adkins { 487c9563608SJanet Adkins sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; 488c9563608SJanet Adkins // Put multiple "sensors" into a single PowerControl, so have 489c9563608SJanet Adkins // generic names for MemberId and Name. Follows Redfish mockup. 490c9563608SJanet Adkins sensorJson["MemberId"] = "0"; 491c9563608SJanet Adkins sensorJson["Name"] = "Chassis Power Control"; 492c9563608SJanet Adkins unit = "/PowerConsumedWatts"_json_pointer; 493c9563608SJanet Adkins } 494c9563608SJanet Adkins else if (lower.find("input") != std::string::npos) 495c9563608SJanet Adkins { 496c9563608SJanet Adkins unit = "/PowerInputWatts"_json_pointer; 497c9563608SJanet Adkins } 498c9563608SJanet Adkins else 499c9563608SJanet Adkins { 500c9563608SJanet Adkins unit = "/PowerOutputWatts"_json_pointer; 501c9563608SJanet Adkins } 502c9563608SJanet Adkins } 503c9563608SJanet Adkins else 504c9563608SJanet Adkins { 505c9563608SJanet Adkins BMCWEB_LOG_ERROR("Redfish cannot map object type for {}", sensorName); 506c9563608SJanet Adkins return; 507c9563608SJanet Adkins } 508c9563608SJanet Adkins // Map of dbus interface name, dbus property name and redfish property_name 509c9563608SJanet Adkins std::vector< 510c9563608SJanet Adkins std::tuple<const char*, const char*, nlohmann::json::json_pointer>> 511c9563608SJanet Adkins properties; 512c9563608SJanet Adkins properties.reserve(7); 513c9563608SJanet Adkins 514c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); 515c9563608SJanet Adkins 516*0c728b42SJanet Adkins if (chassisSubNode == ChassisSubNode::sensorsNode) 517c9563608SJanet Adkins { 518c9563608SJanet Adkins properties.emplace_back( 519c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", 520c9563608SJanet Adkins "/Thresholds/UpperCaution/Reading"_json_pointer); 521c9563608SJanet Adkins properties.emplace_back( 522c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", 523c9563608SJanet Adkins "/Thresholds/LowerCaution/Reading"_json_pointer); 524c9563608SJanet Adkins properties.emplace_back( 525c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", 526c9563608SJanet Adkins "/Thresholds/UpperCritical/Reading"_json_pointer); 527c9563608SJanet Adkins properties.emplace_back( 528c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", 529c9563608SJanet Adkins "/Thresholds/LowerCritical/Reading"_json_pointer); 530c9563608SJanet Adkins } 531c9563608SJanet Adkins else if (sensorType != "power") 532c9563608SJanet Adkins { 533c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", 534c9563608SJanet Adkins "WarningHigh", 535c9563608SJanet Adkins "/UpperThresholdNonCritical"_json_pointer); 536c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", 537c9563608SJanet Adkins "WarningLow", 538c9563608SJanet Adkins "/LowerThresholdNonCritical"_json_pointer); 539c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", 540c9563608SJanet Adkins "CriticalHigh", 541c9563608SJanet Adkins "/UpperThresholdCritical"_json_pointer); 542c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", 543c9563608SJanet Adkins "CriticalLow", 544c9563608SJanet Adkins "/LowerThresholdCritical"_json_pointer); 545c9563608SJanet Adkins } 546c9563608SJanet Adkins 547c9563608SJanet Adkins // TODO Need to get UpperThresholdFatal and LowerThresholdFatal 548c9563608SJanet Adkins 549*0c728b42SJanet Adkins if (chassisSubNode == ChassisSubNode::sensorsNode) 550c9563608SJanet Adkins { 551c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 552c9563608SJanet Adkins "/ReadingRangeMin"_json_pointer); 553c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 554c9563608SJanet Adkins "/ReadingRangeMax"_json_pointer); 555c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy", 556c9563608SJanet Adkins "Accuracy", "/Accuracy"_json_pointer); 557c9563608SJanet Adkins } 558c9563608SJanet Adkins else if (sensorType == "temperature") 559c9563608SJanet Adkins { 560c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 561c9563608SJanet Adkins "/MinReadingRangeTemp"_json_pointer); 562c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 563c9563608SJanet Adkins "/MaxReadingRangeTemp"_json_pointer); 564c9563608SJanet Adkins } 565c9563608SJanet Adkins else if (sensorType != "power") 566c9563608SJanet Adkins { 567c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 568c9563608SJanet Adkins "/MinReadingRange"_json_pointer); 569c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 570c9563608SJanet Adkins "/MaxReadingRange"_json_pointer); 571c9563608SJanet Adkins } 572c9563608SJanet Adkins 573c9563608SJanet Adkins for (const std::tuple<const char*, const char*, 574c9563608SJanet Adkins nlohmann::json::json_pointer>& p : properties) 575c9563608SJanet Adkins { 576c9563608SJanet Adkins for (const auto& [valueName, valueVariant] : propertiesDict) 577c9563608SJanet Adkins { 578c9563608SJanet Adkins if (valueName != std::get<1>(p)) 579c9563608SJanet Adkins { 580c9563608SJanet Adkins continue; 581c9563608SJanet Adkins } 582c9563608SJanet Adkins 583c9563608SJanet Adkins // The property we want to set may be nested json, so use 584c9563608SJanet Adkins // a json_pointer for easy indexing into the json structure. 585c9563608SJanet Adkins const nlohmann::json::json_pointer& key = std::get<2>(p); 586c9563608SJanet Adkins 587c9563608SJanet Adkins const double* doubleValue = std::get_if<double>(&valueVariant); 588c9563608SJanet Adkins if (doubleValue == nullptr) 589c9563608SJanet Adkins { 590c9563608SJanet Adkins BMCWEB_LOG_ERROR("Got value interface that wasn't double"); 591c9563608SJanet Adkins continue; 592c9563608SJanet Adkins } 593c9563608SJanet Adkins if (!std::isfinite(*doubleValue)) 594c9563608SJanet Adkins { 595c9563608SJanet Adkins if (valueName == "Value") 596c9563608SJanet Adkins { 597c9563608SJanet Adkins // Readings are allowed to be NAN for unavailable; coerce 598c9563608SJanet Adkins // them to null in the json response. 599c9563608SJanet Adkins sensorJson[key] = nullptr; 600c9563608SJanet Adkins continue; 601c9563608SJanet Adkins } 602c9563608SJanet Adkins BMCWEB_LOG_WARNING("Sensor value for {} was unexpectedly {}", 603c9563608SJanet Adkins valueName, *doubleValue); 604c9563608SJanet Adkins continue; 605c9563608SJanet Adkins } 606c9563608SJanet Adkins if (forceToInt) 607c9563608SJanet Adkins { 608c9563608SJanet Adkins sensorJson[key] = static_cast<int64_t>(*doubleValue); 609c9563608SJanet Adkins } 610c9563608SJanet Adkins else 611c9563608SJanet Adkins { 612c9563608SJanet Adkins sensorJson[key] = *doubleValue; 613c9563608SJanet Adkins } 614c9563608SJanet Adkins } 615c9563608SJanet Adkins } 616c9563608SJanet Adkins } 617c9563608SJanet Adkins 6181516c21bSJanet Adkins } // namespace sensor_utils 6191516c21bSJanet Adkins } // namespace redfish 620