xref: /openbmc/bmcweb/features/redfish/lib/sensors.hpp (revision 3174e4dfd3185c131a07371b4b5a5b40cf0e0bdb)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include "node.hpp"
19 
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <boost/algorithm/string/split.hpp>
22 #include <boost/container/flat_map.hpp>
23 #include <boost/range/algorithm/replace_copy_if.hpp>
24 #include <dbus_singleton.hpp>
25 #include <utils/json_utils.hpp>
26 
27 #include <cmath>
28 #include <variant>
29 
30 namespace redfish
31 {
32 
33 using GetSubTreeType = std::vector<
34     std::pair<std::string,
35               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36 
37 using SensorVariant =
38     std::variant<int64_t, double, uint32_t, bool, std::string>;
39 
40 using ManagedObjectsVectorType = std::vector<std::pair<
41     sdbusplus::message::object_path,
42     boost::container::flat_map<
43         std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
44 
45 namespace sensors
46 {
47 namespace node
48 {
49 static constexpr std::string_view power = "Power";
50 static constexpr std::string_view sensors = "Sensors";
51 static constexpr std::string_view thermal = "Thermal";
52 } // namespace node
53 
54 namespace dbus
55 {
56 static const boost::container::flat_map<std::string_view,
57                                         std::vector<const char*>>
58     types = {{node::power,
59               {"/xyz/openbmc_project/sensors/voltage",
60                "/xyz/openbmc_project/sensors/power"}},
61              {node::sensors,
62               {"/xyz/openbmc_project/sensors/power",
63                "/xyz/openbmc_project/sensors/current",
64                "/xyz/openbmc_project/sensors/utilization"}},
65              {node::thermal,
66               {"/xyz/openbmc_project/sensors/fan_tach",
67                "/xyz/openbmc_project/sensors/temperature",
68                "/xyz/openbmc_project/sensors/fan_pwm"}}};
69 }
70 } // namespace sensors
71 
72 /**
73  * SensorsAsyncResp
74  * Gathers data needed for response processing after async calls are done
75  */
76 class SensorsAsyncResp
77 {
78   public:
79     using DataCompleteCb = std::function<void(
80         const boost::beast::http::status status,
81         const boost::container::flat_map<std::string, std::string>& uriToDbus)>;
82 
83     struct SensorData
84     {
85         const std::string name;
86         std::string uri;
87         const std::string valueKey;
88         const std::string dbusPath;
89     };
90 
91     SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
92                      const std::vector<const char*> typesIn,
93                      const std::string_view& subNode) :
94         res(response),
95         chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
96     {}
97 
98     // Store extra data about sensor mapping and return it in callback
99     SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
100                      const std::vector<const char*> typesIn,
101                      const std::string_view& subNode,
102                      DataCompleteCb&& creationComplete) :
103         res(response),
104         chassisId(chassisIdIn), types(typesIn),
105         chassisSubNode(subNode), metadata{std::vector<SensorData>()},
106         dataComplete{std::move(creationComplete)}
107     {}
108 
109     ~SensorsAsyncResp()
110     {
111         if (res.result() == boost::beast::http::status::internal_server_error)
112         {
113             // Reset the json object to clear out any data that made it in
114             // before the error happened todo(ed) handle error condition with
115             // proper code
116             res.jsonValue = nlohmann::json::object();
117         }
118 
119         if (dataComplete && metadata)
120         {
121             boost::container::flat_map<std::string, std::string> map;
122             if (res.result() == boost::beast::http::status::ok)
123             {
124                 for (auto& sensor : *metadata)
125                 {
126                     map.insert(std::make_pair(sensor.uri + sensor.valueKey,
127                                               sensor.dbusPath));
128                 }
129             }
130             dataComplete(res.result(), map);
131         }
132 
133         res.end();
134     }
135 
136     void addMetadata(const nlohmann::json& sensorObject,
137                      const std::string& valueKey, const std::string& dbusPath)
138     {
139         if (metadata)
140         {
141             metadata->emplace_back(SensorData{sensorObject["Name"],
142                                               sensorObject["@odata.id"],
143                                               valueKey, dbusPath});
144         }
145     }
146 
147     void updateUri(const std::string& name, const std::string& uri)
148     {
149         if (metadata)
150         {
151             for (auto& sensor : *metadata)
152             {
153                 if (sensor.name == name)
154                 {
155                     sensor.uri = uri;
156                 }
157             }
158         }
159     }
160 
161     crow::Response& res;
162     const std::string chassisId;
163     const std::vector<const char*> types;
164     const std::string chassisSubNode;
165 
166   private:
167     std::optional<std::vector<SensorData>> metadata;
168     DataCompleteCb dataComplete;
169 };
170 
171 /**
172  * Possible states for physical inventory leds
173  */
174 enum class LedState
175 {
176     OFF,
177     ON,
178     BLINK,
179     UNKNOWN
180 };
181 
182 /**
183  * D-Bus inventory item associated with one or more sensors.
184  */
185 class InventoryItem
186 {
187   public:
188     InventoryItem(const std::string& objPath) :
189         objectPath(objPath), name(), isPresent(true), isFunctional(true),
190         isPowerSupply(false), powerSupplyEfficiencyPercent(-1), manufacturer(),
191         model(), partNumber(), serialNumber(), sensors(), ledObjectPath(""),
192         ledState(LedState::UNKNOWN)
193     {
194         // Set inventory item name to last node of object path
195         auto pos = objectPath.rfind('/');
196         if ((pos != std::string::npos) && ((pos + 1) < objectPath.size()))
197         {
198             name = objectPath.substr(pos + 1);
199         }
200     }
201 
202     std::string objectPath;
203     std::string name;
204     bool isPresent;
205     bool isFunctional;
206     bool isPowerSupply;
207     int powerSupplyEfficiencyPercent;
208     std::string manufacturer;
209     std::string model;
210     std::string partNumber;
211     std::string serialNumber;
212     std::set<std::string> sensors;
213     std::string ledObjectPath;
214     LedState ledState;
215 };
216 
217 /**
218  * @brief Get objects with connection necessary for sensors
219  * @param SensorsAsyncResp Pointer to object holding response data
220  * @param sensorNames Sensors retrieved from chassis
221  * @param callback Callback for processing gathered connections
222  */
223 template <typename Callback>
224 void getObjectsWithConnection(
225     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
226     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
227     Callback&& callback)
228 {
229     BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
230     const std::string path = "/xyz/openbmc_project/sensors";
231     const std::array<std::string, 1> interfaces = {
232         "xyz.openbmc_project.Sensor.Value"};
233 
234     // Response handler for parsing objects subtree
235     auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
236                         sensorNames](const boost::system::error_code ec,
237                                      const GetSubTreeType& subtree) {
238         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
239         if (ec)
240         {
241             messages::internalError(SensorsAsyncResp->res);
242             BMCWEB_LOG_ERROR
243                 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
244             return;
245         }
246 
247         BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
248 
249         // Make unique list of connections only for requested sensor types and
250         // found in the chassis
251         boost::container::flat_set<std::string> connections;
252         std::set<std::pair<std::string, std::string>> objectsWithConnection;
253         // Intrinsic to avoid malloc.  Most systems will have < 8 sensor
254         // producers
255         connections.reserve(8);
256 
257         BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
258         for (const std::string& tsensor : *sensorNames)
259         {
260             BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
261         }
262 
263         for (const std::pair<
264                  std::string,
265                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
266                  object : subtree)
267         {
268             if (sensorNames->find(object.first) != sensorNames->end())
269             {
270                 for (const std::pair<std::string, std::vector<std::string>>&
271                          objData : object.second)
272                 {
273                     BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
274                     connections.insert(objData.first);
275                     objectsWithConnection.insert(
276                         std::make_pair(object.first, objData.first));
277                 }
278             }
279         }
280         BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
281         callback(std::move(connections), std::move(objectsWithConnection));
282         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
283     };
284     // Make call to ObjectMapper to find all sensors objects
285     crow::connections::systemBus->async_method_call(
286         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
287         "/xyz/openbmc_project/object_mapper",
288         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
289     BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
290 }
291 
292 /**
293  * @brief Create connections necessary for sensors
294  * @param SensorsAsyncResp Pointer to object holding response data
295  * @param sensorNames Sensors retrieved from chassis
296  * @param callback Callback for processing gathered connections
297  */
298 template <typename Callback>
299 void getConnections(
300     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
301     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
302     Callback&& callback)
303 {
304     auto objectsWithConnectionCb =
305         [callback](const boost::container::flat_set<std::string>& connections,
306                    const std::set<std::pair<std::string, std::string>>&
307                    /*objectsWithConnection*/) { callback(connections); };
308     getObjectsWithConnection(SensorsAsyncResp, sensorNames,
309                              std::move(objectsWithConnectionCb));
310 }
311 
312 /**
313  * @brief Shrinks the list of sensors for processing
314  * @param SensorsAysncResp  The class holding the Redfish response
315  * @param allSensors  A list of all the sensors associated to the
316  * chassis element (i.e. baseboard, front panel, etc...)
317  * @param activeSensors A list that is a reduction of the incoming
318  * allSensors list.  Eliminate Thermal sensors when a Power request is
319  * made, and eliminate Power sensors when a Thermal request is made.
320  */
321 inline void reduceSensorList(
322     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
323     const std::vector<std::string>* allSensors,
324     std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
325 {
326     if (SensorsAsyncResp == nullptr)
327     {
328         return;
329     }
330     if ((allSensors == nullptr) || (activeSensors == nullptr))
331     {
332         messages::resourceNotFound(
333             SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
334             SensorsAsyncResp->chassisSubNode == sensors::node::thermal
335                 ? "Temperatures"
336                 : "Voltages");
337 
338         return;
339     }
340     if (allSensors->empty())
341     {
342         // Nothing to do, the activeSensors object is also empty
343         return;
344     }
345 
346     for (const char* type : SensorsAsyncResp->types)
347     {
348         for (const std::string& sensor : *allSensors)
349         {
350             if (boost::starts_with(sensor, type))
351             {
352                 activeSensors->emplace(sensor);
353             }
354         }
355     }
356 }
357 
358 /**
359  * @brief Retrieves valid chassis path
360  * @param asyncResp   Pointer to object holding response data
361  * @param callback  Callback for next step to get valid chassis path
362  */
363 template <typename Callback>
364 void getValidChassisPath(std::shared_ptr<SensorsAsyncResp> asyncResp,
365                          Callback&& callback)
366 {
367     BMCWEB_LOG_DEBUG << "checkChassisId enter";
368     const std::array<const char*, 2> interfaces = {
369         "xyz.openbmc_project.Inventory.Item.Board",
370         "xyz.openbmc_project.Inventory.Item.Chassis"};
371 
372     auto respHandler =
373         [callback{std::move(callback)},
374          asyncResp](const boost::system::error_code ec,
375                     const std::vector<std::string>& chassisPaths) mutable {
376             BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
377             if (ec)
378             {
379                 BMCWEB_LOG_ERROR
380                     << "getValidChassisPath respHandler DBUS error: " << ec;
381                 messages::internalError(asyncResp->res);
382                 return;
383             }
384 
385             std::optional<std::string> chassisPath;
386             std::string chassisName;
387             for (const std::string& chassis : chassisPaths)
388             {
389                 std::size_t lastPos = chassis.rfind("/");
390                 if (lastPos == std::string::npos)
391                 {
392                     BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
393                     continue;
394                 }
395                 chassisName = chassis.substr(lastPos + 1);
396                 if (chassisName == asyncResp->chassisId)
397                 {
398                     chassisPath = chassis;
399                     break;
400                 }
401             }
402             callback(chassisPath);
403         };
404 
405     // Get the Chassis Collection
406     crow::connections::systemBus->async_method_call(
407         respHandler, "xyz.openbmc_project.ObjectMapper",
408         "/xyz/openbmc_project/object_mapper",
409         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
410         "/xyz/openbmc_project/inventory", 0, interfaces);
411     BMCWEB_LOG_DEBUG << "checkChassisId exit";
412 }
413 
414 /**
415  * @brief Retrieves requested chassis sensors and redundancy data from DBus .
416  * @param SensorsAsyncResp   Pointer to object holding response data
417  * @param callback  Callback for next step in gathered sensor processing
418  */
419 template <typename Callback>
420 void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
421                 Callback&& callback)
422 {
423     BMCWEB_LOG_DEBUG << "getChassis enter";
424     const std::array<const char*, 2> interfaces = {
425         "xyz.openbmc_project.Inventory.Item.Board",
426         "xyz.openbmc_project.Inventory.Item.Chassis"};
427     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
428                            const boost::system::error_code ec,
429                            const std::vector<std::string>& chassisPaths) {
430         BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
431         if (ec)
432         {
433             BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
434             messages::internalError(sensorsAsyncResp->res);
435             return;
436         }
437 
438         const std::string* chassisPath = nullptr;
439         std::string chassisName;
440         for (const std::string& chassis : chassisPaths)
441         {
442             std::size_t lastPos = chassis.rfind("/");
443             if (lastPos == std::string::npos)
444             {
445                 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
446                 continue;
447             }
448             chassisName = chassis.substr(lastPos + 1);
449             if (chassisName == sensorsAsyncResp->chassisId)
450             {
451                 chassisPath = &chassis;
452                 break;
453             }
454         }
455         if (chassisPath == nullptr)
456         {
457             messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
458                                        sensorsAsyncResp->chassisId);
459             return;
460         }
461 
462         const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
463         if (chassisSubNode == sensors::node::power)
464         {
465             sensorsAsyncResp->res.jsonValue["@odata.type"] =
466                 "#Power.v1_5_2.Power";
467         }
468         else if (chassisSubNode == sensors::node::thermal)
469         {
470             sensorsAsyncResp->res.jsonValue["@odata.type"] =
471                 "#Thermal.v1_4_0.Thermal";
472             sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
473             sensorsAsyncResp->res.jsonValue["Temperatures"] =
474                 nlohmann::json::array();
475         }
476         else if (chassisSubNode == sensors::node::sensors)
477         {
478             sensorsAsyncResp->res.jsonValue["@odata.type"] =
479                 "#SensorCollection.SensorCollection";
480             sensorsAsyncResp->res.jsonValue["Description"] =
481                 "Collection of Sensors for this Chassis";
482             sensorsAsyncResp->res.jsonValue["Members"] =
483                 nlohmann::json::array();
484             sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
485         }
486 
487         if (chassisSubNode != sensors::node::sensors)
488         {
489             sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
490         }
491 
492         sensorsAsyncResp->res.jsonValue["@odata.id"] =
493             "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
494             chassisSubNode;
495         sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
496 
497         // Get the list of all sensors for this Chassis element
498         std::string sensorPath = *chassisPath + "/all_sensors";
499         crow::connections::systemBus->async_method_call(
500             [sensorsAsyncResp, callback{std::move(callback)}](
501                 const boost::system::error_code& e,
502                 const std::variant<std::vector<std::string>>&
503                     variantEndpoints) {
504                 if (e)
505                 {
506                     if (e.value() != EBADR)
507                     {
508                         messages::internalError(sensorsAsyncResp->res);
509                         return;
510                     }
511                 }
512                 const std::vector<std::string>* nodeSensorList =
513                     std::get_if<std::vector<std::string>>(&(variantEndpoints));
514                 if (nodeSensorList == nullptr)
515                 {
516                     messages::resourceNotFound(
517                         sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
518                         sensorsAsyncResp->chassisSubNode ==
519                                 sensors::node::thermal
520                             ? "Temperatures"
521                             : sensorsAsyncResp->chassisSubNode ==
522                                       sensors::node::power
523                                   ? "Voltages"
524                                   : "Sensors");
525                     return;
526                 }
527                 const std::shared_ptr<boost::container::flat_set<std::string>>
528                     culledSensorList = std::make_shared<
529                         boost::container::flat_set<std::string>>();
530                 reduceSensorList(sensorsAsyncResp, nodeSensorList,
531                                  culledSensorList);
532                 callback(culledSensorList);
533             },
534             "xyz.openbmc_project.ObjectMapper", sensorPath,
535             "org.freedesktop.DBus.Properties", "Get",
536             "xyz.openbmc_project.Association", "endpoints");
537     };
538 
539     // Get the Chassis Collection
540     crow::connections::systemBus->async_method_call(
541         respHandler, "xyz.openbmc_project.ObjectMapper",
542         "/xyz/openbmc_project/object_mapper",
543         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
544         "/xyz/openbmc_project/inventory", 0, interfaces);
545     BMCWEB_LOG_DEBUG << "getChassis exit";
546 }
547 
548 /**
549  * @brief Finds all DBus object paths that implement ObjectManager.
550  *
551  * Creates a mapping from the associated connection name to the object path.
552  *
553  * Finds the object paths asynchronously.  Invokes callback when information has
554  * been obtained.
555  *
556  * The callback must have the following signature:
557  *   @code
558  *   callback(std::shared_ptr<boost::container::flat_map<std::string,
559  *                std::string>> objectMgrPaths)
560  *   @endcode
561  *
562  * @param sensorsAsyncResp Pointer to object holding response data.
563  * @param callback Callback to invoke when object paths obtained.
564  */
565 template <typename Callback>
566 void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
567                            Callback&& callback)
568 {
569     BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
570     const std::array<std::string, 1> interfaces = {
571         "org.freedesktop.DBus.ObjectManager"};
572 
573     // Response handler for GetSubTree DBus method
574     auto respHandler = [callback{std::move(callback)},
575                         SensorsAsyncResp](const boost::system::error_code ec,
576                                           const GetSubTreeType& subtree) {
577         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
578         if (ec)
579         {
580             messages::internalError(SensorsAsyncResp->res);
581             BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
582                              << ec;
583             return;
584         }
585 
586         // Loop over returned object paths
587         std::shared_ptr<boost::container::flat_map<std::string, std::string>>
588             objectMgrPaths = std::make_shared<
589                 boost::container::flat_map<std::string, std::string>>();
590         for (const std::pair<
591                  std::string,
592                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
593                  object : subtree)
594         {
595             // Loop over connections for current object path
596             const std::string& objectPath = object.first;
597             for (const std::pair<std::string, std::vector<std::string>>&
598                      objData : object.second)
599             {
600                 // Add mapping from connection to object path
601                 const std::string& connection = objData.first;
602                 (*objectMgrPaths)[connection] = objectPath;
603                 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
604                                  << objectPath;
605             }
606         }
607         callback(objectMgrPaths);
608         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
609     };
610 
611     // Query mapper for all DBus object paths that implement ObjectManager
612     crow::connections::systemBus->async_method_call(
613         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
614         "/xyz/openbmc_project/object_mapper",
615         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
616     BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
617 }
618 
619 /**
620  * @brief Returns the Redfish State value for the specified inventory item.
621  * @param inventoryItem D-Bus inventory item associated with a sensor.
622  * @return State value for inventory item.
623  */
624 inline std::string getState(const InventoryItem* inventoryItem)
625 {
626     if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
627     {
628         return "Absent";
629     }
630 
631     return "Enabled";
632 }
633 
634 /**
635  * @brief Returns the Redfish Health value for the specified sensor.
636  * @param sensorJson Sensor JSON object.
637  * @param interfacesDict Map of all sensor interfaces.
638  * @param inventoryItem D-Bus inventory item associated with the sensor.  Will
639  * be nullptr if no associated inventory item was found.
640  * @return Health value for sensor.
641  */
642 inline std::string getHealth(
643     nlohmann::json& sensorJson,
644     const boost::container::flat_map<
645         std::string, boost::container::flat_map<std::string, SensorVariant>>&
646         interfacesDict,
647     const InventoryItem* inventoryItem)
648 {
649     // Get current health value (if any) in the sensor JSON object.  Some JSON
650     // objects contain multiple sensors (such as PowerSupplies).  We want to set
651     // the overall health to be the most severe of any of the sensors.
652     std::string currentHealth;
653     auto statusIt = sensorJson.find("Status");
654     if (statusIt != sensorJson.end())
655     {
656         auto healthIt = statusIt->find("Health");
657         if (healthIt != statusIt->end())
658         {
659             std::string* health = healthIt->get_ptr<std::string*>();
660             if (health != nullptr)
661             {
662                 currentHealth = *health;
663             }
664         }
665     }
666 
667     // If current health in JSON object is already Critical, return that.  This
668     // should override the sensor health, which might be less severe.
669     if (currentHealth == "Critical")
670     {
671         return "Critical";
672     }
673 
674     // Check if sensor has critical threshold alarm
675     auto criticalThresholdIt =
676         interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
677     if (criticalThresholdIt != interfacesDict.end())
678     {
679         auto thresholdHighIt =
680             criticalThresholdIt->second.find("CriticalAlarmHigh");
681         auto thresholdLowIt =
682             criticalThresholdIt->second.find("CriticalAlarmLow");
683         if (thresholdHighIt != criticalThresholdIt->second.end())
684         {
685             const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
686             if (asserted == nullptr)
687             {
688                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
689             }
690             else if (*asserted)
691             {
692                 return "Critical";
693             }
694         }
695         if (thresholdLowIt != criticalThresholdIt->second.end())
696         {
697             const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
698             if (asserted == nullptr)
699             {
700                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
701             }
702             else if (*asserted)
703             {
704                 return "Critical";
705             }
706         }
707     }
708 
709     // Check if associated inventory item is not functional
710     if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
711     {
712         return "Critical";
713     }
714 
715     // If current health in JSON object is already Warning, return that.  This
716     // should override the sensor status, which might be less severe.
717     if (currentHealth == "Warning")
718     {
719         return "Warning";
720     }
721 
722     // Check if sensor has warning threshold alarm
723     auto warningThresholdIt =
724         interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
725     if (warningThresholdIt != interfacesDict.end())
726     {
727         auto thresholdHighIt =
728             warningThresholdIt->second.find("WarningAlarmHigh");
729         auto thresholdLowIt =
730             warningThresholdIt->second.find("WarningAlarmLow");
731         if (thresholdHighIt != warningThresholdIt->second.end())
732         {
733             const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
734             if (asserted == nullptr)
735             {
736                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
737             }
738             else if (*asserted)
739             {
740                 return "Warning";
741             }
742         }
743         if (thresholdLowIt != warningThresholdIt->second.end())
744         {
745             const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
746             if (asserted == nullptr)
747             {
748                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
749             }
750             else if (*asserted)
751             {
752                 return "Warning";
753             }
754         }
755     }
756 
757     return "OK";
758 }
759 
760 inline void setLedState(nlohmann::json& sensorJson,
761                         const InventoryItem* inventoryItem)
762 {
763     if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
764     {
765         switch (inventoryItem->ledState)
766         {
767             case LedState::OFF:
768                 sensorJson["IndicatorLED"] = "Off";
769                 break;
770             case LedState::ON:
771                 sensorJson["IndicatorLED"] = "Lit";
772                 break;
773             case LedState::BLINK:
774                 sensorJson["IndicatorLED"] = "Blinking";
775                 break;
776             case LedState::UNKNOWN:
777                 break;
778         }
779     }
780 }
781 
782 /**
783  * @brief Builds a json sensor representation of a sensor.
784  * @param sensorName  The name of the sensor to be built
785  * @param sensorType  The type (temperature, fan_tach, etc) of the sensor to
786  * build
787  * @param sensorsAsyncResp  Sensor metadata
788  * @param interfacesDict  A dictionary of the interfaces and properties of said
789  * interfaces to be built from
790  * @param sensor_json  The json object to fill
791  * @param inventoryItem D-Bus inventory item associated with the sensor.  Will
792  * be nullptr if no associated inventory item was found.
793  */
794 inline void objectInterfacesToJson(
795     const std::string& sensorName, const std::string& sensorType,
796     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
797     const boost::container::flat_map<
798         std::string, boost::container::flat_map<std::string, SensorVariant>>&
799         interfacesDict,
800     nlohmann::json& sensor_json, InventoryItem* inventoryItem)
801 {
802     // We need a value interface before we can do anything with it
803     auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
804     if (valueIt == interfacesDict.end())
805     {
806         BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
807         return;
808     }
809 
810     // Assume values exist as is (10^0 == 1) if no scale exists
811     int64_t scaleMultiplier = 0;
812 
813     auto scaleIt = valueIt->second.find("Scale");
814     // If a scale exists, pull value as int64, and use the scaling.
815     if (scaleIt != valueIt->second.end())
816     {
817         const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
818         if (int64Value != nullptr)
819         {
820             scaleMultiplier = *int64Value;
821         }
822     }
823 
824     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
825     {
826         // For sensors in SensorCollection we set Id instead of MemberId,
827         // including power sensors.
828         sensor_json["Id"] = sensorName;
829         sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
830     }
831     else if (sensorType != "power")
832     {
833         // Set MemberId and Name for non-power sensors.  For PowerSupplies and
834         // PowerControl, those properties have more general values because
835         // multiple sensors can be stored in the same JSON object.
836         sensor_json["MemberId"] = sensorName;
837         sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
838     }
839 
840     sensor_json["Status"]["State"] = getState(inventoryItem);
841     sensor_json["Status"]["Health"] =
842         getHealth(sensor_json, interfacesDict, inventoryItem);
843 
844     // Parameter to set to override the type we get from dbus, and force it to
845     // int, regardless of what is available.  This is used for schemas like fan,
846     // that require integers, not floats.
847     bool forceToInt = false;
848 
849     nlohmann::json::json_pointer unit("/Reading");
850     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
851     {
852         sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
853         if (sensorType == "power")
854         {
855             sensor_json["ReadingUnits"] = "Watts";
856         }
857         else if (sensorType == "current")
858         {
859             sensor_json["ReadingUnits"] = "Amperes";
860         }
861         else if (sensorType == "utilization")
862         {
863             sensor_json["ReadingUnits"] = "Percent";
864         }
865     }
866     else if (sensorType == "temperature")
867     {
868         unit = "/ReadingCelsius"_json_pointer;
869         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
870         // TODO(ed) Documentation says that path should be type fan_tach,
871         // implementation seems to implement fan
872     }
873     else if (sensorType == "fan" || sensorType == "fan_tach")
874     {
875         unit = "/Reading"_json_pointer;
876         sensor_json["ReadingUnits"] = "RPM";
877         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
878         setLedState(sensor_json, inventoryItem);
879         forceToInt = true;
880     }
881     else if (sensorType == "fan_pwm")
882     {
883         unit = "/Reading"_json_pointer;
884         sensor_json["ReadingUnits"] = "Percent";
885         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
886         setLedState(sensor_json, inventoryItem);
887         forceToInt = true;
888     }
889     else if (sensorType == "voltage")
890     {
891         unit = "/ReadingVolts"_json_pointer;
892         sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
893     }
894     else if (sensorType == "power")
895     {
896         std::string sensorNameLower =
897             boost::algorithm::to_lower_copy(sensorName);
898 
899         if (!sensorName.compare("total_power"))
900         {
901             sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
902             // Put multiple "sensors" into a single PowerControl, so have
903             // generic names for MemberId and Name. Follows Redfish mockup.
904             sensor_json["MemberId"] = "0";
905             sensor_json["Name"] = "Chassis Power Control";
906             unit = "/PowerConsumedWatts"_json_pointer;
907         }
908         else if (sensorNameLower.find("input") != std::string::npos)
909         {
910             unit = "/PowerInputWatts"_json_pointer;
911         }
912         else
913         {
914             unit = "/PowerOutputWatts"_json_pointer;
915         }
916     }
917     else
918     {
919         BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
920         return;
921     }
922     // Map of dbus interface name, dbus property name and redfish property_name
923     std::vector<
924         std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
925         properties;
926     properties.reserve(7);
927 
928     properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
929 
930     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
931     {
932         properties.emplace_back(
933             "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
934             "/Thresholds/UpperCaution/Reading"_json_pointer);
935         properties.emplace_back(
936             "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
937             "/Thresholds/LowerCaution/Reading"_json_pointer);
938         properties.emplace_back(
939             "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
940             "/Thresholds/UpperCritical/Reading"_json_pointer);
941         properties.emplace_back(
942             "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
943             "/Thresholds/LowerCritical/Reading"_json_pointer);
944     }
945     else if (sensorType != "power")
946     {
947         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
948                                 "WarningHigh",
949                                 "/UpperThresholdNonCritical"_json_pointer);
950         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
951                                 "WarningLow",
952                                 "/LowerThresholdNonCritical"_json_pointer);
953         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
954                                 "CriticalHigh",
955                                 "/UpperThresholdCritical"_json_pointer);
956         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
957                                 "CriticalLow",
958                                 "/LowerThresholdCritical"_json_pointer);
959     }
960 
961     // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
962 
963     if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
964     {
965         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
966                                 "/ReadingRangeMin"_json_pointer);
967         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
968                                 "/ReadingRangeMax"_json_pointer);
969     }
970     else if (sensorType == "temperature")
971     {
972         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
973                                 "/MinReadingRangeTemp"_json_pointer);
974         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
975                                 "/MaxReadingRangeTemp"_json_pointer);
976     }
977     else if (sensorType != "power")
978     {
979         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
980                                 "/MinReadingRange"_json_pointer);
981         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
982                                 "/MaxReadingRange"_json_pointer);
983     }
984 
985     for (const std::tuple<const char*, const char*,
986                           nlohmann::json::json_pointer>& p : properties)
987     {
988         auto interfaceProperties = interfacesDict.find(std::get<0>(p));
989         if (interfaceProperties != interfacesDict.end())
990         {
991             auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
992             if (thisValueIt != interfaceProperties->second.end())
993             {
994                 const SensorVariant& valueVariant = thisValueIt->second;
995 
996                 // The property we want to set may be nested json, so use
997                 // a json_pointer for easy indexing into the json structure.
998                 const nlohmann::json::json_pointer& key = std::get<2>(p);
999 
1000                 // Attempt to pull the int64 directly
1001                 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
1002 
1003                 const double* doubleValue = std::get_if<double>(&valueVariant);
1004                 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
1005                 double temp = 0.0;
1006                 if (int64Value != nullptr)
1007                 {
1008                     temp = static_cast<double>(*int64Value);
1009                 }
1010                 else if (doubleValue != nullptr)
1011                 {
1012                     temp = *doubleValue;
1013                 }
1014                 else if (uValue != nullptr)
1015                 {
1016                     temp = *uValue;
1017                 }
1018                 else
1019                 {
1020                     BMCWEB_LOG_ERROR
1021                         << "Got value interface that wasn't int or double";
1022                     continue;
1023                 }
1024                 temp = temp * std::pow(10, scaleMultiplier);
1025                 if (forceToInt)
1026                 {
1027                     sensor_json[key] = static_cast<int64_t>(temp);
1028                 }
1029                 else
1030                 {
1031                     sensor_json[key] = temp;
1032                 }
1033             }
1034         }
1035     }
1036 
1037     sensorsAsyncResp->addMetadata(sensor_json, unit.to_string(),
1038                                   "/xyz/openbmc_project/sensors/" + sensorType +
1039                                       "/" + sensorName);
1040 
1041     BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
1042 }
1043 
1044 inline void
1045     populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
1046 {
1047     crow::connections::systemBus->async_method_call(
1048         [sensorsAsyncResp](const boost::system::error_code ec,
1049                            const GetSubTreeType& resp) {
1050             if (ec)
1051             {
1052                 return; // don't have to have this interface
1053             }
1054             for (const std::pair<std::string,
1055                                  std::vector<std::pair<
1056                                      std::string, std::vector<std::string>>>>&
1057                      pathPair : resp)
1058             {
1059                 const std::string& path = pathPair.first;
1060                 const std::vector<
1061                     std::pair<std::string, std::vector<std::string>>>& objDict =
1062                     pathPair.second;
1063                 if (objDict.empty())
1064                 {
1065                     continue; // this should be impossible
1066                 }
1067 
1068                 const std::string& owner = objDict.begin()->first;
1069                 crow::connections::systemBus->async_method_call(
1070                     [path, owner,
1071                      sensorsAsyncResp](const boost::system::error_code e,
1072                                        std::variant<std::vector<std::string>>
1073                                            variantEndpoints) {
1074                         if (e)
1075                         {
1076                             return; // if they don't have an association we
1077                                     // can't tell what chassis is
1078                         }
1079                         // verify part of the right chassis
1080                         auto endpoints = std::get_if<std::vector<std::string>>(
1081                             &variantEndpoints);
1082 
1083                         if (endpoints == nullptr)
1084                         {
1085                             BMCWEB_LOG_ERROR << "Invalid association interface";
1086                             messages::internalError(sensorsAsyncResp->res);
1087                             return;
1088                         }
1089 
1090                         auto found = std::find_if(
1091                             endpoints->begin(), endpoints->end(),
1092                             [sensorsAsyncResp](const std::string& entry) {
1093                                 return entry.find(
1094                                            sensorsAsyncResp->chassisId) !=
1095                                        std::string::npos;
1096                             });
1097 
1098                         if (found == endpoints->end())
1099                         {
1100                             return;
1101                         }
1102                         crow::connections::systemBus->async_method_call(
1103                             [path, sensorsAsyncResp](
1104                                 const boost::system::error_code& err,
1105                                 const boost::container::flat_map<
1106                                     std::string,
1107                                     std::variant<uint8_t,
1108                                                  std::vector<std::string>,
1109                                                  std::string>>& ret) {
1110                                 if (err)
1111                                 {
1112                                     return; // don't have to have this
1113                                             // interface
1114                                 }
1115                                 auto findFailures = ret.find("AllowedFailures");
1116                                 auto findCollection = ret.find("Collection");
1117                                 auto findStatus = ret.find("Status");
1118 
1119                                 if (findFailures == ret.end() ||
1120                                     findCollection == ret.end() ||
1121                                     findStatus == ret.end())
1122                                 {
1123                                     BMCWEB_LOG_ERROR
1124                                         << "Invalid redundancy interface";
1125                                     messages::internalError(
1126                                         sensorsAsyncResp->res);
1127                                     return;
1128                                 }
1129 
1130                                 auto allowedFailures = std::get_if<uint8_t>(
1131                                     &(findFailures->second));
1132                                 auto collection =
1133                                     std::get_if<std::vector<std::string>>(
1134                                         &(findCollection->second));
1135                                 auto status = std::get_if<std::string>(
1136                                     &(findStatus->second));
1137 
1138                                 if (allowedFailures == nullptr ||
1139                                     collection == nullptr || status == nullptr)
1140                                 {
1141 
1142                                     BMCWEB_LOG_ERROR
1143                                         << "Invalid redundancy interface "
1144                                            "types";
1145                                     messages::internalError(
1146                                         sensorsAsyncResp->res);
1147                                     return;
1148                                 }
1149                                 size_t lastSlash = path.rfind("/");
1150                                 if (lastSlash == std::string::npos)
1151                                 {
1152                                     // this should be impossible
1153                                     messages::internalError(
1154                                         sensorsAsyncResp->res);
1155                                     return;
1156                                 }
1157                                 std::string name = path.substr(lastSlash + 1);
1158                                 std::replace(name.begin(), name.end(), '_',
1159                                              ' ');
1160 
1161                                 std::string health;
1162 
1163                                 if (boost::ends_with(*status, "Full"))
1164                                 {
1165                                     health = "OK";
1166                                 }
1167                                 else if (boost::ends_with(*status, "Degraded"))
1168                                 {
1169                                     health = "Warning";
1170                                 }
1171                                 else
1172                                 {
1173                                     health = "Critical";
1174                                 }
1175                                 std::vector<nlohmann::json> redfishCollection;
1176                                 const auto& fanRedfish =
1177                                     sensorsAsyncResp->res.jsonValue["Fans"];
1178                                 for (const std::string& item : *collection)
1179                                 {
1180                                     lastSlash = item.rfind("/");
1181                                     // make a copy as collection is const
1182                                     std::string itemName =
1183                                         item.substr(lastSlash + 1);
1184                                     /*
1185                                     todo(ed): merge patch that fixes the names
1186                                     std::replace(itemName.begin(),
1187                                                  itemName.end(), '_', ' ');*/
1188                                     auto schemaItem = std::find_if(
1189                                         fanRedfish.begin(), fanRedfish.end(),
1190                                         [itemName](const nlohmann::json& fan) {
1191                                             return fan["MemberId"] == itemName;
1192                                         });
1193                                     if (schemaItem != fanRedfish.end())
1194                                     {
1195                                         redfishCollection.push_back(
1196                                             {{"@odata.id",
1197                                               (*schemaItem)["@odata.id"]}});
1198                                     }
1199                                     else
1200                                     {
1201                                         BMCWEB_LOG_ERROR
1202                                             << "failed to find fan in schema";
1203                                         messages::internalError(
1204                                             sensorsAsyncResp->res);
1205                                         return;
1206                                     }
1207                                 }
1208 
1209                                 size_t minNumNeeded =
1210                                     collection->size() > 0
1211                                         ? collection->size() - *allowedFailures
1212                                         : 0;
1213                                 nlohmann::json& jResp =
1214                                     sensorsAsyncResp->res
1215                                         .jsonValue["Redundancy"];
1216                                 jResp.push_back(
1217                                     {{"@odata.id",
1218                                       "/redfish/v1/Chassis/" +
1219                                           sensorsAsyncResp->chassisId + "/" +
1220                                           sensorsAsyncResp->chassisSubNode +
1221                                           "#/Redundancy/" +
1222                                           std::to_string(jResp.size())},
1223                                      {"@odata.type",
1224                                       "#Redundancy.v1_3_2.Redundancy"},
1225                                      {"MinNumNeeded", minNumNeeded},
1226                                      {"MemberId", name},
1227                                      {"Mode", "N+m"},
1228                                      {"Name", name},
1229                                      {"RedundancySet", redfishCollection},
1230                                      {"Status",
1231                                       {{"Health", health},
1232                                        {"State", "Enabled"}}}});
1233                             },
1234                             owner, path, "org.freedesktop.DBus.Properties",
1235                             "GetAll",
1236                             "xyz.openbmc_project.Control.FanRedundancy");
1237                     },
1238                     "xyz.openbmc_project.ObjectMapper", path + "/chassis",
1239                     "org.freedesktop.DBus.Properties", "Get",
1240                     "xyz.openbmc_project.Association", "endpoints");
1241             }
1242         },
1243         "xyz.openbmc_project.ObjectMapper",
1244         "/xyz/openbmc_project/object_mapper",
1245         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1246         "/xyz/openbmc_project/control", 2,
1247         std::array<const char*, 1>{
1248             "xyz.openbmc_project.Control.FanRedundancy"});
1249 }
1250 
1251 inline void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1252 {
1253     nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1254     std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1255     if (SensorsAsyncResp->chassisSubNode == sensors::node::power)
1256     {
1257         sensorHeaders = {"Voltages", "PowerSupplies"};
1258     }
1259     for (const std::string& sensorGroup : sensorHeaders)
1260     {
1261         nlohmann::json::iterator entry = response.find(sensorGroup);
1262         if (entry != response.end())
1263         {
1264             std::sort(entry->begin(), entry->end(),
1265                       [](nlohmann::json& c1, nlohmann::json& c2) {
1266                           return c1["Name"] < c2["Name"];
1267                       });
1268 
1269             // add the index counts to the end of each entry
1270             size_t count = 0;
1271             for (nlohmann::json& sensorJson : *entry)
1272             {
1273                 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1274                 if (odata == sensorJson.end())
1275                 {
1276                     continue;
1277                 }
1278                 std::string* value = odata->get_ptr<std::string*>();
1279                 if (value != nullptr)
1280                 {
1281                     *value += std::to_string(count);
1282                     count++;
1283                     SensorsAsyncResp->updateUri(sensorJson["Name"], *value);
1284                 }
1285             }
1286         }
1287     }
1288 }
1289 
1290 /**
1291  * @brief Finds the inventory item with the specified object path.
1292  * @param inventoryItems D-Bus inventory items associated with sensors.
1293  * @param invItemObjPath D-Bus object path of inventory item.
1294  * @return Inventory item within vector, or nullptr if no match found.
1295  */
1296 inline InventoryItem* findInventoryItem(
1297     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1298     const std::string& invItemObjPath)
1299 {
1300     for (InventoryItem& inventoryItem : *inventoryItems)
1301     {
1302         if (inventoryItem.objectPath == invItemObjPath)
1303         {
1304             return &inventoryItem;
1305         }
1306     }
1307     return nullptr;
1308 }
1309 
1310 /**
1311  * @brief Finds the inventory item associated with the specified sensor.
1312  * @param inventoryItems D-Bus inventory items associated with sensors.
1313  * @param sensorObjPath D-Bus object path of sensor.
1314  * @return Inventory item within vector, or nullptr if no match found.
1315  */
1316 inline InventoryItem* findInventoryItemForSensor(
1317     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1318     const std::string& sensorObjPath)
1319 {
1320     for (InventoryItem& inventoryItem : *inventoryItems)
1321     {
1322         if (inventoryItem.sensors.count(sensorObjPath) > 0)
1323         {
1324             return &inventoryItem;
1325         }
1326     }
1327     return nullptr;
1328 }
1329 
1330 /**
1331  * @brief Finds the inventory item associated with the specified led path.
1332  * @param inventoryItems D-Bus inventory items associated with sensors.
1333  * @param ledObjPath D-Bus object path of led.
1334  * @return Inventory item within vector, or nullptr if no match found.
1335  */
1336 inline InventoryItem*
1337     findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1338                             const std::string& ledObjPath)
1339 {
1340     for (InventoryItem& inventoryItem : inventoryItems)
1341     {
1342         if (inventoryItem.ledObjectPath == ledObjPath)
1343         {
1344             return &inventoryItem;
1345         }
1346     }
1347     return nullptr;
1348 }
1349 
1350 /**
1351  * @brief Adds inventory item and associated sensor to specified vector.
1352  *
1353  * Adds a new InventoryItem to the vector if necessary.  Searches for an
1354  * existing InventoryItem with the specified object path.  If not found, one is
1355  * added to the vector.
1356  *
1357  * Next, the specified sensor is added to the set of sensors associated with the
1358  * InventoryItem.
1359  *
1360  * @param inventoryItems D-Bus inventory items associated with sensors.
1361  * @param invItemObjPath D-Bus object path of inventory item.
1362  * @param sensorObjPath D-Bus object path of sensor
1363  */
1364 inline void
1365     addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1366                      const std::string& invItemObjPath,
1367                      const std::string& sensorObjPath)
1368 {
1369     // Look for inventory item in vector
1370     InventoryItem* inventoryItem =
1371         findInventoryItem(inventoryItems, invItemObjPath);
1372 
1373     // If inventory item doesn't exist in vector, add it
1374     if (inventoryItem == nullptr)
1375     {
1376         inventoryItems->emplace_back(invItemObjPath);
1377         inventoryItem = &(inventoryItems->back());
1378     }
1379 
1380     // Add sensor to set of sensors associated with inventory item
1381     inventoryItem->sensors.emplace(sensorObjPath);
1382 }
1383 
1384 /**
1385  * @brief Stores D-Bus data in the specified inventory item.
1386  *
1387  * Finds D-Bus data in the specified map of interfaces.  Stores the data in the
1388  * specified InventoryItem.
1389  *
1390  * This data is later used to provide sensor property values in the JSON
1391  * response.
1392  *
1393  * @param inventoryItem Inventory item where data will be stored.
1394  * @param interfacesDict Map containing D-Bus interfaces and their properties
1395  * for the specified inventory item.
1396  */
1397 inline void storeInventoryItemData(
1398     InventoryItem& inventoryItem,
1399     const boost::container::flat_map<
1400         std::string, boost::container::flat_map<std::string, SensorVariant>>&
1401         interfacesDict)
1402 {
1403     // Get properties from Inventory.Item interface
1404     auto interfaceIt =
1405         interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1406     if (interfaceIt != interfacesDict.end())
1407     {
1408         auto propertyIt = interfaceIt->second.find("Present");
1409         if (propertyIt != interfaceIt->second.end())
1410         {
1411             const bool* value = std::get_if<bool>(&propertyIt->second);
1412             if (value != nullptr)
1413             {
1414                 inventoryItem.isPresent = *value;
1415             }
1416         }
1417     }
1418 
1419     // Check if Inventory.Item.PowerSupply interface is present
1420     interfaceIt =
1421         interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1422     if (interfaceIt != interfacesDict.end())
1423     {
1424         inventoryItem.isPowerSupply = true;
1425     }
1426 
1427     // Get properties from Inventory.Decorator.Asset interface
1428     interfaceIt =
1429         interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1430     if (interfaceIt != interfacesDict.end())
1431     {
1432         auto propertyIt = interfaceIt->second.find("Manufacturer");
1433         if (propertyIt != interfaceIt->second.end())
1434         {
1435             const std::string* value =
1436                 std::get_if<std::string>(&propertyIt->second);
1437             if (value != nullptr)
1438             {
1439                 inventoryItem.manufacturer = *value;
1440             }
1441         }
1442 
1443         propertyIt = interfaceIt->second.find("Model");
1444         if (propertyIt != interfaceIt->second.end())
1445         {
1446             const std::string* value =
1447                 std::get_if<std::string>(&propertyIt->second);
1448             if (value != nullptr)
1449             {
1450                 inventoryItem.model = *value;
1451             }
1452         }
1453 
1454         propertyIt = interfaceIt->second.find("PartNumber");
1455         if (propertyIt != interfaceIt->second.end())
1456         {
1457             const std::string* value =
1458                 std::get_if<std::string>(&propertyIt->second);
1459             if (value != nullptr)
1460             {
1461                 inventoryItem.partNumber = *value;
1462             }
1463         }
1464 
1465         propertyIt = interfaceIt->second.find("SerialNumber");
1466         if (propertyIt != interfaceIt->second.end())
1467         {
1468             const std::string* value =
1469                 std::get_if<std::string>(&propertyIt->second);
1470             if (value != nullptr)
1471             {
1472                 inventoryItem.serialNumber = *value;
1473             }
1474         }
1475     }
1476 
1477     // Get properties from State.Decorator.OperationalStatus interface
1478     interfaceIt = interfacesDict.find(
1479         "xyz.openbmc_project.State.Decorator.OperationalStatus");
1480     if (interfaceIt != interfacesDict.end())
1481     {
1482         auto propertyIt = interfaceIt->second.find("Functional");
1483         if (propertyIt != interfaceIt->second.end())
1484         {
1485             const bool* value = std::get_if<bool>(&propertyIt->second);
1486             if (value != nullptr)
1487             {
1488                 inventoryItem.isFunctional = *value;
1489             }
1490         }
1491     }
1492 }
1493 
1494 /**
1495  * @brief Gets D-Bus data for inventory items associated with sensors.
1496  *
1497  * Uses the specified connections (services) to obtain D-Bus data for inventory
1498  * items associated with sensors.  Stores the resulting data in the
1499  * inventoryItems vector.
1500  *
1501  * This data is later used to provide sensor property values in the JSON
1502  * response.
1503  *
1504  * Finds the inventory item data asynchronously.  Invokes callback when data has
1505  * been obtained.
1506  *
1507  * The callback must have the following signature:
1508  *   @code
1509  *   callback(void)
1510  *   @endcode
1511  *
1512  * This function is called recursively, obtaining data asynchronously from one
1513  * connection in each call.  This ensures the callback is not invoked until the
1514  * last asynchronous function has completed.
1515  *
1516  * @param sensorsAsyncResp Pointer to object holding response data.
1517  * @param inventoryItems D-Bus inventory items associated with sensors.
1518  * @param invConnections Connections that provide data for the inventory items.
1519  * @param objectMgrPaths Mappings from connection name to DBus object path that
1520  * implements ObjectManager.
1521  * @param callback Callback to invoke when inventory data has been obtained.
1522  * @param invConnectionsIndex Current index in invConnections.  Only specified
1523  * in recursive calls to this function.
1524  */
1525 template <typename Callback>
1526 static void getInventoryItemsData(
1527     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1528     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1529     std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1530     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1531         objectMgrPaths,
1532     Callback&& callback, size_t invConnectionsIndex = 0)
1533 {
1534     BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
1535 
1536     // If no more connections left, call callback
1537     if (invConnectionsIndex >= invConnections->size())
1538     {
1539         callback();
1540         BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1541         return;
1542     }
1543 
1544     // Get inventory item data from current connection
1545     auto it = invConnections->nth(invConnectionsIndex);
1546     if (it != invConnections->end())
1547     {
1548         const std::string& invConnection = *it;
1549 
1550         // Response handler for GetManagedObjects
1551         auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1552                             objectMgrPaths, callback{std::move(callback)},
1553                             invConnectionsIndex](
1554                                const boost::system::error_code ec,
1555                                ManagedObjectsVectorType& resp) {
1556             BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
1557             if (ec)
1558             {
1559                 BMCWEB_LOG_ERROR
1560                     << "getInventoryItemsData respHandler DBus error " << ec;
1561                 messages::internalError(sensorsAsyncResp->res);
1562                 return;
1563             }
1564 
1565             // Loop through returned object paths
1566             for (const auto& objDictEntry : resp)
1567             {
1568                 const std::string& objPath =
1569                     static_cast<const std::string&>(objDictEntry.first);
1570 
1571                 // If this object path is one of the specified inventory items
1572                 InventoryItem* inventoryItem =
1573                     findInventoryItem(inventoryItems, objPath);
1574                 if (inventoryItem != nullptr)
1575                 {
1576                     // Store inventory data in InventoryItem
1577                     storeInventoryItemData(*inventoryItem, objDictEntry.second);
1578                 }
1579             }
1580 
1581             // Recurse to get inventory item data from next connection
1582             getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1583                                   invConnections, objectMgrPaths,
1584                                   std::move(callback), invConnectionsIndex + 1);
1585 
1586             BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
1587         };
1588 
1589         // Find DBus object path that implements ObjectManager for the current
1590         // connection.  If no mapping found, default to "/".
1591         auto iter = objectMgrPaths->find(invConnection);
1592         const std::string& objectMgrPath =
1593             (iter != objectMgrPaths->end()) ? iter->second : "/";
1594         BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1595                          << objectMgrPath;
1596 
1597         // Get all object paths and their interfaces for current connection
1598         crow::connections::systemBus->async_method_call(
1599             std::move(respHandler), invConnection, objectMgrPath,
1600             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1601     }
1602 
1603     BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1604 }
1605 
1606 /**
1607  * @brief Gets connections that provide D-Bus data for inventory items.
1608  *
1609  * Gets the D-Bus connections (services) that provide data for the inventory
1610  * items that are associated with sensors.
1611  *
1612  * Finds the connections asynchronously.  Invokes callback when information has
1613  * been obtained.
1614  *
1615  * The callback must have the following signature:
1616  *   @code
1617  *   callback(std::shared_ptr<boost::container::flat_set<std::string>>
1618  *            invConnections)
1619  *   @endcode
1620  *
1621  * @param sensorsAsyncResp Pointer to object holding response data.
1622  * @param inventoryItems D-Bus inventory items associated with sensors.
1623  * @param callback Callback to invoke when connections have been obtained.
1624  */
1625 template <typename Callback>
1626 static void getInventoryItemsConnections(
1627     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1628     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1629     Callback&& callback)
1630 {
1631     BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1632 
1633     const std::string path = "/xyz/openbmc_project/inventory";
1634     const std::array<std::string, 4> interfaces = {
1635         "xyz.openbmc_project.Inventory.Item",
1636         "xyz.openbmc_project.Inventory.Item.PowerSupply",
1637         "xyz.openbmc_project.Inventory.Decorator.Asset",
1638         "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1639 
1640     // Response handler for parsing output from GetSubTree
1641     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1642                         inventoryItems](const boost::system::error_code ec,
1643                                         const GetSubTreeType& subtree) {
1644         BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1645         if (ec)
1646         {
1647             messages::internalError(sensorsAsyncResp->res);
1648             BMCWEB_LOG_ERROR
1649                 << "getInventoryItemsConnections respHandler DBus error " << ec;
1650             return;
1651         }
1652 
1653         // Make unique list of connections for desired inventory items
1654         std::shared_ptr<boost::container::flat_set<std::string>>
1655             invConnections =
1656                 std::make_shared<boost::container::flat_set<std::string>>();
1657         invConnections->reserve(8);
1658 
1659         // Loop through objects from GetSubTree
1660         for (const std::pair<
1661                  std::string,
1662                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
1663                  object : subtree)
1664         {
1665             // Check if object path is one of the specified inventory items
1666             const std::string& objPath = object.first;
1667             if (findInventoryItem(inventoryItems, objPath) != nullptr)
1668             {
1669                 // Store all connections to inventory item
1670                 for (const std::pair<std::string, std::vector<std::string>>&
1671                          objData : object.second)
1672                 {
1673                     const std::string& invConnection = objData.first;
1674                     invConnections->insert(invConnection);
1675                 }
1676             }
1677         }
1678 
1679         callback(invConnections);
1680         BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1681     };
1682 
1683     // Make call to ObjectMapper to find all inventory items
1684     crow::connections::systemBus->async_method_call(
1685         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1686         "/xyz/openbmc_project/object_mapper",
1687         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1688     BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1689 }
1690 
1691 /**
1692  * @brief Gets associations from sensors to inventory items.
1693  *
1694  * Looks for ObjectMapper associations from the specified sensors to related
1695  * inventory items. Then finds the associations from those inventory items to
1696  * their LEDs, if any.
1697  *
1698  * Finds the inventory items asynchronously.  Invokes callback when information
1699  * has been obtained.
1700  *
1701  * The callback must have the following signature:
1702  *   @code
1703  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1704  *   @endcode
1705  *
1706  * @param sensorsAsyncResp Pointer to object holding response data.
1707  * @param sensorNames All sensors within the current chassis.
1708  * @param objectMgrPaths Mappings from connection name to DBus object path that
1709  * implements ObjectManager.
1710  * @param callback Callback to invoke when inventory items have been obtained.
1711  */
1712 template <typename Callback>
1713 static void getInventoryItemAssociations(
1714     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1715     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1716     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1717         objectMgrPaths,
1718     Callback&& callback)
1719 {
1720     BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
1721 
1722     // Response handler for GetManagedObjects
1723     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1724                         sensorNames](const boost::system::error_code ec,
1725                                      dbus::utility::ManagedObjectType& resp) {
1726         BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
1727         if (ec)
1728         {
1729             BMCWEB_LOG_ERROR
1730                 << "getInventoryItemAssociations respHandler DBus error " << ec;
1731             messages::internalError(sensorsAsyncResp->res);
1732             return;
1733         }
1734 
1735         // Create vector to hold list of inventory items
1736         std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1737             std::make_shared<std::vector<InventoryItem>>();
1738 
1739         // Loop through returned object paths
1740         std::string sensorAssocPath;
1741         sensorAssocPath.reserve(128); // avoid memory allocations
1742         for (const auto& objDictEntry : resp)
1743         {
1744             const std::string& objPath =
1745                 static_cast<const std::string&>(objDictEntry.first);
1746             const boost::container::flat_map<
1747                 std::string, boost::container::flat_map<
1748                                  std::string, dbus::utility::DbusVariantType>>&
1749                 interfacesDict = objDictEntry.second;
1750 
1751             // If path is inventory association for one of the specified sensors
1752             for (const std::string& sensorName : *sensorNames)
1753             {
1754                 sensorAssocPath = sensorName;
1755                 sensorAssocPath += "/inventory";
1756                 if (objPath == sensorAssocPath)
1757                 {
1758                     // Get Association interface for object path
1759                     auto assocIt =
1760                         interfacesDict.find("xyz.openbmc_project.Association");
1761                     if (assocIt != interfacesDict.end())
1762                     {
1763                         // Get inventory item from end point
1764                         auto endpointsIt = assocIt->second.find("endpoints");
1765                         if (endpointsIt != assocIt->second.end())
1766                         {
1767                             const std::vector<std::string>* endpoints =
1768                                 std::get_if<std::vector<std::string>>(
1769                                     &endpointsIt->second);
1770                             if ((endpoints != nullptr) && !endpoints->empty())
1771                             {
1772                                 // Add inventory item to vector
1773                                 const std::string& invItemPath =
1774                                     endpoints->front();
1775                                 addInventoryItem(inventoryItems, invItemPath,
1776                                                  sensorName);
1777                             }
1778                         }
1779                     }
1780                     break;
1781                 }
1782             }
1783         }
1784 
1785         // Now loop through the returned object paths again, this time to
1786         // find the leds associated with the inventory items we just found
1787         std::string inventoryAssocPath;
1788         inventoryAssocPath.reserve(128); // avoid memory allocations
1789         for (const auto& objDictEntry : resp)
1790         {
1791             const std::string& objPath =
1792                 static_cast<const std::string&>(objDictEntry.first);
1793             const boost::container::flat_map<
1794                 std::string, boost::container::flat_map<
1795                                  std::string, dbus::utility::DbusVariantType>>&
1796                 interfacesDict = objDictEntry.second;
1797 
1798             for (InventoryItem& inventoryItem : *inventoryItems)
1799             {
1800                 inventoryAssocPath = inventoryItem.objectPath;
1801                 inventoryAssocPath += "/leds";
1802                 if (objPath == inventoryAssocPath)
1803                 {
1804                     // Get Association interface for object path
1805                     auto assocIt =
1806                         interfacesDict.find("xyz.openbmc_project.Association");
1807                     if (assocIt != interfacesDict.end())
1808                     {
1809                         // Get inventory item from end point
1810                         auto endpointsIt = assocIt->second.find("endpoints");
1811                         if (endpointsIt != assocIt->second.end())
1812                         {
1813                             const std::vector<std::string>* endpoints =
1814                                 std::get_if<std::vector<std::string>>(
1815                                     &endpointsIt->second);
1816                             if ((endpoints != nullptr) && !endpoints->empty())
1817                             {
1818                                 // Store LED path in inventory item
1819                                 const std::string& ledPath = endpoints->front();
1820                                 inventoryItem.ledObjectPath = ledPath;
1821                             }
1822                         }
1823                     }
1824                     break;
1825                 }
1826             }
1827         }
1828         callback(inventoryItems);
1829         BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
1830     };
1831 
1832     // Find DBus object path that implements ObjectManager for ObjectMapper
1833     std::string connection = "xyz.openbmc_project.ObjectMapper";
1834     auto iter = objectMgrPaths->find(connection);
1835     const std::string& objectMgrPath =
1836         (iter != objectMgrPaths->end()) ? iter->second : "/";
1837     BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1838                      << objectMgrPath;
1839 
1840     // Call GetManagedObjects on the ObjectMapper to get all associations
1841     crow::connections::systemBus->async_method_call(
1842         std::move(respHandler), connection, objectMgrPath,
1843         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1844 
1845     BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
1846 }
1847 
1848 /**
1849  * @brief Gets D-Bus data for inventory item leds associated with sensors.
1850  *
1851  * Uses the specified connections (services) to obtain D-Bus data for inventory
1852  * item leds associated with sensors.  Stores the resulting data in the
1853  * inventoryItems vector.
1854  *
1855  * This data is later used to provide sensor property values in the JSON
1856  * response.
1857  *
1858  * Finds the inventory item led data asynchronously.  Invokes callback when data
1859  * has been obtained.
1860  *
1861  * The callback must have the following signature:
1862  *   @code
1863  *   callback()
1864  *   @endcode
1865  *
1866  * This function is called recursively, obtaining data asynchronously from one
1867  * connection in each call.  This ensures the callback is not invoked until the
1868  * last asynchronous function has completed.
1869  *
1870  * @param sensorsAsyncResp Pointer to object holding response data.
1871  * @param inventoryItems D-Bus inventory items associated with sensors.
1872  * @param ledConnections Connections that provide data for the inventory leds.
1873  * @param callback Callback to invoke when inventory data has been obtained.
1874  * @param ledConnectionsIndex Current index in ledConnections.  Only specified
1875  * in recursive calls to this function.
1876  */
1877 template <typename Callback>
1878 void getInventoryLedData(
1879     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1880     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1881     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1882         ledConnections,
1883     Callback&& callback, size_t ledConnectionsIndex = 0)
1884 {
1885     BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1886 
1887     // If no more connections left, call callback
1888     if (ledConnectionsIndex >= ledConnections->size())
1889     {
1890         callback();
1891         BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1892         return;
1893     }
1894 
1895     // Get inventory item data from current connection
1896     auto it = ledConnections->nth(ledConnectionsIndex);
1897     if (it != ledConnections->end())
1898     {
1899         const std::string& ledPath = (*it).first;
1900         const std::string& ledConnection = (*it).second;
1901         // Response handler for Get State property
1902         auto respHandler =
1903             [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1904              callback{std::move(callback)},
1905              ledConnectionsIndex](const boost::system::error_code ec,
1906                                   const std::variant<std::string>& ledState) {
1907                 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
1908                 if (ec)
1909                 {
1910                     BMCWEB_LOG_ERROR
1911                         << "getInventoryLedData respHandler DBus error " << ec;
1912                     messages::internalError(sensorsAsyncResp->res);
1913                     return;
1914                 }
1915 
1916                 const std::string* state = std::get_if<std::string>(&ledState);
1917                 if (state != nullptr)
1918                 {
1919                     BMCWEB_LOG_DEBUG << "Led state: " << *state;
1920                     // Find inventory item with this LED object path
1921                     InventoryItem* inventoryItem =
1922                         findInventoryItemForLed(*inventoryItems, ledPath);
1923                     if (inventoryItem != nullptr)
1924                     {
1925                         // Store LED state in InventoryItem
1926                         if (boost::ends_with(*state, "On"))
1927                         {
1928                             inventoryItem->ledState = LedState::ON;
1929                         }
1930                         else if (boost::ends_with(*state, "Blink"))
1931                         {
1932                             inventoryItem->ledState = LedState::BLINK;
1933                         }
1934                         else if (boost::ends_with(*state, "Off"))
1935                         {
1936                             inventoryItem->ledState = LedState::OFF;
1937                         }
1938                         else
1939                         {
1940                             inventoryItem->ledState = LedState::UNKNOWN;
1941                         }
1942                     }
1943                 }
1944                 else
1945                 {
1946                     BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
1947                                      << ledPath;
1948                 }
1949 
1950                 // Recurse to get LED data from next connection
1951                 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1952                                     ledConnections, std::move(callback),
1953                                     ledConnectionsIndex + 1);
1954 
1955                 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
1956             };
1957 
1958         // Get the State property for the current LED
1959         crow::connections::systemBus->async_method_call(
1960             std::move(respHandler), ledConnection, ledPath,
1961             "org.freedesktop.DBus.Properties", "Get",
1962             "xyz.openbmc_project.Led.Physical", "State");
1963     }
1964 
1965     BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1966 }
1967 
1968 /**
1969  * @brief Gets LED data for LEDs associated with given inventory items.
1970  *
1971  * Gets the D-Bus connections (services) that provide LED data for the LEDs
1972  * associated with the specified inventory items.  Then gets the LED data from
1973  * each connection and stores it in the inventory item.
1974  *
1975  * This data is later used to provide sensor property values in the JSON
1976  * response.
1977  *
1978  * Finds the LED data asynchronously.  Invokes callback when information has
1979  * been obtained.
1980  *
1981  * The callback must have the following signature:
1982  *   @code
1983  *   callback()
1984  *   @endcode
1985  *
1986  * @param sensorsAsyncResp Pointer to object holding response data.
1987  * @param inventoryItems D-Bus inventory items associated with sensors.
1988  * @param callback Callback to invoke when inventory items have been obtained.
1989  */
1990 template <typename Callback>
1991 void getInventoryLeds(
1992     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1993     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1994     Callback&& callback)
1995 {
1996     BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
1997 
1998     const std::string path = "/xyz/openbmc_project";
1999     const std::array<std::string, 1> interfaces = {
2000         "xyz.openbmc_project.Led.Physical"};
2001 
2002     // Response handler for parsing output from GetSubTree
2003     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2004                         inventoryItems](const boost::system::error_code ec,
2005                                         const GetSubTreeType& subtree) {
2006         BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
2007         if (ec)
2008         {
2009             messages::internalError(sensorsAsyncResp->res);
2010             BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
2011                              << ec;
2012             return;
2013         }
2014 
2015         // Build map of LED object paths to connections
2016         std::shared_ptr<boost::container::flat_map<std::string, std::string>>
2017             ledConnections = std::make_shared<
2018                 boost::container::flat_map<std::string, std::string>>();
2019 
2020         // Loop through objects from GetSubTree
2021         for (const std::pair<
2022                  std::string,
2023                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
2024                  object : subtree)
2025         {
2026             // Check if object path is LED for one of the specified inventory
2027             // items
2028             const std::string& ledPath = object.first;
2029             if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
2030             {
2031                 // Add mapping from ledPath to connection
2032                 const std::string& connection = object.second.begin()->first;
2033                 (*ledConnections)[ledPath] = connection;
2034                 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
2035                                  << connection;
2036             }
2037         }
2038 
2039         getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
2040                             std::move(callback));
2041         BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
2042     };
2043     // Make call to ObjectMapper to find all inventory items
2044     crow::connections::systemBus->async_method_call(
2045         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2046         "/xyz/openbmc_project/object_mapper",
2047         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
2048     BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
2049 }
2050 
2051 /**
2052  * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
2053  *
2054  * Uses the specified connections (services) (currently assumes just one) to
2055  * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
2056  * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
2057  *
2058  * This data is later used to provide sensor property values in the JSON
2059  * response.
2060  *
2061  * Finds the Power Supply Attributes data asynchronously.  Invokes callback
2062  * when data has been obtained.
2063  *
2064  * The callback must have the following signature:
2065  *   @code
2066  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2067  *   @endcode
2068  *
2069  * @param sensorsAsyncResp Pointer to object holding response data.
2070  * @param inventoryItems D-Bus inventory items associated with sensors.
2071  * @param psAttributesConnections Connections that provide data for the Power
2072  *        Supply Attributes
2073  * @param callback Callback to invoke when data has been obtained.
2074  */
2075 template <typename Callback>
2076 void getPowerSupplyAttributesData(
2077     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2078     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2079     const boost::container::flat_map<std::string, std::string>&
2080         psAttributesConnections,
2081     Callback&& callback)
2082 {
2083     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter";
2084 
2085     if (psAttributesConnections.empty())
2086     {
2087         BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!";
2088         callback(inventoryItems);
2089         return;
2090     }
2091 
2092     // Assuming just one connection (service) for now
2093     auto it = psAttributesConnections.nth(0);
2094 
2095     const std::string& psAttributesPath = (*it).first;
2096     const std::string& psAttributesConnection = (*it).second;
2097 
2098     // Response handler for Get DeratingFactor property
2099     auto respHandler = [sensorsAsyncResp, inventoryItems,
2100                         callback{std::move(callback)}](
2101                            const boost::system::error_code ec,
2102                            const std::variant<uint32_t>& deratingFactor) {
2103         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter";
2104         if (ec)
2105         {
2106             BMCWEB_LOG_ERROR
2107                 << "getPowerSupplyAttributesData respHandler DBus error " << ec;
2108             messages::internalError(sensorsAsyncResp->res);
2109             return;
2110         }
2111 
2112         const uint32_t* value = std::get_if<uint32_t>(&deratingFactor);
2113         if (value != nullptr)
2114         {
2115             BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value;
2116             // Store value in Power Supply Inventory Items
2117             for (InventoryItem& inventoryItem : *inventoryItems)
2118             {
2119                 if (inventoryItem.isPowerSupply == true)
2120                 {
2121                     inventoryItem.powerSupplyEfficiencyPercent =
2122                         static_cast<int>(*value);
2123                 }
2124             }
2125         }
2126         else
2127         {
2128             BMCWEB_LOG_DEBUG
2129                 << "Failed to find EfficiencyPercent value for PowerSupplies";
2130         }
2131 
2132         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit";
2133         callback(inventoryItems);
2134     };
2135 
2136     // Get the DeratingFactor property for the PowerSupplyAttributes
2137     // Currently only property on the interface/only one we care about
2138     crow::connections::systemBus->async_method_call(
2139         std::move(respHandler), psAttributesConnection, psAttributesPath,
2140         "org.freedesktop.DBus.Properties", "Get",
2141         "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor");
2142 
2143     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit";
2144 }
2145 
2146 /**
2147  * @brief Gets the Power Supply Attributes such as EfficiencyPercent
2148  *
2149  * Gets the D-Bus connection (service) that provides Power Supply Attributes
2150  * data. Then gets the Power Supply Attributes data from the connection
2151  * (currently just assumes 1 connection) and stores the data in the inventory
2152  * item.
2153  *
2154  * This data is later used to provide sensor property values in the JSON
2155  * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
2156  *
2157  * Finds the Power Supply Attributes data asynchronously. Invokes callback
2158  * when information has been obtained.
2159  *
2160  * The callback must have the following signature:
2161  *   @code
2162  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2163  *   @endcode
2164  *
2165  * @param sensorsAsyncResp Pointer to object holding response data.
2166  * @param inventoryItems D-Bus inventory items associated with sensors.
2167  * @param callback Callback to invoke when data has been obtained.
2168  */
2169 template <typename Callback>
2170 void getPowerSupplyAttributes(
2171     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2172     std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2173     Callback&& callback)
2174 {
2175     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter";
2176 
2177     // Only need the power supply attributes when the Power Schema
2178     if (sensorsAsyncResp->chassisSubNode != sensors::node::power)
2179     {
2180         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power";
2181         callback(inventoryItems);
2182         return;
2183     }
2184 
2185     const std::array<std::string, 1> interfaces = {
2186         "xyz.openbmc_project.Control.PowerSupplyAttributes"};
2187 
2188     // Response handler for parsing output from GetSubTree
2189     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2190                         inventoryItems](const boost::system::error_code ec,
2191                                         const GetSubTreeType& subtree) {
2192         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter";
2193         if (ec)
2194         {
2195             messages::internalError(sensorsAsyncResp->res);
2196             BMCWEB_LOG_ERROR
2197                 << "getPowerSupplyAttributes respHandler DBus error " << ec;
2198             return;
2199         }
2200         if (subtree.size() == 0)
2201         {
2202             BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!";
2203             callback(inventoryItems);
2204             return;
2205         }
2206 
2207         // Currently we only support 1 power supply attribute, use this for
2208         // all the power supplies. Build map of object path to connection.
2209         // Assume just 1 connection and 1 path for now.
2210         boost::container::flat_map<std::string, std::string>
2211             psAttributesConnections;
2212 
2213         if (subtree[0].first.empty() || subtree[0].second.empty())
2214         {
2215             BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2216             callback(inventoryItems);
2217             return;
2218         }
2219 
2220         const std::string& psAttributesPath = subtree[0].first;
2221         const std::string& connection = subtree[0].second.begin()->first;
2222 
2223         if (connection.empty())
2224         {
2225             BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2226             callback(inventoryItems);
2227             return;
2228         }
2229 
2230         psAttributesConnections[psAttributesPath] = connection;
2231         BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> "
2232                          << connection;
2233 
2234         getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
2235                                      psAttributesConnections,
2236                                      std::move(callback));
2237         BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit";
2238     };
2239     // Make call to ObjectMapper to find the PowerSupplyAttributes service
2240     crow::connections::systemBus->async_method_call(
2241         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2242         "/xyz/openbmc_project/object_mapper",
2243         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2244         "/xyz/openbmc_project", 0, interfaces);
2245     BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit";
2246 }
2247 
2248 /**
2249  * @brief Gets inventory items associated with sensors.
2250  *
2251  * Finds the inventory items that are associated with the specified sensors.
2252  * Then gets D-Bus data for the inventory items, such as presence and VPD.
2253  *
2254  * This data is later used to provide sensor property values in the JSON
2255  * response.
2256  *
2257  * Finds the inventory items asynchronously.  Invokes callback when the
2258  * inventory items have been obtained.
2259  *
2260  * The callback must have the following signature:
2261  *   @code
2262  *   callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2263  *   @endcode
2264  *
2265  * @param sensorsAsyncResp Pointer to object holding response data.
2266  * @param sensorNames All sensors within the current chassis.
2267  * @param objectMgrPaths Mappings from connection name to DBus object path that
2268  * implements ObjectManager.
2269  * @param callback Callback to invoke when inventory items have been obtained.
2270  */
2271 template <typename Callback>
2272 static void getInventoryItems(
2273     std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2274     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2275     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
2276         objectMgrPaths,
2277     Callback&& callback)
2278 {
2279     BMCWEB_LOG_DEBUG << "getInventoryItems enter";
2280     auto getInventoryItemAssociationsCb =
2281         [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
2282             std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
2283             BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
2284             auto getInventoryItemsConnectionsCb =
2285                 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
2286                  callback{std::move(callback)}](
2287                     std::shared_ptr<boost::container::flat_set<std::string>>
2288                         invConnections) {
2289                     BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
2290                     auto getInventoryItemsDataCb =
2291                         [sensorsAsyncResp, inventoryItems,
2292                          callback{std::move(callback)}]() {
2293                             BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
2294 
2295                             auto getInventoryLedsCb = [sensorsAsyncResp,
2296                                                        inventoryItems,
2297                                                        callback{std::move(
2298                                                            callback)}]() {
2299                                 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter";
2300                                 // Find Power Supply Attributes and get the data
2301                                 getPowerSupplyAttributes(sensorsAsyncResp,
2302                                                          inventoryItems,
2303                                                          std::move(callback));
2304                                 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit";
2305                             };
2306 
2307                             // Find led connections and get the data
2308                             getInventoryLeds(sensorsAsyncResp, inventoryItems,
2309                                              std::move(getInventoryLedsCb));
2310                             BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
2311                         };
2312 
2313                     // Get inventory item data from connections
2314                     getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2315                                           invConnections, objectMgrPaths,
2316                                           std::move(getInventoryItemsDataCb));
2317                     BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2318                 };
2319 
2320             // Get connections that provide inventory item data
2321             getInventoryItemsConnections(
2322                 sensorsAsyncResp, inventoryItems,
2323                 std::move(getInventoryItemsConnectionsCb));
2324             BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
2325         };
2326 
2327     // Get associations from sensors to inventory items
2328     getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2329                                  std::move(getInventoryItemAssociationsCb));
2330     BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2331 }
2332 
2333 /**
2334  * @brief Returns JSON PowerSupply object for the specified inventory item.
2335  *
2336  * Searches for a JSON PowerSupply object that matches the specified inventory
2337  * item.  If one is not found, a new PowerSupply object is added to the JSON
2338  * array.
2339  *
2340  * Multiple sensors are often associated with one power supply inventory item.
2341  * As a result, multiple sensor values are stored in one JSON PowerSupply
2342  * object.
2343  *
2344  * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2345  * @param inventoryItem Inventory item for the power supply.
2346  * @param chassisId Chassis that contains the power supply.
2347  * @return JSON PowerSupply object for the specified inventory item.
2348  */
2349 inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
2350                                       const InventoryItem& inventoryItem,
2351                                       const std::string& chassisId)
2352 {
2353     // Check if matching PowerSupply object already exists in JSON array
2354     for (nlohmann::json& powerSupply : powerSupplyArray)
2355     {
2356         if (powerSupply["MemberId"] == inventoryItem.name)
2357         {
2358             return powerSupply;
2359         }
2360     }
2361 
2362     // Add new PowerSupply object to JSON array
2363     powerSupplyArray.push_back({});
2364     nlohmann::json& powerSupply = powerSupplyArray.back();
2365     powerSupply["@odata.id"] =
2366         "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2367     powerSupply["MemberId"] = inventoryItem.name;
2368     powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2369     powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2370     powerSupply["Model"] = inventoryItem.model;
2371     powerSupply["PartNumber"] = inventoryItem.partNumber;
2372     powerSupply["SerialNumber"] = inventoryItem.serialNumber;
2373     setLedState(powerSupply, &inventoryItem);
2374 
2375     if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
2376     {
2377         powerSupply["EfficiencyPercent"] =
2378             inventoryItem.powerSupplyEfficiencyPercent;
2379     }
2380 
2381     powerSupply["Status"]["State"] = getState(&inventoryItem);
2382     const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2383     powerSupply["Status"]["Health"] = health;
2384 
2385     return powerSupply;
2386 }
2387 
2388 /**
2389  * @brief Gets the values of the specified sensors.
2390  *
2391  * Stores the results as JSON in the SensorsAsyncResp.
2392  *
2393  * Gets the sensor values asynchronously.  Stores the results later when the
2394  * information has been obtained.
2395  *
2396  * The sensorNames set contains all requested sensors for the current chassis.
2397  *
2398  * To minimize the number of DBus calls, the DBus method
2399  * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2400  * values of all sensors provided by a connection (service).
2401  *
2402  * The connections set contains all the connections that provide sensor values.
2403  *
2404  * The objectMgrPaths map contains mappings from a connection name to the
2405  * corresponding DBus object path that implements ObjectManager.
2406  *
2407  * The InventoryItem vector contains D-Bus inventory items associated with the
2408  * sensors.  Inventory item data is needed for some Redfish sensor properties.
2409  *
2410  * @param SensorsAsyncResp Pointer to object holding response data.
2411  * @param sensorNames All requested sensors within the current chassis.
2412  * @param connections Connections that provide sensor values.
2413  * @param objectMgrPaths Mappings from connection name to DBus object path that
2414  * implements ObjectManager.
2415  * @param inventoryItems Inventory items associated with the sensors.
2416  */
2417 inline void getSensorData(
2418     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2419     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2420     const boost::container::flat_set<std::string>& connections,
2421     std::shared_ptr<boost::container::flat_map<std::string, std::string>>
2422         objectMgrPaths,
2423     std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2424 {
2425     BMCWEB_LOG_DEBUG << "getSensorData enter";
2426     // Get managed objects from all services exposing sensors
2427     for (const std::string& connection : connections)
2428     {
2429         // Response handler to process managed objects
2430         auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
2431                                     inventoryItems](
2432                                        const boost::system::error_code ec,
2433                                        ManagedObjectsVectorType& resp) {
2434             BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2435             if (ec)
2436             {
2437                 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2438                 messages::internalError(SensorsAsyncResp->res);
2439                 return;
2440             }
2441             // Go through all objects and update response with sensor data
2442             for (const auto& objDictEntry : resp)
2443             {
2444                 const std::string& objPath =
2445                     static_cast<const std::string&>(objDictEntry.first);
2446                 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2447                                  << objPath;
2448 
2449                 std::vector<std::string> split;
2450                 // Reserve space for
2451                 // /xyz/openbmc_project/sensors/<name>/<subname>
2452                 split.reserve(6);
2453                 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2454                 if (split.size() < 6)
2455                 {
2456                     BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2457                                      << objPath;
2458                     continue;
2459                 }
2460                 // These indexes aren't intuitive, as boost::split puts an empty
2461                 // string at the beginning
2462                 const std::string& sensorType = split[4];
2463                 const std::string& sensorName = split[5];
2464                 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2465                                  << " sensorType " << sensorType;
2466                 if (sensorNames->find(objPath) == sensorNames->end())
2467                 {
2468                     BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2469                     continue;
2470                 }
2471 
2472                 // Find inventory item (if any) associated with sensor
2473                 InventoryItem* inventoryItem =
2474                     findInventoryItemForSensor(inventoryItems, objPath);
2475 
2476                 const std::string& sensorSchema =
2477                     SensorsAsyncResp->chassisSubNode;
2478 
2479                 nlohmann::json* sensorJson = nullptr;
2480 
2481                 if (sensorSchema == sensors::node::sensors)
2482                 {
2483                     SensorsAsyncResp->res.jsonValue["@odata.id"] =
2484                         "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
2485                         "/" + SensorsAsyncResp->chassisSubNode + "/" +
2486                         sensorName;
2487                     sensorJson = &(SensorsAsyncResp->res.jsonValue);
2488                 }
2489                 else
2490                 {
2491                     std::string fieldName;
2492                     if (sensorType == "temperature")
2493                     {
2494                         fieldName = "Temperatures";
2495                     }
2496                     else if (sensorType == "fan" || sensorType == "fan_tach" ||
2497                              sensorType == "fan_pwm")
2498                     {
2499                         fieldName = "Fans";
2500                     }
2501                     else if (sensorType == "voltage")
2502                     {
2503                         fieldName = "Voltages";
2504                     }
2505                     else if (sensorType == "power")
2506                     {
2507                         if (!sensorName.compare("total_power"))
2508                         {
2509                             fieldName = "PowerControl";
2510                         }
2511                         else if ((inventoryItem != nullptr) &&
2512                                  (inventoryItem->isPowerSupply))
2513                         {
2514                             fieldName = "PowerSupplies";
2515                         }
2516                         else
2517                         {
2518                             // Other power sensors are in SensorCollection
2519                             continue;
2520                         }
2521                     }
2522                     else
2523                     {
2524                         BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2525                                          << sensorType;
2526                         continue;
2527                     }
2528 
2529                     nlohmann::json& tempArray =
2530                         SensorsAsyncResp->res.jsonValue[fieldName];
2531                     if (fieldName == "PowerControl")
2532                     {
2533                         if (tempArray.empty())
2534                         {
2535                             // Put multiple "sensors" into a single
2536                             // PowerControl. Follows MemberId naming and
2537                             // naming in power.hpp.
2538                             tempArray.push_back(
2539                                 {{"@odata.id",
2540                                   "/redfish/v1/Chassis/" +
2541                                       SensorsAsyncResp->chassisId + "/" +
2542                                       SensorsAsyncResp->chassisSubNode + "#/" +
2543                                       fieldName + "/0"}});
2544                         }
2545                         sensorJson = &(tempArray.back());
2546                     }
2547                     else if (fieldName == "PowerSupplies")
2548                     {
2549                         if (inventoryItem != nullptr)
2550                         {
2551                             sensorJson =
2552                                 &(getPowerSupply(tempArray, *inventoryItem,
2553                                                  SensorsAsyncResp->chassisId));
2554                         }
2555                     }
2556                     else
2557                     {
2558                         tempArray.push_back(
2559                             {{"@odata.id",
2560                               "/redfish/v1/Chassis/" +
2561                                   SensorsAsyncResp->chassisId + "/" +
2562                                   SensorsAsyncResp->chassisSubNode + "#/" +
2563                                   fieldName + "/"}});
2564                         sensorJson = &(tempArray.back());
2565                     }
2566                 }
2567 
2568                 if (sensorJson != nullptr)
2569                 {
2570                     objectInterfacesToJson(
2571                         sensorName, sensorType, SensorsAsyncResp,
2572                         objDictEntry.second, *sensorJson, inventoryItem);
2573                 }
2574             }
2575             if (SensorsAsyncResp.use_count() == 1)
2576             {
2577                 sortJSONResponse(SensorsAsyncResp);
2578                 if (SensorsAsyncResp->chassisSubNode == sensors::node::thermal)
2579                 {
2580                     populateFanRedundancy(SensorsAsyncResp);
2581                 }
2582             }
2583             BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2584         };
2585 
2586         // Find DBus object path that implements ObjectManager for the current
2587         // connection.  If no mapping found, default to "/".
2588         auto iter = objectMgrPaths->find(connection);
2589         const std::string& objectMgrPath =
2590             (iter != objectMgrPaths->end()) ? iter->second : "/";
2591         BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2592                          << objectMgrPath;
2593 
2594         crow::connections::systemBus->async_method_call(
2595             getManagedObjectsCb, connection, objectMgrPath,
2596             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2597     }
2598     BMCWEB_LOG_DEBUG << "getSensorData exit";
2599 }
2600 
2601 inline void processSensorList(
2602     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2603     std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
2604 {
2605     auto getConnectionCb =
2606         [SensorsAsyncResp, sensorNames](
2607             const boost::container::flat_set<std::string>& connections) {
2608             BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2609             auto getObjectManagerPathsCb =
2610                 [SensorsAsyncResp, sensorNames, connections](
2611                     std::shared_ptr<
2612                         boost::container::flat_map<std::string, std::string>>
2613                         objectMgrPaths) {
2614                     BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2615                     auto getInventoryItemsCb =
2616                         [SensorsAsyncResp, sensorNames, connections,
2617                          objectMgrPaths](
2618                             std::shared_ptr<std::vector<InventoryItem>>
2619                                 inventoryItems) {
2620                             BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2621                             // Get sensor data and store results in JSON
2622                             getSensorData(SensorsAsyncResp, sensorNames,
2623                                           connections, objectMgrPaths,
2624                                           inventoryItems);
2625                             BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2626                         };
2627 
2628                     // Get inventory items associated with sensors
2629                     getInventoryItems(SensorsAsyncResp, sensorNames,
2630                                       objectMgrPaths,
2631                                       std::move(getInventoryItemsCb));
2632 
2633                     BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2634                 };
2635 
2636             // Get mapping from connection names to the DBus object
2637             // paths that implement the ObjectManager interface
2638             getObjectManagerPaths(SensorsAsyncResp,
2639                                   std::move(getObjectManagerPathsCb));
2640             BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2641         };
2642 
2643     // Get set of connections that provide sensor values
2644     getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2645 }
2646 
2647 /**
2648  * @brief Entry point for retrieving sensors data related to requested
2649  *        chassis.
2650  * @param SensorsAsyncResp   Pointer to object holding response data
2651  */
2652 inline void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
2653 {
2654     BMCWEB_LOG_DEBUG << "getChassisData enter";
2655     auto getChassisCb =
2656         [SensorsAsyncResp](
2657             std::shared_ptr<boost::container::flat_set<std::string>>
2658                 sensorNames) {
2659             BMCWEB_LOG_DEBUG << "getChassisCb enter";
2660             processSensorList(SensorsAsyncResp, sensorNames);
2661             BMCWEB_LOG_DEBUG << "getChassisCb exit";
2662         };
2663     SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
2664 
2665     // Get set of sensors in chassis
2666     getChassis(SensorsAsyncResp, std::move(getChassisCb));
2667     BMCWEB_LOG_DEBUG << "getChassisData exit";
2668 }
2669 
2670 /**
2671  * @brief Find the requested sensorName in the list of all sensors supplied by
2672  * the chassis node
2673  *
2674  * @param sensorName   The sensor name supplied in the PATCH request
2675  * @param sensorsList  The list of sensors managed by the chassis node
2676  * @param sensorsModified  The list of sensors that were found as a result of
2677  *                         repeated calls to this function
2678  */
2679 inline bool findSensorNameUsingSensorPath(
2680     std::string_view sensorName,
2681     boost::container::flat_set<std::string>& sensorsList,
2682     boost::container::flat_set<std::string>& sensorsModified)
2683 {
2684     for (std::string_view chassisSensor : sensorsList)
2685     {
2686         std::size_t pos = chassisSensor.rfind("/");
2687         if (pos >= (chassisSensor.size() - 1))
2688         {
2689             continue;
2690         }
2691         std::string_view thisSensorName = chassisSensor.substr(pos + 1);
2692         if (thisSensorName == sensorName)
2693         {
2694             sensorsModified.emplace(chassisSensor);
2695             return true;
2696         }
2697     }
2698     return false;
2699 }
2700 
2701 /**
2702  * @brief Entry point for overriding sensor values of given sensor
2703  *
2704  * @param res   response object
2705  * @param allCollections   Collections extract from sensors' request patch info
2706  * @param chassisSubNode   Chassis Node for which the query has to happen
2707  */
2708 inline void setSensorsOverride(
2709     std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
2710     std::unordered_map<std::string, std::vector<nlohmann::json>>&
2711         allCollections)
2712 {
2713     BMCWEB_LOG_INFO << "setSensorsOverride for subNode"
2714                     << sensorAsyncResp->chassisSubNode << "\n";
2715 
2716     const char* propertyValueName;
2717     std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
2718     std::string memberId;
2719     double value;
2720     for (auto& collectionItems : allCollections)
2721     {
2722         if (collectionItems.first == "Temperatures")
2723         {
2724             propertyValueName = "ReadingCelsius";
2725         }
2726         else if (collectionItems.first == "Fans")
2727         {
2728             propertyValueName = "Reading";
2729         }
2730         else
2731         {
2732             propertyValueName = "ReadingVolts";
2733         }
2734         for (auto& item : collectionItems.second)
2735         {
2736             if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
2737                                      memberId, propertyValueName, value))
2738             {
2739                 return;
2740             }
2741             overrideMap.emplace(memberId,
2742                                 std::make_pair(value, collectionItems.first));
2743         }
2744     }
2745 
2746     auto getChassisSensorListCb = [sensorAsyncResp,
2747                                    overrideMap](const std::shared_ptr<
2748                                                 boost::container::flat_set<
2749                                                     std::string>>
2750                                                     sensorsList) {
2751         // Match sensor names in the PATCH request to those managed by the
2752         // chassis node
2753         const std::shared_ptr<boost::container::flat_set<std::string>>
2754             sensorNames =
2755                 std::make_shared<boost::container::flat_set<std::string>>();
2756         for (const auto& item : overrideMap)
2757         {
2758             const auto& sensor = item.first;
2759             if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2760                                                *sensorNames))
2761             {
2762                 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
2763                 messages::resourceNotFound(sensorAsyncResp->res,
2764                                            item.second.second, item.first);
2765                 return;
2766             }
2767         }
2768         // Get the connection to which the memberId belongs
2769         auto getObjectsWithConnectionCb =
2770             [sensorAsyncResp, overrideMap](
2771                 const boost::container::flat_set<std::string>& /*connections*/,
2772                 const std::set<std::pair<std::string, std::string>>&
2773                     objectsWithConnection) {
2774                 if (objectsWithConnection.size() != overrideMap.size())
2775                 {
2776                     BMCWEB_LOG_INFO
2777                         << "Unable to find all objects with proper connection "
2778                         << objectsWithConnection.size() << " requested "
2779                         << overrideMap.size() << "\n";
2780                     messages::resourceNotFound(
2781                         sensorAsyncResp->res,
2782                         sensorAsyncResp->chassisSubNode ==
2783                                 sensors::node::thermal
2784                             ? "Temperatures"
2785                             : "Voltages",
2786                         "Count");
2787                     return;
2788                 }
2789                 for (const auto& item : objectsWithConnection)
2790                 {
2791 
2792                     auto lastPos = item.first.rfind('/');
2793                     if (lastPos == std::string::npos)
2794                     {
2795                         messages::internalError(sensorAsyncResp->res);
2796                         return;
2797                     }
2798                     std::string sensorName = item.first.substr(lastPos + 1);
2799 
2800                     const auto& iterator = overrideMap.find(sensorName);
2801                     if (iterator == overrideMap.end())
2802                     {
2803                         BMCWEB_LOG_INFO << "Unable to find sensor object"
2804                                         << item.first << "\n";
2805                         messages::internalError(sensorAsyncResp->res);
2806                         return;
2807                     }
2808                     crow::connections::systemBus->async_method_call(
2809                         [sensorAsyncResp](const boost::system::error_code ec) {
2810                             if (ec)
2811                             {
2812                                 BMCWEB_LOG_DEBUG
2813                                     << "setOverrideValueStatus DBUS error: "
2814                                     << ec;
2815                                 messages::internalError(sensorAsyncResp->res);
2816                                 return;
2817                             }
2818                         },
2819                         item.second, item.first,
2820                         "org.freedesktop.DBus.Properties", "Set",
2821                         "xyz.openbmc_project.Sensor.Value", "Value",
2822                         std::variant<double>(iterator->second.first));
2823                 }
2824             };
2825         // Get object with connection for the given sensor name
2826         getObjectsWithConnection(sensorAsyncResp, sensorNames,
2827                                  std::move(getObjectsWithConnectionCb));
2828     };
2829     // get full sensor list for the given chassisId and cross verify the sensor.
2830     getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2831 }
2832 
2833 inline bool isOverridingAllowed(const std::string& manufacturingModeStatus)
2834 {
2835     if (manufacturingModeStatus ==
2836         "xyz.openbmc_project.Control.Security.SpecialMode.Modes.Manufacturing")
2837     {
2838         return true;
2839     }
2840 
2841 #ifdef BMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE
2842     if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
2843                                    "SpecialMode.Modes.ValidationUnsecure")
2844     {
2845         return true;
2846     }
2847 
2848 #endif
2849 
2850     return false;
2851 }
2852 
2853 /**
2854  * @brief Entry point for Checking the manufacturing mode before doing sensor
2855  * override values of given sensor
2856  *
2857  * @param res   response object
2858  * @param allCollections   Collections extract from sensors' request patch info
2859  * @param chassisSubNode   Chassis Node for which the query has to happen
2860  */
2861 inline void checkAndDoSensorsOverride(
2862     std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
2863     std::unordered_map<std::string, std::vector<nlohmann::json>>&
2864         allCollections)
2865 {
2866     BMCWEB_LOG_INFO << "checkAndDoSensorsOverride for subnode"
2867                     << sensorAsyncResp->chassisSubNode << "\n";
2868 
2869     const std::array<std::string, 1> interfaces = {
2870         "xyz.openbmc_project.Security.SpecialMode"};
2871 
2872     crow::connections::systemBus->async_method_call(
2873         [sensorAsyncResp, allCollections](const boost::system::error_code ec2,
2874                                           const GetSubTreeType& resp) mutable {
2875             if (ec2)
2876             {
2877                 BMCWEB_LOG_DEBUG
2878                     << "Error in querying GetSubTree with Object Mapper. "
2879                     << ec2;
2880                 messages::internalError(sensorAsyncResp->res);
2881                 return;
2882             }
2883 #ifdef BMCWEB_INSECURE_UNRESTRICTED_SENSOR_OVERRIDE
2884             // Proceed with sensor override
2885             setSensorsOverride(sensorAsyncResp, allCollections);
2886             return;
2887 #endif
2888 
2889             if (resp.size() != 1)
2890             {
2891                 BMCWEB_LOG_WARNING
2892                     << "Overriding sensor value is not allowed - Internal "
2893                        "error in querying SpecialMode property.";
2894                 messages::internalError(sensorAsyncResp->res);
2895                 return;
2896             }
2897             const std::string& path = resp[0].first;
2898             const std::string& serviceName = resp[0].second.begin()->first;
2899 
2900             if (path.empty() || serviceName.empty())
2901             {
2902                 BMCWEB_LOG_DEBUG
2903                     << "Path or service name is returned as empty. ";
2904                 messages::internalError(sensorAsyncResp->res);
2905                 return;
2906             }
2907 
2908             // Sensor override is allowed only in manufacturing mode or
2909             // validation unsecure mode .
2910             crow::connections::systemBus->async_method_call(
2911                 [sensorAsyncResp, allCollections,
2912                  path](const boost::system::error_code ec,
2913                        std::variant<std::string>& getManufactMode) mutable {
2914                     if (ec)
2915                     {
2916                         BMCWEB_LOG_DEBUG
2917                             << "Error in querying Special mode property " << ec;
2918                         messages::internalError(sensorAsyncResp->res);
2919                         return;
2920                     }
2921 
2922                     const std::string* manufacturingModeStatus =
2923                         std::get_if<std::string>(&getManufactMode);
2924 
2925                     if (nullptr == manufacturingModeStatus)
2926                     {
2927                         BMCWEB_LOG_DEBUG << "Sensor override mode is not "
2928                                             "Enabled. Returning ... ";
2929                         messages::internalError(sensorAsyncResp->res);
2930                         return;
2931                     }
2932 
2933                     if (isOverridingAllowed(*manufacturingModeStatus))
2934                     {
2935                         BMCWEB_LOG_INFO << "Manufacturing mode is Enabled. "
2936                                            "Proceeding further... ";
2937                         setSensorsOverride(sensorAsyncResp, allCollections);
2938                     }
2939                     else
2940                     {
2941                         BMCWEB_LOG_WARNING
2942                             << "Manufacturing mode is not Enabled...can't "
2943                                "Override the sensor value. ";
2944 
2945                         messages::actionNotSupported(
2946                             sensorAsyncResp->res,
2947                             "Overriding of Sensor Value for non "
2948                             "manufacturing mode");
2949                         return;
2950                     }
2951                 },
2952                 serviceName, path, "org.freedesktop.DBus.Properties", "Get",
2953                 "xyz.openbmc_project.Security.SpecialMode", "SpecialMode");
2954         },
2955 
2956         "xyz.openbmc_project.ObjectMapper",
2957         "/xyz/openbmc_project/object_mapper",
2958         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 5, interfaces);
2959 }
2960 
2961 /**
2962  * @brief Retrieves mapping of Redfish URIs to sensor value property to D-Bus
2963  * path of the sensor.
2964  *
2965  * Function builds valid Redfish response for sensor query of given chassis and
2966  * node. It then builds metadata about Redfish<->D-Bus correlations and provides
2967  * it to caller in a callback.
2968  *
2969  * @param chassis   Chassis for which retrieval should be performed
2970  * @param node  Node (group) of sensors. See sensors::node for supported values
2971  * @param mapComplete   Callback to be called with retrieval result
2972  */
2973 inline void retrieveUriToDbusMap(const std::string& chassis,
2974                                  const std::string& node,
2975                                  SensorsAsyncResp::DataCompleteCb&& mapComplete)
2976 {
2977     auto typesIt = sensors::dbus::types.find(node);
2978     if (typesIt == sensors::dbus::types.end())
2979     {
2980         BMCWEB_LOG_ERROR << "Wrong node provided : " << node;
2981         mapComplete(boost::beast::http::status::bad_request, {});
2982         return;
2983     }
2984 
2985     auto respBuffer = std::make_shared<crow::Response>();
2986     auto callback =
2987         [respBuffer, mapCompleteCb{std::move(mapComplete)}](
2988             const boost::beast::http::status status,
2989             const boost::container::flat_map<std::string, std::string>&
2990                 uriToDbus) { mapCompleteCb(status, uriToDbus); };
2991 
2992     auto resp = std::make_shared<SensorsAsyncResp>(
2993         *respBuffer, chassis, typesIt->second, node, std::move(callback));
2994     getChassisData(resp);
2995 }
2996 
2997 class SensorCollection : public Node
2998 {
2999   public:
3000     SensorCollection(App& app) :
3001         Node(app, "/redfish/v1/Chassis/<str>/Sensors/", std::string())
3002     {
3003         entityPrivileges = {
3004             {boost::beast::http::verb::get, {{"Login"}}},
3005             {boost::beast::http::verb::head, {{"Login"}}},
3006             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3007             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3008             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3009             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3010     }
3011 
3012   private:
3013     void doGet(crow::Response& res, const crow::Request&,
3014                const std::vector<std::string>& params) override
3015     {
3016         BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
3017         if (params.size() != 1)
3018         {
3019             BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
3020             messages::internalError(res);
3021             res.end();
3022             return;
3023         }
3024 
3025         const std::string& chassisId = params[0];
3026         std::shared_ptr<SensorsAsyncResp> asyncResp =
3027             std::make_shared<SensorsAsyncResp>(
3028                 res, chassisId, sensors::dbus::types.at(sensors::node::sensors),
3029                 sensors::node::sensors);
3030 
3031         auto getChassisCb =
3032             [asyncResp](std::shared_ptr<boost::container::flat_set<std::string>>
3033                             sensorNames) {
3034                 BMCWEB_LOG_DEBUG << "getChassisCb enter";
3035 
3036                 nlohmann::json& entriesArray =
3037                     asyncResp->res.jsonValue["Members"];
3038                 for (auto& sensor : *sensorNames)
3039                 {
3040                     BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
3041 
3042                     std::size_t lastPos = sensor.rfind("/");
3043                     if (lastPos == std::string::npos ||
3044                         lastPos + 1 >= sensor.size())
3045                     {
3046                         BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
3047                         messages::internalError(asyncResp->res);
3048                         return;
3049                     }
3050                     std::string sensorName = sensor.substr(lastPos + 1);
3051                     entriesArray.push_back(
3052                         {{"@odata.id",
3053                           "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
3054                               asyncResp->chassisSubNode + "/" + sensorName}});
3055                 }
3056 
3057                 asyncResp->res.jsonValue["Members@odata.count"] =
3058                     entriesArray.size();
3059                 BMCWEB_LOG_DEBUG << "getChassisCb exit";
3060             };
3061 
3062         // Get set of sensors in chassis
3063         getChassis(asyncResp, std::move(getChassisCb));
3064         BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
3065     }
3066 };
3067 
3068 class Sensor : public Node
3069 {
3070   public:
3071     Sensor(App& app) :
3072         Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
3073              std::string())
3074     {
3075         entityPrivileges = {
3076             {boost::beast::http::verb::get, {{"Login"}}},
3077             {boost::beast::http::verb::head, {{"Login"}}},
3078             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3079             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3080             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3081             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3082     }
3083 
3084   private:
3085     void doGet(crow::Response& res, const crow::Request&,
3086                const std::vector<std::string>& params) override
3087     {
3088         BMCWEB_LOG_DEBUG << "Sensor doGet enter";
3089         if (params.size() != 2)
3090         {
3091             BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
3092             messages::internalError(res);
3093             res.end();
3094             return;
3095         }
3096         const std::string& chassisId = params[0];
3097         std::shared_ptr<SensorsAsyncResp> asyncResp =
3098             std::make_shared<SensorsAsyncResp>(res, chassisId,
3099                                                std::vector<const char*>(),
3100                                                sensors::node::sensors);
3101 
3102         const std::string& sensorName = params[1];
3103         const std::array<const char*, 1> interfaces = {
3104             "xyz.openbmc_project.Sensor.Value"};
3105 
3106         // Get a list of all of the sensors that implement Sensor.Value
3107         // and get the path and service name associated with the sensor
3108         crow::connections::systemBus->async_method_call(
3109             [asyncResp, sensorName](const boost::system::error_code ec,
3110                                     const GetSubTreeType& subtree) {
3111                 BMCWEB_LOG_DEBUG << "respHandler1 enter";
3112                 if (ec)
3113                 {
3114                     messages::internalError(asyncResp->res);
3115                     BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
3116                                      << "Dbus error " << ec;
3117                     return;
3118                 }
3119 
3120                 GetSubTreeType::const_iterator it = std::find_if(
3121                     subtree.begin(), subtree.end(),
3122                     [sensorName](
3123                         const std::pair<
3124                             std::string,
3125                             std::vector<std::pair<std::string,
3126                                                   std::vector<std::string>>>>&
3127                             object) {
3128                         std::string_view sensor = object.first;
3129                         std::size_t lastPos = sensor.rfind("/");
3130                         if (lastPos == std::string::npos ||
3131                             lastPos + 1 >= sensor.size())
3132                         {
3133                             BMCWEB_LOG_ERROR << "Invalid sensor path: "
3134                                              << sensor;
3135                             return false;
3136                         }
3137                         std::string_view name = sensor.substr(lastPos + 1);
3138 
3139                         return name == sensorName;
3140                     });
3141 
3142                 if (it == subtree.end())
3143                 {
3144                     BMCWEB_LOG_ERROR << "Could not find path for sensor: "
3145                                      << sensorName;
3146                     messages::resourceNotFound(asyncResp->res, "Sensor",
3147                                                sensorName);
3148                     return;
3149                 }
3150                 std::string_view sensorPath = (*it).first;
3151                 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
3152                                  << sensorName << "': " << sensorPath;
3153 
3154                 const std::shared_ptr<boost::container::flat_set<std::string>>
3155                     sensorList = std::make_shared<
3156                         boost::container::flat_set<std::string>>();
3157 
3158                 sensorList->emplace(sensorPath);
3159                 processSensorList(asyncResp, sensorList);
3160                 BMCWEB_LOG_DEBUG << "respHandler1 exit";
3161             },
3162             "xyz.openbmc_project.ObjectMapper",
3163             "/xyz/openbmc_project/object_mapper",
3164             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3165             "/xyz/openbmc_project/sensors", 2, interfaces);
3166     }
3167 };
3168 
3169 } // namespace redfish
3170