1 #pragma once
2
3 #include "dbus_utility.hpp"
4 #include "generated/enums/resource.hpp"
5 #include "generated/enums/sensor.hpp"
6 #include "generated/enums/thermal.hpp"
7 #include "str_utility.hpp"
8 #include "utils/dbus_utils.hpp"
9 #include "utils/json_utils.hpp"
10
11 #include <boost/url/format.hpp>
12 #include <sdbusplus/unpack_properties.hpp>
13
14 #include <algorithm>
15 #include <format>
16 #include <functional>
17 #include <optional>
18 #include <ranges>
19 #include <string>
20 #include <string_view>
21 #include <tuple>
22 #include <utility>
23 #include <vector>
24
25 namespace redfish
26 {
27 namespace sensor_utils
28 {
29
30 enum class ChassisSubNode
31 {
32 powerNode,
33 sensorsNode,
34 thermalNode,
35 thermalMetricsNode,
36 unknownNode,
37 };
38
chassisSubNodeToString(ChassisSubNode subNode)39 constexpr std::string_view chassisSubNodeToString(ChassisSubNode subNode)
40 {
41 switch (subNode)
42 {
43 case ChassisSubNode::powerNode:
44 return "Power";
45 case ChassisSubNode::sensorsNode:
46 return "Sensors";
47 case ChassisSubNode::thermalNode:
48 return "Thermal";
49 case ChassisSubNode::thermalMetricsNode:
50 return "ThermalMetrics";
51 case ChassisSubNode::unknownNode:
52 default:
53 return "";
54 }
55 }
56
chassisSubNodeFromString(const std::string & subNodeStr)57 inline ChassisSubNode chassisSubNodeFromString(const std::string& subNodeStr)
58 {
59 // If none match unknownNode is returned
60 ChassisSubNode subNode = ChassisSubNode::unknownNode;
61
62 if (subNodeStr == "Power")
63 {
64 subNode = ChassisSubNode::powerNode;
65 }
66 else if (subNodeStr == "Sensors")
67 {
68 subNode = ChassisSubNode::sensorsNode;
69 }
70 else if (subNodeStr == "Thermal")
71 {
72 subNode = ChassisSubNode::thermalNode;
73 }
74 else if (subNodeStr == "ThermalMetrics")
75 {
76 subNode = ChassisSubNode::thermalMetricsNode;
77 }
78
79 return subNode;
80 }
81
isExcerptNode(const ChassisSubNode subNode)82 inline bool isExcerptNode(const ChassisSubNode subNode)
83 {
84 return (subNode == ChassisSubNode::thermalMetricsNode);
85 }
86
87 /**
88 * Possible states for physical inventory leds
89 */
90 enum class LedState
91 {
92 OFF,
93 ON,
94 BLINK,
95 UNKNOWN
96 };
97
98 /**
99 * D-Bus inventory item associated with one or more sensors.
100 */
101 class InventoryItem
102 {
103 public:
InventoryItem(const std::string & objPath)104 explicit InventoryItem(const std::string& objPath) : objectPath(objPath)
105 {
106 // Set inventory item name to last node of object path
107 sdbusplus::message::object_path path(objectPath);
108 name = path.filename();
109 if (name.empty())
110 {
111 BMCWEB_LOG_ERROR("Failed to find '/' in {}", objectPath);
112 }
113 }
114
115 std::string objectPath;
116 std::string name;
117 bool isPresent = true;
118 bool isFunctional = true;
119 bool isPowerSupply = false;
120 int powerSupplyEfficiencyPercent = -1;
121 std::string manufacturer;
122 std::string model;
123 std::string partNumber;
124 std::string serialNumber;
125 std::set<std::string> sensors;
126 std::string ledObjectPath;
127 LedState ledState = LedState::UNKNOWN;
128 };
129
getSensorId(std::string_view sensorName,std::string_view sensorType)130 inline std::string getSensorId(std::string_view sensorName,
131 std::string_view sensorType)
132 {
133 std::string normalizedType(sensorType);
134 auto remove = std::ranges::remove(normalizedType, '_');
135 normalizedType.erase(std::ranges::begin(remove), normalizedType.end());
136
137 return std::format("{}_{}", normalizedType, sensorName);
138 }
139
140 inline std::pair<std::string, std::string>
splitSensorNameAndType(std::string_view sensorId)141 splitSensorNameAndType(std::string_view sensorId)
142 {
143 size_t index = sensorId.find('_');
144 if (index == std::string::npos)
145 {
146 return std::make_pair<std::string, std::string>("", "");
147 }
148 std::string sensorType{sensorId.substr(0, index)};
149 std::string sensorName{sensorId.substr(index + 1)};
150 // fan_pwm and fan_tach need special handling
151 if (sensorType == "fantach" || sensorType == "fanpwm")
152 {
153 sensorType.insert(3, 1, '_');
154 }
155 return std::make_pair(sensorType, sensorName);
156 }
157
158 namespace sensors
159 {
toReadingUnits(std::string_view sensorType)160 inline std::string_view toReadingUnits(std::string_view sensorType)
161 {
162 if (sensorType == "voltage")
163 {
164 return "V";
165 }
166 if (sensorType == "power")
167 {
168 return "W";
169 }
170 if (sensorType == "current")
171 {
172 return "A";
173 }
174 if (sensorType == "fan_tach")
175 {
176 return "RPM";
177 }
178 if (sensorType == "temperature")
179 {
180 return "Cel";
181 }
182 if (sensorType == "fan_pwm" || sensorType == "utilization" ||
183 sensorType == "humidity")
184 {
185 return "%";
186 }
187 if (sensorType == "altitude")
188 {
189 return "m";
190 }
191 if (sensorType == "airflow")
192 {
193 return "cft_i/min";
194 }
195 if (sensorType == "energy")
196 {
197 return "J";
198 }
199 return "";
200 }
201
toReadingType(std::string_view sensorType)202 inline sensor::ReadingType toReadingType(std::string_view sensorType)
203 {
204 if (sensorType == "voltage")
205 {
206 return sensor::ReadingType::Voltage;
207 }
208 if (sensorType == "power")
209 {
210 return sensor::ReadingType::Power;
211 }
212 if (sensorType == "current")
213 {
214 return sensor::ReadingType::Current;
215 }
216 if (sensorType == "fan_tach")
217 {
218 return sensor::ReadingType::Rotational;
219 }
220 if (sensorType == "temperature")
221 {
222 return sensor::ReadingType::Temperature;
223 }
224 if (sensorType == "fan_pwm" || sensorType == "utilization")
225 {
226 return sensor::ReadingType::Percent;
227 }
228 if (sensorType == "humidity")
229 {
230 return sensor::ReadingType::Humidity;
231 }
232 if (sensorType == "altitude")
233 {
234 return sensor::ReadingType::Altitude;
235 }
236 if (sensorType == "airflow")
237 {
238 return sensor::ReadingType::AirFlow;
239 }
240 if (sensorType == "energy")
241 {
242 return sensor::ReadingType::EnergyJoules;
243 }
244 return sensor::ReadingType::Invalid;
245 }
246
247 } // namespace sensors
248
249 /**
250 * @brief Returns the Redfish State value for the specified inventory item.
251 * @param inventoryItem D-Bus inventory item associated with a sensor.
252 * @param sensorAvailable Boolean representing if D-Bus sensor is marked as
253 * available.
254 * @return State value for inventory item.
255 */
getState(const InventoryItem * inventoryItem,const bool sensorAvailable)256 inline resource::State getState(const InventoryItem* inventoryItem,
257 const bool sensorAvailable)
258 {
259 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
260 {
261 return resource::State::Absent;
262 }
263
264 if (!sensorAvailable)
265 {
266 return resource::State::UnavailableOffline;
267 }
268
269 return resource::State::Enabled;
270 }
271
272 /**
273 * @brief Returns the Redfish Health value for the specified sensor.
274 * @param sensorJson Sensor JSON object.
275 * @param valuesDict Map of all sensor DBus values.
276 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
277 * be nullptr if no associated inventory item was found.
278 * @return Health value for sensor.
279 */
getHealth(nlohmann::json & sensorJson,const dbus::utility::DBusPropertiesMap & valuesDict,const InventoryItem * inventoryItem)280 inline std::string getHealth(nlohmann::json& sensorJson,
281 const dbus::utility::DBusPropertiesMap& valuesDict,
282 const InventoryItem* inventoryItem)
283 {
284 // Get current health value (if any) in the sensor JSON object. Some JSON
285 // objects contain multiple sensors (such as PowerSupplies). We want to set
286 // the overall health to be the most severe of any of the sensors.
287 std::string currentHealth;
288 auto statusIt = sensorJson.find("Status");
289 if (statusIt != sensorJson.end())
290 {
291 auto healthIt = statusIt->find("Health");
292 if (healthIt != statusIt->end())
293 {
294 std::string* health = healthIt->get_ptr<std::string*>();
295 if (health != nullptr)
296 {
297 currentHealth = *health;
298 }
299 }
300 }
301
302 // If current health in JSON object is already Critical, return that. This
303 // should override the sensor health, which might be less severe.
304 if (currentHealth == "Critical")
305 {
306 return "Critical";
307 }
308
309 const bool* criticalAlarmHigh = nullptr;
310 const bool* criticalAlarmLow = nullptr;
311 const bool* warningAlarmHigh = nullptr;
312 const bool* warningAlarmLow = nullptr;
313
314 const bool success = sdbusplus::unpackPropertiesNoThrow(
315 dbus_utils::UnpackErrorPrinter(), valuesDict, "CriticalAlarmHigh",
316 criticalAlarmHigh, "CriticalAlarmLow", criticalAlarmLow,
317 "WarningAlarmHigh", warningAlarmHigh, "WarningAlarmLow",
318 warningAlarmLow);
319
320 if (success)
321 {
322 // Check if sensor has critical threshold alarm
323 if ((criticalAlarmHigh != nullptr && *criticalAlarmHigh) ||
324 (criticalAlarmLow != nullptr && *criticalAlarmLow))
325 {
326 return "Critical";
327 }
328 }
329
330 // Check if associated inventory item is not functional
331 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
332 {
333 return "Critical";
334 }
335
336 // If current health in JSON object is already Warning, return that. This
337 // should override the sensor status, which might be less severe.
338 if (currentHealth == "Warning")
339 {
340 return "Warning";
341 }
342
343 if (success)
344 {
345 // Check if sensor has warning threshold alarm
346 if ((warningAlarmHigh != nullptr && *warningAlarmHigh) ||
347 (warningAlarmLow != nullptr && *warningAlarmLow))
348 {
349 return "Warning";
350 }
351 }
352
353 return "OK";
354 }
355
setLedState(nlohmann::json & sensorJson,const InventoryItem * inventoryItem)356 inline void setLedState(nlohmann::json& sensorJson,
357 const InventoryItem* inventoryItem)
358 {
359 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
360 {
361 switch (inventoryItem->ledState)
362 {
363 case LedState::OFF:
364 sensorJson["IndicatorLED"] = resource::IndicatorLED::Off;
365 break;
366 case LedState::ON:
367 sensorJson["IndicatorLED"] = resource::IndicatorLED::Lit;
368 break;
369 case LedState::BLINK:
370 sensorJson["IndicatorLED"] = resource::IndicatorLED::Blinking;
371 break;
372 default:
373 break;
374 }
375 }
376 }
377
378 /**
379 * @brief Builds a json sensor representation of a sensor.
380 * @param sensorName The name of the sensor to be built
381 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
382 * build
383 * @param chassisSubNode The subnode (thermal, sensor, etc) of the sensor
384 * @param propertiesDict A dictionary of the properties to build the sensor
385 * from.
386 * @param sensorJson The json object to fill
387 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
388 * be nullptr if no associated inventory item was found.
389 */
objectPropertiesToJson(std::string_view sensorName,std::string_view sensorType,ChassisSubNode chassisSubNode,const dbus::utility::DBusPropertiesMap & propertiesDict,nlohmann::json & sensorJson,InventoryItem * inventoryItem)390 inline void objectPropertiesToJson(
391 std::string_view sensorName, std::string_view sensorType,
392 ChassisSubNode chassisSubNode,
393 const dbus::utility::DBusPropertiesMap& propertiesDict,
394 nlohmann::json& sensorJson, InventoryItem* inventoryItem)
395 {
396 // Parameter to set to override the type we get from dbus, and force it to
397 // int, regardless of what is available. This is used for schemas like fan,
398 // that require integers, not floats.
399 bool forceToInt = false;
400
401 nlohmann::json::json_pointer unit("/Reading");
402
403 // This ChassisSubNode builds sensor excerpts
404 bool isExcerpt = isExcerptNode(chassisSubNode);
405
406 /* Sensor excerpts use different keys to reference the sensor. These are
407 * built by the caller.
408 * Additionally they don't include these additional properties.
409 */
410 if (!isExcerpt)
411 {
412 if (chassisSubNode == ChassisSubNode::sensorsNode)
413 {
414 std::string subNodeEscaped = getSensorId(sensorName, sensorType);
415 // For sensors in SensorCollection we set Id instead of MemberId,
416 // including power sensors.
417 sensorJson["Id"] = std::move(subNodeEscaped);
418
419 std::string sensorNameEs(sensorName);
420 std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' ');
421 sensorJson["Name"] = std::move(sensorNameEs);
422 }
423 else if (sensorType != "power")
424 {
425 // Set MemberId and Name for non-power sensors. For PowerSupplies
426 // and PowerControl, those properties have more general values
427 // because multiple sensors can be stored in the same JSON object.
428 std::string sensorNameEs(sensorName);
429 std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' ');
430 sensorJson["Name"] = std::move(sensorNameEs);
431 }
432
433 const bool* checkAvailable = nullptr;
434 bool available = true;
435 const bool success = sdbusplus::unpackPropertiesNoThrow(
436 dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available",
437 checkAvailable);
438 if (!success)
439 {
440 messages::internalError();
441 }
442 if (checkAvailable != nullptr)
443 {
444 available = *checkAvailable;
445 }
446
447 sensorJson["Status"]["State"] = getState(inventoryItem, available);
448 sensorJson["Status"]["Health"] =
449 getHealth(sensorJson, propertiesDict, inventoryItem);
450
451 if (chassisSubNode == ChassisSubNode::sensorsNode)
452 {
453 sensorJson["@odata.type"] = "#Sensor.v1_2_0.Sensor";
454
455 sensor::ReadingType readingType =
456 sensors::toReadingType(sensorType);
457 if (readingType == sensor::ReadingType::Invalid)
458 {
459 BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}",
460 sensorType);
461 }
462 else
463 {
464 sensorJson["ReadingType"] = readingType;
465 }
466
467 std::string_view readingUnits = sensors::toReadingUnits(sensorType);
468 if (readingUnits.empty())
469 {
470 BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}",
471 sensorType);
472 }
473 else
474 {
475 sensorJson["ReadingUnits"] = readingUnits;
476 }
477 }
478 else if (sensorType == "temperature")
479 {
480 unit = "/ReadingCelsius"_json_pointer;
481 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature";
482 // TODO(ed) Documentation says that path should be type fan_tach,
483 // implementation seems to implement fan
484 }
485 else if (sensorType == "fan" || sensorType == "fan_tach")
486 {
487 unit = "/Reading"_json_pointer;
488 sensorJson["ReadingUnits"] = thermal::ReadingUnits::RPM;
489 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan";
490 setLedState(sensorJson, inventoryItem);
491 forceToInt = true;
492 }
493 else if (sensorType == "fan_pwm")
494 {
495 unit = "/Reading"_json_pointer;
496 sensorJson["ReadingUnits"] = thermal::ReadingUnits::Percent;
497 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan";
498 setLedState(sensorJson, inventoryItem);
499 forceToInt = true;
500 }
501 else if (sensorType == "voltage")
502 {
503 unit = "/ReadingVolts"_json_pointer;
504 sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage";
505 }
506 else if (sensorType == "power")
507 {
508 std::string lower;
509 std::ranges::transform(sensorName, std::back_inserter(lower),
510 bmcweb::asciiToLower);
511 if (lower == "total_power")
512 {
513 sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl";
514 // Put multiple "sensors" into a single PowerControl, so have
515 // generic names for MemberId and Name. Follows Redfish mockup.
516 sensorJson["MemberId"] = "0";
517 sensorJson["Name"] = "Chassis Power Control";
518 unit = "/PowerConsumedWatts"_json_pointer;
519 }
520 else if (lower.find("input") != std::string::npos)
521 {
522 unit = "/PowerInputWatts"_json_pointer;
523 }
524 else
525 {
526 unit = "/PowerOutputWatts"_json_pointer;
527 }
528 }
529 else
530 {
531 BMCWEB_LOG_ERROR("Redfish cannot map object type for {}",
532 sensorName);
533 return;
534 }
535 }
536
537 // Map of dbus interface name, dbus property name and redfish property_name
538 std::vector<
539 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
540 properties;
541
542 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
543
544 if (!isExcerpt)
545 {
546 if (chassisSubNode == ChassisSubNode::sensorsNode)
547 {
548 properties.emplace_back(
549 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
550 "/Thresholds/UpperCaution/Reading"_json_pointer);
551 properties.emplace_back(
552 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
553 "/Thresholds/LowerCaution/Reading"_json_pointer);
554 properties.emplace_back(
555 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
556 "/Thresholds/UpperCritical/Reading"_json_pointer);
557 properties.emplace_back(
558 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
559 "/Thresholds/LowerCritical/Reading"_json_pointer);
560
561 /* Add additional properties specific to sensorType */
562 if (sensorType == "fan_tach")
563 {
564 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
565 "Value", "/SpeedRPM"_json_pointer);
566 }
567 }
568 else if (sensorType != "power")
569 {
570 properties.emplace_back(
571 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
572 "/UpperThresholdNonCritical"_json_pointer);
573 properties.emplace_back(
574 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
575 "/LowerThresholdNonCritical"_json_pointer);
576 properties.emplace_back(
577 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
578 "/UpperThresholdCritical"_json_pointer);
579 properties.emplace_back(
580 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
581 "/LowerThresholdCritical"_json_pointer);
582 }
583
584 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
585
586 if (chassisSubNode == ChassisSubNode::sensorsNode)
587 {
588 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
589 "MinValue",
590 "/ReadingRangeMin"_json_pointer);
591 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
592 "MaxValue",
593 "/ReadingRangeMax"_json_pointer);
594 properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy",
595 "Accuracy", "/Accuracy"_json_pointer);
596 }
597 else if (sensorType == "temperature")
598 {
599 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
600 "MinValue",
601 "/MinReadingRangeTemp"_json_pointer);
602 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
603 "MaxValue",
604 "/MaxReadingRangeTemp"_json_pointer);
605 }
606 else if (sensorType != "power")
607 {
608 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
609 "MinValue",
610 "/MinReadingRange"_json_pointer);
611 properties.emplace_back("xyz.openbmc_project.Sensor.Value",
612 "MaxValue",
613 "/MaxReadingRange"_json_pointer);
614 }
615 }
616
617 for (const std::tuple<const char*, const char*,
618 nlohmann::json::json_pointer>& p : properties)
619 {
620 for (const auto& [valueName, valueVariant] : propertiesDict)
621 {
622 if (valueName != std::get<1>(p))
623 {
624 continue;
625 }
626
627 // The property we want to set may be nested json, so use
628 // a json_pointer for easy indexing into the json structure.
629 const nlohmann::json::json_pointer& key = std::get<2>(p);
630
631 const double* doubleValue = std::get_if<double>(&valueVariant);
632 if (doubleValue == nullptr)
633 {
634 BMCWEB_LOG_ERROR("Got value interface that wasn't double");
635 continue;
636 }
637 if (!std::isfinite(*doubleValue))
638 {
639 if (valueName == "Value")
640 {
641 // Readings are allowed to be NAN for unavailable; coerce
642 // them to null in the json response.
643 sensorJson[key] = nullptr;
644 continue;
645 }
646 BMCWEB_LOG_WARNING("Sensor value for {} was unexpectedly {}",
647 valueName, *doubleValue);
648 continue;
649 }
650 if (forceToInt)
651 {
652 sensorJson[key] = static_cast<int64_t>(*doubleValue);
653 }
654 else
655 {
656 sensorJson[key] = *doubleValue;
657 }
658 }
659 }
660 }
661
662 /**
663 * @brief Builds a json sensor excerpt representation of a sensor.
664 *
665 * @details This is a wrapper function to provide consistent setting of
666 * "DataSourceUri" for sensor excerpts and filling of properties. Since sensor
667 * excerpts usually have just the D-Bus path for the sensor that is accepted
668 * and used to build "DataSourceUri".
669
670 * @param path The D-Bus path to the sensor to be built
671 * @param chassisId The Chassis Id for the sensor
672 * @param chassisSubNode The subnode (e.g. ThermalMetrics) of the sensor
673 * @param sensorTypeExpected The expected type of the sensor
674 * @param propertiesDict A dictionary of the properties to build the sensor
675 * from.
676 * @param sensorJson The json object to fill
677 * @returns True if sensorJson object filled. False on any error.
678 * Caller is responsible for handling error.
679 */
objectExcerptToJson(const std::string & path,const std::string_view chassisId,ChassisSubNode chassisSubNode,const std::optional<std::string> & sensorTypeExpected,const dbus::utility::DBusPropertiesMap & propertiesDict,nlohmann::json & sensorJson)680 inline bool objectExcerptToJson(
681 const std::string& path, const std::string_view chassisId,
682 ChassisSubNode chassisSubNode,
683 const std::optional<std::string>& sensorTypeExpected,
684 const dbus::utility::DBusPropertiesMap& propertiesDict,
685 nlohmann::json& sensorJson)
686 {
687 if (!isExcerptNode(chassisSubNode))
688 {
689 BMCWEB_LOG_DEBUG("{} is not a sensor excerpt",
690 chassisSubNodeToString(chassisSubNode));
691 return false;
692 }
693
694 sdbusplus::message::object_path sensorPath(path);
695 std::string sensorName = sensorPath.filename();
696 std::string sensorType = sensorPath.parent_path().filename();
697 if (sensorName.empty() || sensorType.empty())
698 {
699 BMCWEB_LOG_DEBUG("Invalid sensor path {}", path);
700 return false;
701 }
702
703 if (sensorTypeExpected && (sensorType != *sensorTypeExpected))
704 {
705 BMCWEB_LOG_DEBUG("{} is not expected type {}", path,
706 *sensorTypeExpected);
707 return false;
708 }
709
710 // Sensor excerpts use DataSourceUri to reference full sensor Redfish path
711 sensorJson["DataSourceUri"] =
712 boost::urls::format("/redfish/v1/Chassis/{}/Sensors/{}", chassisId,
713 getSensorId(sensorName, sensorType));
714
715 // Fill in sensor excerpt properties
716 objectPropertiesToJson(sensorName, sensorType, chassisSubNode,
717 propertiesDict, sensorJson, nullptr);
718
719 return true;
720 }
721
722 // Maps D-Bus: Service, SensorPath
723 using SensorServicePathMap = std::pair<std::string, std::string>;
724 using SensorServicePathList = std::vector<SensorServicePathMap>;
725
getAllSensorObjects(const std::string & associatedPath,const std::string & path,std::span<const std::string_view> interfaces,const int32_t depth,std::function<void (const boost::system::error_code & ec,SensorServicePathList &)> && callback)726 inline void getAllSensorObjects(
727 const std::string& associatedPath, const std::string& path,
728 std::span<const std::string_view> interfaces, const int32_t depth,
729 std::function<void(const boost::system::error_code& ec,
730 SensorServicePathList&)>&& callback)
731 {
732 sdbusplus::message::object_path endpointPath{associatedPath};
733 endpointPath /= "all_sensors";
734
735 dbus::utility::getAssociatedSubTree(
736 endpointPath, sdbusplus::message::object_path(path), depth, interfaces,
737 [callback = std::move(callback)](
738 const boost::system::error_code& ec,
739 const dbus::utility::MapperGetSubTreeResponse& subtree) {
740 SensorServicePathList sensorsServiceAndPath;
741
742 if (ec)
743 {
744 callback(ec, sensorsServiceAndPath);
745 return;
746 }
747
748 for (const auto& [sensorPath, serviceMaps] : subtree)
749 {
750 for (const auto& [service, mapInterfaces] : serviceMaps)
751 {
752 sensorsServiceAndPath.emplace_back(service, sensorPath);
753 }
754 }
755
756 callback(ec, sensorsServiceAndPath);
757 });
758 }
759
760 } // namespace sensor_utils
761 } // namespace redfish
762