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, SensorProperties* prop)
78 {
79     auto pimMsg = _bus.new_method_call(service.c_str(), path.c_str(),
80                                        propertiesintf, "GetAll");
81 
82     pimMsg.append(sensorintf);
83 
84     PropertyMap propMap;
85 
86     try
87     {
88         auto valueResponseMsg = _bus.call(pimMsg);
89         valueResponseMsg.read(propMap);
90     }
91     catch (const sdbusplus::exception::SdBusError& ex)
92     {
93         log<level::ERR>("GetAll Properties Failed",
94                         entry("WHAT=%s", ex.what()));
95         throw;
96     }
97 
98     // The PropertyMap returned will look like this because it's always
99     // reading a Sensor.Value interface.
100     // a{sv} 3:
101     // "Value" x 24875
102     // "Unit" s "xyz.openbmc_project.Sensor.Value.Unit.DegreesC"
103     // "Scale" x -3
104 
105     // If no error was set, the values should all be there.
106     auto findUnit = propMap.find("Unit");
107     if (findUnit != propMap.end())
108     {
109         prop->unit = std::get<std::string>(findUnit->second);
110     }
111     auto findScale = propMap.find("Scale");
112     auto findMax = propMap.find("MaxValue");
113     auto findMin = propMap.find("MinValue");
114 
115     prop->min = 0;
116     prop->max = 0;
117     prop->scale = 0;
118     if (findScale != propMap.end())
119     {
120         prop->scale = std::get<int64_t>(findScale->second);
121     }
122     if (findMax != propMap.end())
123     {
124         prop->max = std::visit(VariantToDoubleVisitor(), findMax->second);
125     }
126     if (findMin != propMap.end())
127     {
128         prop->min = std::visit(VariantToDoubleVisitor(), findMin->second);
129     }
130 
131     prop->value = std::visit(VariantToDoubleVisitor(), propMap["Value"]);
132 
133     return;
134 }
135 
136 bool DbusHelper::thresholdsAsserted(const std::string& service,
137                                     const std::string& path)
138 {
139 
140     auto critical = _bus.new_method_call(service.c_str(), path.c_str(),
141                                          propertiesintf, "GetAll");
142     critical.append(criticalThreshInf);
143     PropertyMap criticalMap;
144 
145     try
146     {
147         auto msg = _bus.call(critical);
148         msg.read(criticalMap);
149     }
150     catch (sdbusplus::exception_t&)
151     {
152         // do nothing, sensors don't have to expose critical thresholds
153         return false;
154     }
155 
156     auto findCriticalLow = criticalMap.find("CriticalAlarmLow");
157     auto findCriticalHigh = criticalMap.find("CriticalAlarmHigh");
158 
159     bool asserted = false;
160     if (findCriticalLow != criticalMap.end())
161     {
162         asserted = std::get<bool>(findCriticalLow->second);
163     }
164 
165     // as we are catching properties changed, a sensor could theoretically jump
166     // from one threshold to the other in one event, so check both thresholds
167     if (!asserted && findCriticalHigh != criticalMap.end())
168     {
169         asserted = std::get<bool>(findCriticalHigh->second);
170     }
171     return asserted;
172 }
173 
174 } // namespace pid_control
175