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