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 #include "dbuspassive.hpp"
17 
18 #include "dbushelper_interface.hpp"
19 #include "dbuspassiveredundancy.hpp"
20 #include "dbusutil.hpp"
21 #include "util.hpp"
22 
23 #include <sdbusplus/bus.hpp>
24 
25 #include <chrono>
26 #include <cmath>
27 #include <memory>
28 #include <mutex>
29 #include <string>
30 #include <variant>
31 
32 namespace pid_control
33 {
34 
35 std::unique_ptr<ReadInterface> DbusPassive::createDbusPassive(
36     sdbusplus::bus::bus& bus, const std::string& type, const std::string& id,
37     DbusHelperInterface* helper, const conf::SensorConfig* info,
38     const std::shared_ptr<DbusPassiveRedundancy>& redundancy)
39 {
40     if (helper == nullptr)
41     {
42         return nullptr;
43     }
44     if (!validType(type))
45     {
46         return nullptr;
47     }
48 
49     /* Need to get the scale and initial value */
50     auto tempBus = sdbusplus::bus::new_system();
51 
52     /* service == busname */
53     std::string path = getSensorPath(type, id);
54 
55     struct SensorProperties settings;
56     bool failed;
57 
58     try
59     {
60         std::string service = helper->getService(tempBus, sensorintf, path);
61 
62         helper->getProperties(tempBus, service, path, &settings);
63         failed = helper->thresholdsAsserted(tempBus, service, path);
64     }
65     catch (const std::exception& e)
66     {
67         return nullptr;
68     }
69 
70     /* if these values are zero, they're ignored. */
71     if (info->ignoreDbusMinMax)
72     {
73         settings.min = 0;
74         settings.max = 0;
75     }
76 
77     return std::make_unique<DbusPassive>(bus, type, id, helper, settings,
78                                          failed, path, redundancy);
79 }
80 
81 DbusPassive::DbusPassive(
82     sdbusplus::bus::bus& bus, const std::string& type, const std::string& id,
83     DbusHelperInterface* helper, const struct SensorProperties& settings,
84     bool failed, const std::string& path,
85     const std::shared_ptr<DbusPassiveRedundancy>& redundancy) :
86     ReadInterface(),
87     _bus(bus), _signal(bus, getMatch(type, id).c_str(), dbusHandleSignal, this),
88     _id(id), _helper(helper), _failed(failed), path(path),
89     redundancy(redundancy)
90 
91 {
92     _scale = settings.scale;
93     _value = settings.value * pow(10, _scale);
94     _min = settings.min * pow(10, _scale);
95     _max = settings.max * pow(10, _scale);
96     _updated = std::chrono::high_resolution_clock::now();
97 }
98 
99 ReadReturn DbusPassive::read(void)
100 {
101     std::lock_guard<std::mutex> guard(_lock);
102 
103     struct ReadReturn r = {_value, _updated};
104 
105     return r;
106 }
107 
108 void DbusPassive::setValue(double value)
109 {
110     std::lock_guard<std::mutex> guard(_lock);
111 
112     _value = value;
113     _updated = std::chrono::high_resolution_clock::now();
114 }
115 
116 bool DbusPassive::getFailed(void) const
117 {
118     if (redundancy)
119     {
120         const std::set<std::string>& failures = redundancy->getFailed();
121         if (failures.find(path) != failures.end())
122         {
123             return true;
124         }
125     }
126 
127     return _failed || !_functional;
128 }
129 
130 void DbusPassive::setFailed(bool value)
131 {
132     _failed = value;
133 }
134 
135 void DbusPassive::setFunctional(bool value)
136 {
137     _functional = value;
138 }
139 
140 int64_t DbusPassive::getScale(void)
141 {
142     return _scale;
143 }
144 
145 std::string DbusPassive::getID(void)
146 {
147     return _id;
148 }
149 
150 double DbusPassive::getMax(void)
151 {
152     return _max;
153 }
154 
155 double DbusPassive::getMin(void)
156 {
157     return _min;
158 }
159 
160 int handleSensorValue(sdbusplus::message::message& msg, DbusPassive* owner)
161 {
162     std::string msgSensor;
163     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
164 
165     msg.read(msgSensor, msgData);
166 
167     if (msgSensor == "xyz.openbmc_project.Sensor.Value")
168     {
169         auto valPropMap = msgData.find("Value");
170         if (valPropMap != msgData.end())
171         {
172             double value =
173                 std::visit(VariantToDoubleVisitor(), valPropMap->second);
174 
175             value *= std::pow(10, owner->getScale());
176 
177             scaleSensorReading(owner->getMin(), owner->getMax(), value);
178 
179             owner->setValue(value);
180         }
181     }
182     else if (msgSensor == "xyz.openbmc_project.Sensor.Threshold.Critical")
183     {
184         auto criticalAlarmLow = msgData.find("CriticalAlarmLow");
185         auto criticalAlarmHigh = msgData.find("CriticalAlarmHigh");
186         if (criticalAlarmHigh == msgData.end() &&
187             criticalAlarmLow == msgData.end())
188         {
189             return 0;
190         }
191 
192         bool asserted = false;
193         if (criticalAlarmLow != msgData.end())
194         {
195             asserted = std::get<bool>(criticalAlarmLow->second);
196         }
197 
198         // checking both as in theory you could de-assert one threshold and
199         // assert the other at the same moment
200         if (!asserted && criticalAlarmHigh != msgData.end())
201         {
202             asserted = std::get<bool>(criticalAlarmHigh->second);
203         }
204         owner->setFailed(asserted);
205     }
206     else if (msgSensor ==
207              "xyz.openbmc_project.State.Decorator.OperationalStatus")
208     {
209         auto functional = msgData.find("Functional");
210         if (functional == msgData.end())
211         {
212             return 0;
213         }
214         bool asserted = std::get<bool>(functional->second);
215         owner->setFunctional(asserted);
216     }
217 
218     return 0;
219 }
220 
221 int dbusHandleSignal(sd_bus_message* msg, void* usrData, sd_bus_error* err)
222 {
223     auto sdbpMsg = sdbusplus::message::message(msg);
224     DbusPassive* obj = static_cast<DbusPassive*>(usrData);
225 
226     return handleSensorValue(sdbpMsg, obj);
227 }
228 
229 } // namespace pid_control
230