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