xref: /openbmc/bmcweb/features/redfish/lib/sensors.hpp (revision 34dd179e402364ff94b697cf16d4dd99e49541f3)
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 <math.h>
19 
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <boost/algorithm/string/split.hpp>
22 #include <boost/container/flat_map.hpp>
23 #include <boost/range/algorithm/replace_copy_if.hpp>
24 #include <dbus_singleton.hpp>
25 #include <utils/json_utils.hpp>
26 #include <variant>
27 
28 namespace redfish
29 {
30 
31 using GetSubTreeType = std::vector<
32     std::pair<std::string,
33               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
34 
35 using SensorVariant = std::variant<int64_t, double, uint32_t, bool>;
36 
37 using ManagedObjectsVectorType = std::vector<std::pair<
38     sdbusplus::message::object_path,
39     boost::container::flat_map<
40         std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
41 
42 /**
43  * SensorsAsyncResp
44  * Gathers data needed for response processing after async calls are done
45  */
46 class SensorsAsyncResp
47 {
48   public:
49     SensorsAsyncResp(crow::Response& response, const std::string& chassisId,
50                      const std::initializer_list<const char*> types,
51                      const std::string& subNode) :
52         res(response),
53         chassisId(chassisId), types(types), chassisSubNode(subNode)
54     {
55     }
56 
57     ~SensorsAsyncResp()
58     {
59         if (res.result() == boost::beast::http::status::internal_server_error)
60         {
61             // Reset the json object to clear out any data that made it in
62             // before the error happened todo(ed) handle error condition with
63             // proper code
64             res.jsonValue = nlohmann::json::object();
65         }
66         res.end();
67     }
68 
69     crow::Response& res;
70     std::string chassisId{};
71     const std::vector<const char*> types;
72     std::string chassisSubNode{};
73 };
74 
75 /**
76  * @brief Get objects with connection necessary for sensors
77  * @param SensorsAsyncResp Pointer to object holding response data
78  * @param sensorNames Sensors retrieved from chassis
79  * @param callback Callback for processing gathered connections
80  */
81 template <typename Callback>
82 void getObjectsWithConnection(
83     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
84     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
85     Callback&& callback)
86 {
87     BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
88     const std::string path = "/xyz/openbmc_project/sensors";
89     const std::array<std::string, 1> interfaces = {
90         "xyz.openbmc_project.Sensor.Value"};
91 
92     // Response handler for parsing objects subtree
93     auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
94                         sensorNames](const boost::system::error_code ec,
95                                      const GetSubTreeType& subtree) {
96         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
97         if (ec)
98         {
99             messages::internalError(SensorsAsyncResp->res);
100             BMCWEB_LOG_ERROR
101                 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
102             return;
103         }
104 
105         BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
106 
107         // Make unique list of connections only for requested sensor types and
108         // found in the chassis
109         boost::container::flat_set<std::string> connections;
110         std::set<std::pair<std::string, std::string>> objectsWithConnection;
111         // Intrinsic to avoid malloc.  Most systems will have < 8 sensor
112         // producers
113         connections.reserve(8);
114 
115         BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
116         for (const std::string& tsensor : *sensorNames)
117         {
118             BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
119         }
120 
121         for (const std::pair<
122                  std::string,
123                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
124                  object : subtree)
125         {
126             if (sensorNames->find(object.first) != sensorNames->end())
127             {
128                 for (const std::pair<std::string, std::vector<std::string>>&
129                          objData : object.second)
130                 {
131                     BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
132                     connections.insert(objData.first);
133                     objectsWithConnection.insert(
134                         std::make_pair(object.first, objData.first));
135                 }
136             }
137         }
138         BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
139         callback(std::move(connections), std::move(objectsWithConnection));
140         BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
141     };
142     // Make call to ObjectMapper to find all sensors objects
143     crow::connections::systemBus->async_method_call(
144         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
145         "/xyz/openbmc_project/object_mapper",
146         "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
147     BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
148 }
149 
150 /**
151  * @brief Create connections necessary for sensors
152  * @param SensorsAsyncResp Pointer to object holding response data
153  * @param sensorNames Sensors retrieved from chassis
154  * @param callback Callback for processing gathered connections
155  */
156 template <typename Callback>
157 void getConnections(
158     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
159     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
160     Callback&& callback)
161 {
162     auto objectsWithConnectionCb =
163         [callback](const boost::container::flat_set<std::string>& connections,
164                    const std::set<std::pair<std::string, std::string>>&
165                        objectsWithConnection) {
166             callback(std::move(connections));
167         };
168     getObjectsWithConnection(SensorsAsyncResp, sensorNames,
169                              std::move(objectsWithConnectionCb));
170 }
171 
172 /**
173  * @brief Shrinks the list of sensors for processing
174  * @param SensorsAysncResp  The class holding the Redfish response
175  * @param allSensors  A list of all the sensors associated to the
176  * chassis element (i.e. baseboard, front panel, etc...)
177  * @param activeSensors A list that is a reduction of the incoming
178  * allSensors list.  Eliminate Thermal sensors when a Power request is
179  * made, and eliminate Power sensors when a Thermal request is made.
180  */
181 void reduceSensorList(
182     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
183     const std::vector<std::string>* allSensors,
184     std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
185 {
186     if (SensorsAsyncResp == nullptr)
187     {
188         return;
189     }
190     if ((allSensors == nullptr) || (activeSensors == nullptr))
191     {
192         messages::resourceNotFound(
193             SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
194             SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
195                                                           : "Voltages");
196 
197         return;
198     }
199     if (allSensors->empty())
200     {
201         // Nothing to do, the activeSensors object is also empty
202         return;
203     }
204 
205     for (const char* type : SensorsAsyncResp->types)
206     {
207         for (const std::string& sensor : *allSensors)
208         {
209             if (boost::starts_with(sensor, type))
210             {
211                 activeSensors->emplace(sensor);
212             }
213         }
214     }
215 }
216 
217 /**
218  * @brief Retrieves requested chassis sensors and redundancy data from DBus .
219  * @param SensorsAsyncResp   Pointer to object holding response data
220  * @param callback  Callback for next step in gathered sensor processing
221  */
222 template <typename Callback>
223 void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
224                 Callback&& callback)
225 {
226     BMCWEB_LOG_DEBUG << "getChassis enter";
227     const std::array<const char*, 3> interfaces = {
228         "xyz.openbmc_project.Inventory.Item.Board",
229         "xyz.openbmc_project.Inventory.Item.Chassis",
230         "xyz.openbmc_project.Inventory.Item.PowerSupply"};
231     auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
232                            const boost::system::error_code ec,
233                            const std::vector<std::string>& chassisPaths) {
234         BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
235         if (ec)
236         {
237             BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
238             messages::internalError(sensorsAsyncResp->res);
239             return;
240         }
241 
242         const std::string* chassisPath = nullptr;
243         std::string chassisName;
244         for (const std::string& chassis : chassisPaths)
245         {
246             std::size_t lastPos = chassis.rfind("/");
247             if (lastPos == std::string::npos)
248             {
249                 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
250                 continue;
251             }
252             chassisName = chassis.substr(lastPos + 1);
253             if (chassisName == sensorsAsyncResp->chassisId)
254             {
255                 chassisPath = &chassis;
256                 break;
257             }
258         }
259         if (chassisPath == nullptr)
260         {
261             messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
262                                        sensorsAsyncResp->chassisId);
263             return;
264         }
265 
266         const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
267         if (chassisSubNode == "Power")
268         {
269             sensorsAsyncResp->res.jsonValue["@odata.type"] =
270                 "#Power.v1_5_2.Power";
271         }
272         else if (chassisSubNode == "Thermal")
273         {
274             sensorsAsyncResp->res.jsonValue["@odata.type"] =
275                 "#Thermal.v1_4_0.Thermal";
276             sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
277             sensorsAsyncResp->res.jsonValue["Temperatures"] =
278                 nlohmann::json::array();
279         }
280         sensorsAsyncResp->res.jsonValue["@odata.id"] =
281             "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
282             chassisSubNode;
283 
284         sensorsAsyncResp->res.jsonValue["@odata.context"] =
285             "/redfish/v1/$metadata#" + chassisSubNode + "." + chassisSubNode;
286         sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
287         sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
288 
289         // Get the list of sensors for this Chassis element
290         std::string sensorPath = *chassisPath + "/sensors";
291         crow::connections::systemBus->async_method_call(
292             [sensorsAsyncResp, callback{std::move(callback)}](
293                 const boost::system::error_code ec,
294                 const std::variant<std::vector<std::string>>&
295                     variantEndpoints) {
296                 if (ec)
297                 {
298                     if (ec.value() != EBADR)
299                     {
300                         messages::internalError(sensorsAsyncResp->res);
301                         return;
302                     }
303                 }
304                 const std::vector<std::string>* nodeSensorList =
305                     std::get_if<std::vector<std::string>>(&(variantEndpoints));
306                 if (nodeSensorList == nullptr)
307                 {
308                     messages::resourceNotFound(
309                         sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
310                         sensorsAsyncResp->chassisSubNode == "Thermal"
311                             ? "Temperatures"
312                             : "Voltages");
313                     return;
314                 }
315                 const std::shared_ptr<boost::container::flat_set<std::string>>
316                     culledSensorList = std::make_shared<
317                         boost::container::flat_set<std::string>>();
318                 reduceSensorList(sensorsAsyncResp, nodeSensorList,
319                                  culledSensorList);
320                 callback(culledSensorList);
321             },
322             "xyz.openbmc_project.ObjectMapper", sensorPath,
323             "org.freedesktop.DBus.Properties", "Get",
324             "xyz.openbmc_project.Association", "endpoints");
325     };
326 
327     // Get the Chassis Collection
328     crow::connections::systemBus->async_method_call(
329         respHandler, "xyz.openbmc_project.ObjectMapper",
330         "/xyz/openbmc_project/object_mapper",
331         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
332         "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
333     BMCWEB_LOG_DEBUG << "getChassis exit";
334 }
335 
336 /**
337  * @brief Finds all DBus object paths that implement ObjectManager.
338  *
339  * Creates a mapping from the associated connection name to the object path.
340  *
341  * Finds the object paths asynchronously.  Invokes callback when information has
342  * been obtained.
343  *
344  * The callback must have the following signature:
345  *   @code
346  *   callback(const boost::container::flat_map<std::string,
347  *            std::string>& objectMgrPaths)
348  *   @endcode
349  *
350  * @param sensorsAsyncResp Pointer to object holding response data.
351  * @param callback Callback to invoke when object paths obtained.
352  */
353 template <typename Callback>
354 void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
355                            Callback&& callback)
356 {
357     BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
358     const std::array<std::string, 1> interfaces = {
359         "org.freedesktop.DBus.ObjectManager"};
360 
361     // Response handler for GetSubTree DBus method
362     auto respHandler = [callback{std::move(callback)},
363                         SensorsAsyncResp](const boost::system::error_code ec,
364                                           const GetSubTreeType& subtree) {
365         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
366         if (ec)
367         {
368             messages::internalError(SensorsAsyncResp->res);
369             BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
370                              << ec;
371             return;
372         }
373 
374         // Loop over returned object paths
375         boost::container::flat_map<std::string, std::string> objectMgrPaths;
376         for (const std::pair<
377                  std::string,
378                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
379                  object : subtree)
380         {
381             // Loop over connections for current object path
382             const std::string& objectPath = object.first;
383             for (const std::pair<std::string, std::vector<std::string>>&
384                      objData : object.second)
385             {
386                 // Add mapping from connection to object path
387                 const std::string& connection = objData.first;
388                 objectMgrPaths[connection] = objectPath;
389                 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
390                                  << objectPath;
391             }
392         }
393         callback(std::move(objectMgrPaths));
394         BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
395     };
396 
397     // Query mapper for all DBus object paths that implement ObjectManager
398     crow::connections::systemBus->async_method_call(
399         std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
400         "/xyz/openbmc_project/object_mapper",
401         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
402         interfaces);
403     BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
404 }
405 
406 /**
407  * @brief Retrieves the health from a sensor .
408  * @param interfacesDict   Map of all sensor interfaces
409  */
410 
411 static std::string getHealth(
412     const boost::container::flat_map<
413         std::string, boost::container::flat_map<std::string, SensorVariant>>&
414         interfacesDict)
415 {
416     auto criticalThresholdIt =
417         interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
418     if (criticalThresholdIt != interfacesDict.end())
419     {
420         auto thresholdHighIt =
421             criticalThresholdIt->second.find("CriticalAlarmHigh");
422         auto thresholdLowIt =
423             criticalThresholdIt->second.find("CriticalAlarmLow");
424         if (thresholdHighIt != criticalThresholdIt->second.end())
425         {
426             const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
427             if (asserted == nullptr)
428             {
429                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
430             }
431             else if (*asserted)
432             {
433                 return "Critical";
434             }
435         }
436         if (thresholdLowIt != criticalThresholdIt->second.end())
437         {
438             const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
439             if (asserted == nullptr)
440             {
441                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
442             }
443             else if (*asserted)
444             {
445                 return "Critical";
446             }
447         }
448     }
449 
450     auto warningThresholdIt =
451         interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
452     if (warningThresholdIt != interfacesDict.end())
453     {
454         auto thresholdHighIt =
455             warningThresholdIt->second.find("WarningAlarmHigh");
456         auto thresholdLowIt =
457             warningThresholdIt->second.find("WarningAlarmLow");
458         if (thresholdHighIt != warningThresholdIt->second.end())
459         {
460             const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
461             if (asserted == nullptr)
462             {
463                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
464             }
465             else if (*asserted)
466             {
467                 return "Warning";
468             }
469         }
470         if (thresholdLowIt != warningThresholdIt->second.end())
471         {
472             const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
473             if (asserted == nullptr)
474             {
475                 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
476             }
477             else if (*asserted)
478             {
479                 return "Warning";
480             }
481         }
482     }
483     return "OK";
484 }
485 
486 /**
487  * @brief Builds a json sensor representation of a sensor.
488  * @param sensorName  The name of the sensor to be built
489  * @param sensorType  The type (temperature, fan_tach, etc) of the sensor to
490  * build
491  * @param interfacesDict  A dictionary of the interfaces and properties of said
492  * interfaces to be built from
493  * @param sensor_json  The json object to fill
494  */
495 void objectInterfacesToJson(
496     const std::string& sensorName, const std::string& sensorType,
497     const boost::container::flat_map<
498         std::string, boost::container::flat_map<std::string, SensorVariant>>&
499         interfacesDict,
500     nlohmann::json& sensor_json)
501 {
502     // We need a value interface before we can do anything with it
503     auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
504     if (valueIt == interfacesDict.end())
505     {
506         BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
507         return;
508     }
509 
510     // Assume values exist as is (10^0 == 1) if no scale exists
511     int64_t scaleMultiplier = 0;
512 
513     auto scaleIt = valueIt->second.find("Scale");
514     // If a scale exists, pull value as int64, and use the scaling.
515     if (scaleIt != valueIt->second.end())
516     {
517         const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
518         if (int64Value != nullptr)
519         {
520             scaleMultiplier = *int64Value;
521         }
522     }
523 
524     sensor_json["MemberId"] = sensorName;
525     sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
526 
527     sensor_json["Status"]["State"] = "Enabled";
528     sensor_json["Status"]["Health"] = getHealth(interfacesDict);
529 
530     // Parameter to set to override the type we get from dbus, and force it to
531     // int, regardless of what is available.  This is used for schemas like fan,
532     // that require integers, not floats.
533     bool forceToInt = false;
534 
535     const char* unit = "Reading";
536     if (sensorType == "temperature")
537     {
538         unit = "ReadingCelsius";
539         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
540         // TODO(ed) Documentation says that path should be type fan_tach,
541         // implementation seems to implement fan
542     }
543     else if (sensorType == "fan" || sensorType == "fan_tach")
544     {
545         unit = "Reading";
546         sensor_json["ReadingUnits"] = "RPM";
547         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
548         forceToInt = true;
549     }
550     else if (sensorType == "fan_pwm")
551     {
552         unit = "Reading";
553         sensor_json["ReadingUnits"] = "Percent";
554         sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
555         forceToInt = true;
556     }
557     else if (sensorType == "voltage")
558     {
559         unit = "ReadingVolts";
560         sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
561     }
562     else if (sensorType == "power")
563     {
564         std::string sensorNameLower =
565             boost::algorithm::to_lower_copy(sensorName);
566 
567         if (!sensorName.compare("total_power"))
568         {
569             unit = "PowerConsumedWatts";
570         }
571         else if (sensorNameLower.find("input") != std::string::npos)
572         {
573             unit = "PowerInputWatts";
574         }
575         else
576         {
577             unit = "PowerOutputWatts";
578         }
579     }
580     else
581     {
582         BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
583         return;
584     }
585     // Map of dbus interface name, dbus property name and redfish property_name
586     std::vector<std::tuple<const char*, const char*, const char*>> properties;
587     properties.reserve(7);
588 
589     properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
590 
591     // If sensor type doesn't map to Redfish PowerSupply, add threshold props
592     if ((sensorType != "current") && (sensorType != "power"))
593     {
594         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
595                                 "WarningHigh", "UpperThresholdNonCritical");
596         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
597                                 "WarningLow", "LowerThresholdNonCritical");
598         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
599                                 "CriticalHigh", "UpperThresholdCritical");
600         properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
601                                 "CriticalLow", "LowerThresholdCritical");
602     }
603 
604     // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
605 
606     if (sensorType == "temperature")
607     {
608         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
609                                 "MinReadingRangeTemp");
610         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
611                                 "MaxReadingRangeTemp");
612     }
613     else if ((sensorType != "current") && (sensorType != "power"))
614     {
615         // Sensor type doesn't map to Redfish PowerSupply; add min/max props
616         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
617                                 "MinReadingRange");
618         properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
619                                 "MaxReadingRange");
620     }
621 
622     for (const std::tuple<const char*, const char*, const char*>& p :
623          properties)
624     {
625         auto interfaceProperties = interfacesDict.find(std::get<0>(p));
626         if (interfaceProperties != interfacesDict.end())
627         {
628             auto valueIt = interfaceProperties->second.find(std::get<1>(p));
629             if (valueIt != interfaceProperties->second.end())
630             {
631                 const SensorVariant& valueVariant = valueIt->second;
632                 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
633                 // Attempt to pull the int64 directly
634                 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
635 
636                 const double* doubleValue = std::get_if<double>(&valueVariant);
637                 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
638                 double temp = 0.0;
639                 if (int64Value != nullptr)
640                 {
641                     temp = *int64Value;
642                 }
643                 else if (doubleValue != nullptr)
644                 {
645                     temp = *doubleValue;
646                 }
647                 else if (uValue != nullptr)
648                 {
649                     temp = *uValue;
650                 }
651                 else
652                 {
653                     BMCWEB_LOG_ERROR
654                         << "Got value interface that wasn't int or double";
655                     continue;
656                 }
657                 temp = temp * std::pow(10, scaleMultiplier);
658                 if (forceToInt)
659                 {
660                     valueIt = static_cast<int64_t>(temp);
661                 }
662                 else
663                 {
664                     valueIt = temp;
665                 }
666             }
667         }
668     }
669     BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
670 }
671 
672 static void
673     populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
674 {
675     crow::connections::systemBus->async_method_call(
676         [sensorsAsyncResp](const boost::system::error_code ec,
677                            const GetSubTreeType& resp) {
678             if (ec)
679             {
680                 return; // don't have to have this interface
681             }
682             for (const std::pair<std::string,
683                                  std::vector<std::pair<
684                                      std::string, std::vector<std::string>>>>&
685                      pathPair : resp)
686             {
687                 const std::string& path = pathPair.first;
688                 const std::vector<
689                     std::pair<std::string, std::vector<std::string>>>& objDict =
690                     pathPair.second;
691                 if (objDict.empty())
692                 {
693                     continue; // this should be impossible
694                 }
695 
696                 const std::string& owner = objDict.begin()->first;
697                 crow::connections::systemBus->async_method_call(
698                     [path, owner,
699                      sensorsAsyncResp](const boost::system::error_code ec,
700                                        std::variant<std::vector<std::string>>
701                                            variantEndpoints) {
702                         if (ec)
703                         {
704                             return; // if they don't have an association we
705                                     // can't tell what chassis is
706                         }
707                         // verify part of the right chassis
708                         auto endpoints = std::get_if<std::vector<std::string>>(
709                             &variantEndpoints);
710 
711                         if (endpoints == nullptr)
712                         {
713                             BMCWEB_LOG_ERROR << "Invalid association interface";
714                             messages::internalError(sensorsAsyncResp->res);
715                             return;
716                         }
717 
718                         auto found = std::find_if(
719                             endpoints->begin(), endpoints->end(),
720                             [sensorsAsyncResp](const std::string& entry) {
721                                 return entry.find(
722                                            sensorsAsyncResp->chassisId) !=
723                                        std::string::npos;
724                             });
725 
726                         if (found == endpoints->end())
727                         {
728                             return;
729                         }
730                         crow::connections::systemBus->async_method_call(
731                             [path, sensorsAsyncResp](
732                                 const boost::system::error_code ec,
733                                 const boost::container::flat_map<
734                                     std::string,
735                                     std::variant<uint8_t,
736                                                  std::vector<std::string>,
737                                                  std::string>>& ret) {
738                                 if (ec)
739                                 {
740                                     return; // don't have to have this
741                                             // interface
742                                 }
743                                 auto findFailures = ret.find("AllowedFailures");
744                                 auto findCollection = ret.find("Collection");
745                                 auto findStatus = ret.find("Status");
746 
747                                 if (findFailures == ret.end() ||
748                                     findCollection == ret.end() ||
749                                     findStatus == ret.end())
750                                 {
751                                     BMCWEB_LOG_ERROR
752                                         << "Invalid redundancy interface";
753                                     messages::internalError(
754                                         sensorsAsyncResp->res);
755                                     return;
756                                 }
757 
758                                 auto allowedFailures = std::get_if<uint8_t>(
759                                     &(findFailures->second));
760                                 auto collection =
761                                     std::get_if<std::vector<std::string>>(
762                                         &(findCollection->second));
763                                 auto status = std::get_if<std::string>(
764                                     &(findStatus->second));
765 
766                                 if (allowedFailures == nullptr ||
767                                     collection == nullptr || status == nullptr)
768                                 {
769 
770                                     BMCWEB_LOG_ERROR
771                                         << "Invalid redundancy interface "
772                                            "types";
773                                     messages::internalError(
774                                         sensorsAsyncResp->res);
775                                     return;
776                                 }
777                                 size_t lastSlash = path.rfind("/");
778                                 if (lastSlash == std::string::npos)
779                                 {
780                                     // this should be impossible
781                                     messages::internalError(
782                                         sensorsAsyncResp->res);
783                                     return;
784                                 }
785                                 std::string name = path.substr(lastSlash + 1);
786                                 std::replace(name.begin(), name.end(), '_',
787                                              ' ');
788 
789                                 std::string health;
790 
791                                 if (boost::ends_with(*status, "Full"))
792                                 {
793                                     health = "OK";
794                                 }
795                                 else if (boost::ends_with(*status, "Degraded"))
796                                 {
797                                     health = "Warning";
798                                 }
799                                 else
800                                 {
801                                     health = "Critical";
802                                 }
803                                 std::vector<nlohmann::json> redfishCollection;
804                                 const auto& fanRedfish =
805                                     sensorsAsyncResp->res.jsonValue["Fans"];
806                                 for (const std::string& item : *collection)
807                                 {
808                                     lastSlash = item.rfind("/");
809                                     // make a copy as collection is const
810                                     std::string itemName =
811                                         item.substr(lastSlash + 1);
812                                     /*
813                                     todo(ed): merge patch that fixes the names
814                                     std::replace(itemName.begin(),
815                                                  itemName.end(), '_', ' ');*/
816                                     auto schemaItem = std::find_if(
817                                         fanRedfish.begin(), fanRedfish.end(),
818                                         [itemName](const nlohmann::json& fan) {
819                                             return fan["MemberId"] == itemName;
820                                         });
821                                     if (schemaItem != fanRedfish.end())
822                                     {
823                                         redfishCollection.push_back(
824                                             {{"@odata.id",
825                                               (*schemaItem)["@odata.id"]}});
826                                     }
827                                     else
828                                     {
829                                         BMCWEB_LOG_ERROR
830                                             << "failed to find fan in schema";
831                                         messages::internalError(
832                                             sensorsAsyncResp->res);
833                                         return;
834                                     }
835                                 }
836 
837                                 auto& resp = sensorsAsyncResp->res
838                                                  .jsonValue["Redundancy"];
839                                 resp.push_back(
840                                     {{"@odata.id",
841                                       "/refish/v1/Chassis/" +
842                                           sensorsAsyncResp->chassisId + "/" +
843                                           sensorsAsyncResp->chassisSubNode +
844                                           "#/Redundancy/" +
845                                           std::to_string(resp.size())},
846                                      {"@odata.type",
847                                       "#Redundancy.v1_3_2.Redundancy"},
848                                      {"MinNumNeeded",
849                                       collection->size() - *allowedFailures},
850                                      {"MemberId", name},
851                                      {"Mode", "N+m"},
852                                      {"Name", name},
853                                      {"RedundancySet", redfishCollection},
854                                      {"Status",
855                                       {{"Health", health},
856                                        {"State", "Enabled"}}}});
857                             },
858                             owner, path, "org.freedesktop.DBus.Properties",
859                             "GetAll",
860                             "xyz.openbmc_project.Control.FanRedundancy");
861                     },
862                     "xyz.openbmc_project.ObjectMapper", path + "/inventory",
863                     "org.freedesktop.DBus.Properties", "Get",
864                     "xyz.openbmc_project.Association", "endpoints");
865             }
866         },
867         "xyz.openbmc_project.ObjectMapper",
868         "/xyz/openbmc_project/object_mapper",
869         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
870         "/xyz/openbmc_project/control", 2,
871         std::array<const char*, 1>{
872             "xyz.openbmc_project.Control.FanRedundancy"});
873 }
874 
875 void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
876 {
877     nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
878     std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
879     if (SensorsAsyncResp->chassisSubNode == "Power")
880     {
881         sensorHeaders = {"Voltages", "PowerSupplies"};
882     }
883     for (const std::string& sensorGroup : sensorHeaders)
884     {
885         nlohmann::json::iterator entry = response.find(sensorGroup);
886         if (entry != response.end())
887         {
888             std::sort(entry->begin(), entry->end(),
889                       [](nlohmann::json& c1, nlohmann::json& c2) {
890                           return c1["Name"] < c2["Name"];
891                       });
892 
893             // add the index counts to the end of each entry
894             size_t count = 0;
895             for (nlohmann::json& sensorJson : *entry)
896             {
897                 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
898                 if (odata == sensorJson.end())
899                 {
900                     continue;
901                 }
902                 std::string* value = odata->get_ptr<std::string*>();
903                 if (value != nullptr)
904                 {
905                     *value += std::to_string(count);
906                     count++;
907                 }
908             }
909         }
910     }
911 }
912 
913 /**
914  * @brief Gets the values of the specified sensors.
915  *
916  * Stores the results as JSON in the SensorsAsyncResp.
917  *
918  * Gets the sensor values asynchronously.  Stores the results later when the
919  * information has been obtained.
920  *
921  * The sensorNames set contains all sensors for the current chassis.
922  * SensorsAsyncResp contains the requested sensor types.  Only sensors of a
923  * requested type are included in the JSON output.
924  *
925  * To minimize the number of DBus calls, the DBus method
926  * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
927  * values of all sensors provided by a connection (service).
928  *
929  * The connections set contains all the connections that provide sensor values.
930  *
931  * The objectMgrPaths map contains mappings from a connection name to the
932  * corresponding DBus object path that implements ObjectManager.
933  *
934  * @param SensorsAsyncResp Pointer to object holding response data.
935  * @param sensorNames All sensors within the current chassis.
936  * @param connections Connections that provide sensor values.
937  * @param objectMgrPaths Mappings from connection name to DBus object path that
938  * implements ObjectManager.
939  */
940 void getSensorData(
941     std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
942     const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
943     const boost::container::flat_set<std::string>& connections,
944     const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
945 {
946     BMCWEB_LOG_DEBUG << "getSensorData enter";
947     // Get managed objects from all services exposing sensors
948     for (const std::string& connection : connections)
949     {
950         // Response handler to process managed objects
951         auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames](
952                                        const boost::system::error_code ec,
953                                        ManagedObjectsVectorType& resp) {
954             BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
955             if (ec)
956             {
957                 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
958                 messages::internalError(SensorsAsyncResp->res);
959                 return;
960             }
961             // Go through all objects and update response with sensor data
962             for (const auto& objDictEntry : resp)
963             {
964                 const std::string& objPath =
965                     static_cast<const std::string&>(objDictEntry.first);
966                 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
967                                  << objPath;
968 
969                 std::vector<std::string> split;
970                 // Reserve space for
971                 // /xyz/openbmc_project/sensors/<name>/<subname>
972                 split.reserve(6);
973                 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
974                 if (split.size() < 6)
975                 {
976                     BMCWEB_LOG_ERROR << "Got path that isn't long enough "
977                                      << objPath;
978                     continue;
979                 }
980                 // These indexes aren't intuitive, as boost::split puts an empty
981                 // string at the beginning
982                 const std::string& sensorType = split[4];
983                 const std::string& sensorName = split[5];
984                 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
985                                  << " sensorType " << sensorType;
986                 if (sensorNames->find(objPath) == sensorNames->end())
987                 {
988                     BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
989                     continue;
990                 }
991 
992                 const char* fieldName = nullptr;
993                 if (sensorType == "temperature")
994                 {
995                     fieldName = "Temperatures";
996                 }
997                 else if (sensorType == "fan" || sensorType == "fan_tach" ||
998                          sensorType == "fan_pwm")
999                 {
1000                     fieldName = "Fans";
1001                 }
1002                 else if (sensorType == "voltage")
1003                 {
1004                     fieldName = "Voltages";
1005                 }
1006                 else if (sensorType == "current")
1007                 {
1008                     fieldName = "PowerSupplies";
1009                 }
1010                 else if (sensorType == "power")
1011                 {
1012                     if (!sensorName.compare("total_power"))
1013                     {
1014                         fieldName = "PowerControl";
1015                     }
1016                     else
1017                     {
1018                         fieldName = "PowerSupplies";
1019                     }
1020                 }
1021                 else
1022                 {
1023                     BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
1024                                      << sensorType;
1025                     continue;
1026                 }
1027 
1028                 nlohmann::json& tempArray =
1029                     SensorsAsyncResp->res.jsonValue[fieldName];
1030 
1031                 if (fieldName == "PowerSupplies" && !tempArray.empty())
1032                 {
1033                     // Power supplies put multiple "sensors" into a single power
1034                     // supply entry, so only create the first one
1035                 }
1036                 else
1037                 {
1038                     tempArray.push_back(
1039                         {{"@odata.id", "/redfish/v1/Chassis/" +
1040                                            SensorsAsyncResp->chassisId + "/" +
1041                                            SensorsAsyncResp->chassisSubNode +
1042                                            "#/" + fieldName + "/"}});
1043                 }
1044                 nlohmann::json& sensorJson = tempArray.back();
1045 
1046                 objectInterfacesToJson(sensorName, sensorType,
1047                                        objDictEntry.second, sensorJson);
1048             }
1049             if (SensorsAsyncResp.use_count() == 1)
1050             {
1051                 sortJSONResponse(SensorsAsyncResp);
1052                 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1053                 {
1054                     populateFanRedundancy(SensorsAsyncResp);
1055                 }
1056             }
1057             BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1058         };
1059 
1060         // Find DBus object path that implements ObjectManager for the current
1061         // connection.  If no mapping found, default to "/".
1062         auto iter = objectMgrPaths.find(connection);
1063         const std::string& objectMgrPath =
1064             (iter != objectMgrPaths.end()) ? iter->second : "/";
1065         BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1066                          << objectMgrPath;
1067 
1068         crow::connections::systemBus->async_method_call(
1069             getManagedObjectsCb, connection, objectMgrPath,
1070             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1071     };
1072     BMCWEB_LOG_DEBUG << "getSensorData exit";
1073 }
1074 
1075 /**
1076  * @brief Entry point for retrieving sensors data related to requested
1077  *        chassis.
1078  * @param SensorsAsyncResp   Pointer to object holding response data
1079  */
1080 void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1081 {
1082     BMCWEB_LOG_DEBUG << "getChassisData enter";
1083     auto getChassisCb =
1084         [SensorsAsyncResp](
1085             std::shared_ptr<boost::container::flat_set<std::string>>
1086                 sensorNames) {
1087             BMCWEB_LOG_DEBUG << "getChassisCb enter";
1088             auto getConnectionCb =
1089                 [SensorsAsyncResp,
1090                  sensorNames](const boost::container::flat_set<std::string>&
1091                                   connections) {
1092                     BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1093                     auto getObjectManagerPathsCb =
1094                         [SensorsAsyncResp, sensorNames, connections](
1095                             const boost::container::flat_map<
1096                                 std::string, std::string>& objectMgrPaths) {
1097                             BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
1098                             // Get sensor data and store results in JSON
1099                             // response
1100                             getSensorData(SensorsAsyncResp, sensorNames,
1101                                           connections, objectMgrPaths);
1102                             BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1103                         };
1104 
1105                     // Get mapping from connection names to the DBus object
1106                     // paths that implement the ObjectManager interface
1107                     getObjectManagerPaths(SensorsAsyncResp,
1108                                           std::move(getObjectManagerPathsCb));
1109                     BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1110                 };
1111 
1112             // Get set of connections that provide sensor values
1113             getConnections(SensorsAsyncResp, sensorNames,
1114                            std::move(getConnectionCb));
1115             BMCWEB_LOG_DEBUG << "getChassisCb exit";
1116         };
1117     SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
1118 
1119     // Get set of sensors in chassis
1120     getChassis(SensorsAsyncResp, std::move(getChassisCb));
1121     BMCWEB_LOG_DEBUG << "getChassisData exit";
1122 };
1123 
1124 /**
1125  * @brief Find the requested sensorName in the list of all sensors supplied by
1126  * the chassis node
1127  *
1128  * @param sensorName   The sensor name supplied in the PATCH request
1129  * @param sensorsList  The list of sensors managed by the chassis node
1130  * @param sensorsModified  The list of sensors that were found as a result of
1131  *                         repeated calls to this function
1132  */
1133 bool findSensorNameUsingSensorPath(
1134     std::string_view sensorName,
1135     boost::container::flat_set<std::string>& sensorsList,
1136     boost::container::flat_set<std::string>& sensorsModified)
1137 {
1138     for (std::string_view chassisSensor : sensorsList)
1139     {
1140         std::size_t pos = chassisSensor.rfind("/");
1141         if (pos >= (chassisSensor.size() - 1))
1142         {
1143             continue;
1144         }
1145         std::string_view thisSensorName = chassisSensor.substr(pos + 1);
1146         if (thisSensorName == sensorName)
1147         {
1148             sensorsModified.emplace(chassisSensor);
1149             return true;
1150         }
1151     }
1152     return false;
1153 }
1154 
1155 /**
1156  * @brief Entry point for overriding sensor values of given sensor
1157  *
1158  * @param res   response object
1159  * @param req   request object
1160  * @param params   parameter passed for CRUD
1161  * @param typeList   TypeList of sensors for the resource queried
1162  * @param chassisSubNode   Chassis Node for which the query has to happen
1163  */
1164 void setSensorOverride(crow::Response& res, const crow::Request& req,
1165                        const std::vector<std::string>& params,
1166                        const std::initializer_list<const char*> typeList,
1167                        const std::string& chassisSubNode)
1168 {
1169 
1170     // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1171     // override) based on another d-bus announcement to be more generic.
1172     if (params.size() != 1)
1173     {
1174         messages::internalError(res);
1175         res.end();
1176         return;
1177     }
1178 
1179     std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1180     std::optional<std::vector<nlohmann::json>> temperatureCollections;
1181     std::optional<std::vector<nlohmann::json>> fanCollections;
1182     std::vector<nlohmann::json> voltageCollections;
1183     BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1184                     << "\n";
1185 
1186     if (chassisSubNode == "Thermal")
1187     {
1188         if (!json_util::readJson(req, res, "Temperatures",
1189                                  temperatureCollections, "Fans",
1190                                  fanCollections))
1191         {
1192             return;
1193         }
1194         if (!temperatureCollections && !fanCollections)
1195         {
1196             messages::resourceNotFound(res, "Thermal",
1197                                        "Temperatures / Voltages");
1198             res.end();
1199             return;
1200         }
1201         if (temperatureCollections)
1202         {
1203             allCollections.emplace("Temperatures",
1204                                    *std::move(temperatureCollections));
1205         }
1206         if (fanCollections)
1207         {
1208             allCollections.emplace("Fans", *std::move(fanCollections));
1209         }
1210     }
1211     else if (chassisSubNode == "Power")
1212     {
1213         if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1214         {
1215             return;
1216         }
1217         allCollections.emplace("Voltages", std::move(voltageCollections));
1218     }
1219     else
1220     {
1221         res.result(boost::beast::http::status::not_found);
1222         res.end();
1223         return;
1224     }
1225 
1226     const char* propertyValueName;
1227     std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
1228     std::string memberId;
1229     double value;
1230     for (auto& collectionItems : allCollections)
1231     {
1232         if (collectionItems.first == "Temperatures")
1233         {
1234             propertyValueName = "ReadingCelsius";
1235         }
1236         else if (collectionItems.first == "Fans")
1237         {
1238             propertyValueName = "Reading";
1239         }
1240         else
1241         {
1242             propertyValueName = "ReadingVolts";
1243         }
1244         for (auto& item : collectionItems.second)
1245         {
1246             if (!json_util::readJson(item, res, "MemberId", memberId,
1247                                      propertyValueName, value))
1248             {
1249                 return;
1250             }
1251             overrideMap.emplace(memberId,
1252                                 std::make_pair(value, collectionItems.first));
1253         }
1254     }
1255     const std::string& chassisName = params[0];
1256     auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1257         res, chassisName, typeList, chassisSubNode);
1258     auto getChassisSensorListCb = [sensorAsyncResp,
1259                                    overrideMap](const std::shared_ptr<
1260                                                 boost::container::flat_set<
1261                                                     std::string>>
1262                                                     sensorsList) {
1263         // Match sensor names in the PATCH request to those managed by the
1264         // chassis node
1265         const std::shared_ptr<boost::container::flat_set<std::string>>
1266             sensorNames =
1267                 std::make_shared<boost::container::flat_set<std::string>>();
1268         for (const auto& item : overrideMap)
1269         {
1270             const auto& sensor = item.first;
1271             if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
1272                                                *sensorNames))
1273             {
1274                 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
1275                 messages::resourceNotFound(sensorAsyncResp->res,
1276                                            item.second.second, item.first);
1277                 return;
1278             }
1279         }
1280         // Get the connection to which the memberId belongs
1281         auto getObjectsWithConnectionCb =
1282             [sensorAsyncResp, overrideMap](
1283                 const boost::container::flat_set<std::string>& connections,
1284                 const std::set<std::pair<std::string, std::string>>&
1285                     objectsWithConnection) {
1286                 if (objectsWithConnection.size() != overrideMap.size())
1287                 {
1288                     BMCWEB_LOG_INFO
1289                         << "Unable to find all objects with proper connection "
1290                         << objectsWithConnection.size() << " requested "
1291                         << overrideMap.size() << "\n";
1292                     messages::resourceNotFound(
1293                         sensorAsyncResp->res,
1294                         sensorAsyncResp->chassisSubNode == "Thermal"
1295                             ? "Temperatures"
1296                             : "Voltages",
1297                         "Count");
1298                     return;
1299                 }
1300                 for (const auto& item : objectsWithConnection)
1301                 {
1302 
1303                     auto lastPos = item.first.rfind('/');
1304                     if (lastPos == std::string::npos)
1305                     {
1306                         messages::internalError(sensorAsyncResp->res);
1307                         return;
1308                     }
1309                     std::string sensorName = item.first.substr(lastPos + 1);
1310 
1311                     const auto& iterator = overrideMap.find(sensorName);
1312                     if (iterator == overrideMap.end())
1313                     {
1314                         BMCWEB_LOG_INFO << "Unable to find sensor object"
1315                                         << item.first << "\n";
1316                         messages::internalError(sensorAsyncResp->res);
1317                         return;
1318                     }
1319                     crow::connections::systemBus->async_method_call(
1320                         [sensorAsyncResp](const boost::system::error_code ec) {
1321                             if (ec)
1322                             {
1323                                 BMCWEB_LOG_DEBUG
1324                                     << "setOverrideValueStatus DBUS error: "
1325                                     << ec;
1326                                 messages::internalError(sensorAsyncResp->res);
1327                                 return;
1328                             }
1329                         },
1330                         item.second, item.first,
1331                         "org.freedesktop.DBus.Properties", "Set",
1332                         "xyz.openbmc_project.Sensor.Value", "Value",
1333                         sdbusplus::message::variant<double>(
1334                             iterator->second.first));
1335                 }
1336             };
1337         // Get object with connection for the given sensor name
1338         getObjectsWithConnection(sensorAsyncResp, sensorNames,
1339                                  std::move(getObjectsWithConnectionCb));
1340     };
1341     // get full sensor list for the given chassisId and cross verify the sensor.
1342     getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1343 }
1344 
1345 } // namespace redfish
1346