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