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(sdbusplus::bus::bus& bus,
43                                    const std::string& intf,
44                                    const std::string& path)
45 {
46     auto mapper =
47         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
48                             "/xyz/openbmc_project/object_mapper",
49                             "xyz.openbmc_project.ObjectMapper", "GetObject");
50 
51     mapper.append(path);
52     mapper.append(std::vector<std::string>({intf}));
53 
54     std::map<std::string, std::vector<std::string>> response;
55 
56     try
57     {
58         auto responseMsg = bus.call(mapper);
59 
60         responseMsg.read(response);
61     }
62     catch (const sdbusplus::exception::SdBusError& ex)
63     {
64         log<level::ERR>("ObjectMapper call failure",
65                         entry("WHAT=%s", ex.what()));
66         throw;
67     }
68 
69     if (response.begin() == response.end())
70     {
71         throw std::runtime_error("Unable to find Object: " + path);
72     }
73 
74     return response.begin()->first;
75 }
76 
77 void DbusHelper::getProperties(sdbusplus::bus::bus& bus,
78                                const std::string& service,
79                                const std::string& path,
80                                struct 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::SdBusError& 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     return;
137 }
138 
139 bool DbusHelper::thresholdsAsserted(sdbusplus::bus::bus& bus,
140                                     const std::string& service,
141                                     const std::string& path)
142 {
143 
144     auto critical = bus.new_method_call(service.c_str(), path.c_str(),
145                                         propertiesintf, "GetAll");
146     critical.append(criticalThreshInf);
147     PropertyMap criticalMap;
148 
149     try
150     {
151         auto msg = bus.call(critical);
152         msg.read(criticalMap);
153     }
154     catch (sdbusplus::exception_t&)
155     {
156         // do nothing, sensors don't have to expose critical thresholds
157         return false;
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     return asserted;
176 }
177 
178 } // namespace pid_control
179