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
270c728b42SJanet Adkins enum class ChassisSubNode
280c728b42SJanet Adkins {
290c728b42SJanet Adkins powerNode,
300c728b42SJanet Adkins sensorsNode,
310c728b42SJanet Adkins thermalNode,
320c728b42SJanet Adkins unknownNode,
330c728b42SJanet Adkins };
340c728b42SJanet Adkins
chassisSubNodeToString(ChassisSubNode subNode)350c728b42SJanet Adkins constexpr std::string_view chassisSubNodeToString(ChassisSubNode subNode)
360c728b42SJanet Adkins {
370c728b42SJanet Adkins switch (subNode)
380c728b42SJanet Adkins {
390c728b42SJanet Adkins case ChassisSubNode::powerNode:
400c728b42SJanet Adkins return "Power";
410c728b42SJanet Adkins case ChassisSubNode::sensorsNode:
420c728b42SJanet Adkins return "Sensors";
430c728b42SJanet Adkins case ChassisSubNode::thermalNode:
440c728b42SJanet Adkins return "Thermal";
450c728b42SJanet Adkins case ChassisSubNode::unknownNode:
460c728b42SJanet Adkins default:
470c728b42SJanet Adkins return "";
480c728b42SJanet Adkins }
490c728b42SJanet Adkins }
500c728b42SJanet Adkins
chassisSubNodeFromString(const std::string & subNodeStr)510c728b42SJanet Adkins inline ChassisSubNode chassisSubNodeFromString(const std::string& subNodeStr)
520c728b42SJanet Adkins {
530c728b42SJanet Adkins // If none match unknownNode is returned
540c728b42SJanet Adkins ChassisSubNode subNode = ChassisSubNode::unknownNode;
550c728b42SJanet Adkins
560c728b42SJanet Adkins if (subNodeStr == "Power")
570c728b42SJanet Adkins {
580c728b42SJanet Adkins subNode = ChassisSubNode::powerNode;
590c728b42SJanet Adkins }
600c728b42SJanet Adkins else if (subNodeStr == "Sensors")
610c728b42SJanet Adkins {
620c728b42SJanet Adkins subNode = ChassisSubNode::sensorsNode;
630c728b42SJanet Adkins }
640c728b42SJanet Adkins else if (subNodeStr == "Thermal")
650c728b42SJanet Adkins {
660c728b42SJanet Adkins subNode = ChassisSubNode::thermalNode;
670c728b42SJanet Adkins }
680c728b42SJanet Adkins
690c728b42SJanet Adkins return subNode;
700c728b42SJanet 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:
InventoryItem(const std::string & objPath)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
getSensorId(std::string_view sensorName,std::string_view sensorType)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>
splitSensorNameAndType(std::string_view sensorId)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 {
toReadingUnits(std::string_view sensorType)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
toReadingType(std::string_view sensorType)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 */
getState(const InventoryItem * inventoryItem,const bool sensorAvailable)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 */
getHealth(nlohmann::json & sensorJson,const dbus::utility::DBusPropertiesMap & valuesDict,const InventoryItem * inventoryItem)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
setLedState(nlohmann::json & sensorJson,const InventoryItem * inventoryItem)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 */
objectPropertiesToJson(std::string_view sensorName,std::string_view sensorType,ChassisSubNode chassisSubNode,const dbus::utility::DBusPropertiesMap & propertiesDict,nlohmann::json & sensorJson,InventoryItem * inventoryItem)375c9563608SJanet Adkins inline void objectPropertiesToJson(
376c9563608SJanet Adkins std::string_view sensorName, std::string_view sensorType,
3770c728b42SJanet Adkins ChassisSubNode chassisSubNode,
378c9563608SJanet Adkins const dbus::utility::DBusPropertiesMap& propertiesDict,
379c9563608SJanet Adkins nlohmann::json& sensorJson, InventoryItem* inventoryItem)
380c9563608SJanet Adkins {
3810c728b42SJanet 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");
4260c728b42SJanet 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
513c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
514c9563608SJanet Adkins
5150c728b42SJanet Adkins if (chassisSubNode == ChassisSubNode::sensorsNode)
516c9563608SJanet Adkins {
517c9563608SJanet Adkins properties.emplace_back(
518c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
519c9563608SJanet Adkins "/Thresholds/UpperCaution/Reading"_json_pointer);
520c9563608SJanet Adkins properties.emplace_back(
521c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
522c9563608SJanet Adkins "/Thresholds/LowerCaution/Reading"_json_pointer);
523c9563608SJanet Adkins properties.emplace_back(
524c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
525c9563608SJanet Adkins "/Thresholds/UpperCritical/Reading"_json_pointer);
526c9563608SJanet Adkins properties.emplace_back(
527c9563608SJanet Adkins "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
528c9563608SJanet Adkins "/Thresholds/LowerCritical/Reading"_json_pointer);
529*cd5a898fSJanet Adkins
530*cd5a898fSJanet Adkins /* Add additional properties specific to sensorType */
531*cd5a898fSJanet Adkins if (sensorType == "fan_tach")
532*cd5a898fSJanet Adkins {
533*cd5a898fSJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value",
534*cd5a898fSJanet Adkins "/SpeedRPM"_json_pointer);
535*cd5a898fSJanet Adkins }
536c9563608SJanet Adkins }
537c9563608SJanet Adkins else if (sensorType != "power")
538c9563608SJanet Adkins {
539c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
540c9563608SJanet Adkins "WarningHigh",
541c9563608SJanet Adkins "/UpperThresholdNonCritical"_json_pointer);
542c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
543c9563608SJanet Adkins "WarningLow",
544c9563608SJanet Adkins "/LowerThresholdNonCritical"_json_pointer);
545c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
546c9563608SJanet Adkins "CriticalHigh",
547c9563608SJanet Adkins "/UpperThresholdCritical"_json_pointer);
548c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
549c9563608SJanet Adkins "CriticalLow",
550c9563608SJanet Adkins "/LowerThresholdCritical"_json_pointer);
551c9563608SJanet Adkins }
552c9563608SJanet Adkins
553c9563608SJanet Adkins // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
554c9563608SJanet Adkins
5550c728b42SJanet Adkins if (chassisSubNode == ChassisSubNode::sensorsNode)
556c9563608SJanet Adkins {
557c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
558c9563608SJanet Adkins "/ReadingRangeMin"_json_pointer);
559c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
560c9563608SJanet Adkins "/ReadingRangeMax"_json_pointer);
561c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy",
562c9563608SJanet Adkins "Accuracy", "/Accuracy"_json_pointer);
563c9563608SJanet Adkins }
564c9563608SJanet Adkins else if (sensorType == "temperature")
565c9563608SJanet Adkins {
566c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
567c9563608SJanet Adkins "/MinReadingRangeTemp"_json_pointer);
568c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
569c9563608SJanet Adkins "/MaxReadingRangeTemp"_json_pointer);
570c9563608SJanet Adkins }
571c9563608SJanet Adkins else if (sensorType != "power")
572c9563608SJanet Adkins {
573c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
574c9563608SJanet Adkins "/MinReadingRange"_json_pointer);
575c9563608SJanet Adkins properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
576c9563608SJanet Adkins "/MaxReadingRange"_json_pointer);
577c9563608SJanet Adkins }
578c9563608SJanet Adkins
579c9563608SJanet Adkins for (const std::tuple<const char*, const char*,
580c9563608SJanet Adkins nlohmann::json::json_pointer>& p : properties)
581c9563608SJanet Adkins {
582c9563608SJanet Adkins for (const auto& [valueName, valueVariant] : propertiesDict)
583c9563608SJanet Adkins {
584c9563608SJanet Adkins if (valueName != std::get<1>(p))
585c9563608SJanet Adkins {
586c9563608SJanet Adkins continue;
587c9563608SJanet Adkins }
588c9563608SJanet Adkins
589c9563608SJanet Adkins // The property we want to set may be nested json, so use
590c9563608SJanet Adkins // a json_pointer for easy indexing into the json structure.
591c9563608SJanet Adkins const nlohmann::json::json_pointer& key = std::get<2>(p);
592c9563608SJanet Adkins
593c9563608SJanet Adkins const double* doubleValue = std::get_if<double>(&valueVariant);
594c9563608SJanet Adkins if (doubleValue == nullptr)
595c9563608SJanet Adkins {
596c9563608SJanet Adkins BMCWEB_LOG_ERROR("Got value interface that wasn't double");
597c9563608SJanet Adkins continue;
598c9563608SJanet Adkins }
599c9563608SJanet Adkins if (!std::isfinite(*doubleValue))
600c9563608SJanet Adkins {
601c9563608SJanet Adkins if (valueName == "Value")
602c9563608SJanet Adkins {
603c9563608SJanet Adkins // Readings are allowed to be NAN for unavailable; coerce
604c9563608SJanet Adkins // them to null in the json response.
605c9563608SJanet Adkins sensorJson[key] = nullptr;
606c9563608SJanet Adkins continue;
607c9563608SJanet Adkins }
608c9563608SJanet Adkins BMCWEB_LOG_WARNING("Sensor value for {} was unexpectedly {}",
609c9563608SJanet Adkins valueName, *doubleValue);
610c9563608SJanet Adkins continue;
611c9563608SJanet Adkins }
612c9563608SJanet Adkins if (forceToInt)
613c9563608SJanet Adkins {
614c9563608SJanet Adkins sensorJson[key] = static_cast<int64_t>(*doubleValue);
615c9563608SJanet Adkins }
616c9563608SJanet Adkins else
617c9563608SJanet Adkins {
618c9563608SJanet Adkins sensorJson[key] = *doubleValue;
619c9563608SJanet Adkins }
620c9563608SJanet Adkins }
621c9563608SJanet Adkins }
622c9563608SJanet Adkins }
623c9563608SJanet Adkins
6241516c21bSJanet Adkins } // namespace sensor_utils
6251516c21bSJanet Adkins } // namespace redfish
626