xref: /openbmc/phosphor-pid-control/dbus/dbushelper.cpp (revision 11a1edcd204b87c6258592dc47f3d04e426e66e8)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2017 Google Inc
3 
4 #include "config.h"
5 
6 #include "dbushelper.hpp"
7 
8 #include "dbushelper_interface.hpp"
9 #include "dbusutil.hpp"
10 
11 #include <phosphor-logging/log.hpp>
12 #include <sdbusplus/bus.hpp>
13 #include <sdbusplus/exception.hpp>
14 #include <xyz/openbmc_project/ObjectMapper/common.hpp>
15 #include <xyz/openbmc_project/Sensor/Value/client.hpp>
16 
17 #include <cstdint>
18 #include <map>
19 #include <stdexcept>
20 #include <string>
21 #include <variant>
22 #include <vector>
23 
24 using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
25 using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
26 
27 namespace pid_control
28 {
29 
30 using Property = std::string;
31 using Value = std::variant<int64_t, double, std::string, bool>;
32 using PropertyMap = std::map<Property, Value>;
33 
34 using namespace phosphor::logging;
35 
36 /* TODO(venture): Basically all phosphor apps need this, maybe it should be a
37  * part of sdbusplus.  There is an old version in libmapper.
38  */
getService(const std::string & intf,const std::string & path)39 std::string DbusHelper::getService(const std::string& intf,
40                                    const std::string& path)
41 {
42     auto mapper = _bus.new_method_call(
43         ObjectMapper::default_service, ObjectMapper::instance_path,
44         ObjectMapper::interface, ObjectMapper::method_names::get_object);
45 
46     mapper.append(path);
47     mapper.append(std::vector<std::string>({intf}));
48 
49     std::map<std::string, std::vector<std::string>> response;
50 
51     try
52     {
53         auto responseMsg = _bus.call(mapper);
54 
55         responseMsg.read(response);
56     }
57     catch (const sdbusplus::exception_t& ex)
58     {
59         log<level::ERR>("ObjectMapper call failure",
60                         entry("WHAT=%s", ex.what()));
61         throw;
62     }
63 
64     if (response.begin() == response.end())
65     {
66         throw std::runtime_error("Unable to find Object: " + path);
67     }
68 
69     return response.begin()->first;
70 }
71 
getProperties(const std::string & service,const std::string & path,SensorProperties * prop)72 void DbusHelper::getProperties(const std::string& service,
73                                const std::string& path, SensorProperties* prop)
74 {
75     auto pimMsg = _bus.new_method_call(service.c_str(), path.c_str(),
76                                        propertiesintf, "GetAll");
77 
78     pimMsg.append(SensorValue::interface);
79 
80     PropertyMap propMap;
81 
82     try
83     {
84         auto valueResponseMsg = _bus.call(pimMsg);
85         valueResponseMsg.read(propMap);
86     }
87     catch (const sdbusplus::exception_t& ex)
88     {
89         log<level::ERR>("GetAll Properties Failed",
90                         entry("WHAT=%s", ex.what()));
91         throw;
92     }
93 
94     // The PropertyMap returned will look like this because it's always
95     // reading a Sensor.Value interface.
96     // a{sv} 3:
97     // "Value" x 24875
98     // "Unit" s "xyz.openbmc_project.Sensor.Value.Unit.DegreesC"
99     // "Scale" x -3
100 
101     // If no error was set, the values should all be there.
102     auto findUnit = propMap.find(SensorValue::property_names::unit);
103     if (findUnit != propMap.end())
104     {
105         prop->unit = std::get<std::string>(findUnit->second);
106     }
107     // TODO: in PDI there is no such 'Scale' property on the Sensor.Value
108     // interface
109     auto findScale = propMap.find("Scale");
110     auto findMax = propMap.find(SensorValue::property_names::max_value);
111     auto findMin = propMap.find(SensorValue::property_names::min_value);
112 
113     prop->min = 0;
114     prop->max = 0;
115     prop->scale = 0;
116     if (findScale != propMap.end())
117     {
118         prop->scale = std::get<int64_t>(findScale->second);
119     }
120     if (findMax != propMap.end())
121     {
122         prop->max = std::visit(VariantToDoubleVisitor(), findMax->second);
123     }
124     if (findMin != propMap.end())
125     {
126         prop->min = std::visit(VariantToDoubleVisitor(), findMin->second);
127     }
128 
129     prop->value = std::visit(VariantToDoubleVisitor(),
130                              propMap[SensorValue::property_names::value]);
131 
132     bool available = true;
133     try
134     {
135         getProperty(service, path, StateDecoratorAvailability::interface,
136                     StateDecoratorAvailability::property_names::available,
137                     available);
138     }
139     catch (const sdbusplus::exception_t& ex)
140     {
141         // unsupported Available property, leaving reading at 'True'
142     }
143     prop->available = available;
144 
145     return;
146 }
147 
thresholdsAsserted(const std::string & service,const std::string & path)148 bool DbusHelper::thresholdsAsserted(const std::string& service,
149                                     const std::string& path)
150 {
151     auto critical = _bus.new_method_call(service.c_str(), path.c_str(),
152                                          propertiesintf, "GetAll");
153     critical.append(SensorThresholdCritical::interface);
154     PropertyMap criticalMap;
155 
156     try
157     {
158         auto msg = _bus.call(critical);
159         msg.read(criticalMap);
160     }
161     catch (const sdbusplus::exception_t&)
162     {
163         // do nothing, sensors don't have to expose critical thresholds
164 #ifndef UNC_FAILSAFE
165         return false;
166 #endif
167     }
168 
169     auto findCriticalLow = criticalMap.find(
170         SensorThresholdCritical::property_names::critical_alarm_low);
171     auto findCriticalHigh = criticalMap.find(
172         SensorThresholdCritical::property_names::critical_alarm_high);
173 
174     bool asserted = false;
175     if (findCriticalLow != criticalMap.end())
176     {
177         asserted = std::get<bool>(findCriticalLow->second);
178     }
179 
180     // as we are catching properties changed, a sensor could theoretically jump
181     // from one threshold to the other in one event, so check both thresholds
182     if (!asserted && findCriticalHigh != criticalMap.end())
183     {
184         asserted = std::get<bool>(findCriticalHigh->second);
185     }
186 #ifdef UNC_FAILSAFE
187     if (!asserted)
188     {
189         auto warning = _bus.new_method_call(service.c_str(), path.c_str(),
190                                             propertiesintf, "GetAll");
191         warning.append(warningThreshInf);
192         PropertyMap warningMap;
193 
194         try
195         {
196             auto msg = _bus.call(warning);
197             msg.read(warningMap);
198         }
199         catch (const sdbusplus::exception_t&)
200         {
201             // sensors don't have to expose non-critical thresholds
202             return false;
203         }
204         auto findWarningHigh = warningMap.find(
205             SensorThresholdWarning::property_names::warning_alarm_high);
206 
207         if (findWarningHigh != warningMap.end())
208         {
209             asserted = std::get<bool>(findWarningHigh->second);
210         }
211     }
212 #endif
213     return asserted;
214 }
215 
216 } // namespace pid_control
217