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