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