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     std::unique_ptr<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, std::move(helper),
78                                          settings, failed, path, redundancy);
79 }
80 
81 DbusPassive::DbusPassive(
82     sdbusplus::bus::bus& bus, const std::string& type, const std::string& id,
83     std::unique_ptr<DbusHelperInterface> helper,
84     const struct SensorProperties& settings, bool failed,
85     const std::string& path,
86     const std::shared_ptr<DbusPassiveRedundancy>& redundancy) :
87     ReadInterface(),
88     _bus(bus), _signal(bus, getMatch(type, id).c_str(), dbusHandleSignal, this),
89     _id(id), _helper(std::move(helper)), _failed(failed), path(path),
90     redundancy(redundancy)
91 
92 {
93     _scale = settings.scale;
94     _value = settings.value * pow(10, _scale);
95     _min = settings.min * pow(10, _scale);
96     _max = settings.max * pow(10, _scale);
97     _updated = std::chrono::high_resolution_clock::now();
98 }
99 
100 ReadReturn DbusPassive::read(void)
101 {
102     std::lock_guard<std::mutex> guard(_lock);
103 
104     struct ReadReturn r = {_value, _updated};
105 
106     return r;
107 }
108 
109 void DbusPassive::setValue(double value)
110 {
111     std::lock_guard<std::mutex> guard(_lock);
112 
113     _value = value;
114     _updated = std::chrono::high_resolution_clock::now();
115 }
116 
117 bool DbusPassive::getFailed(void) const
118 {
119     if (redundancy)
120     {
121         const std::set<std::string>& failures = redundancy->getFailed();
122         if (failures.find(path) != failures.end())
123         {
124             return true;
125         }
126     }
127 
128     return _failed || !_functional;
129 }
130 
131 void DbusPassive::setFailed(bool value)
132 {
133     _failed = value;
134 }
135 
136 void DbusPassive::setFunctional(bool value)
137 {
138     _functional = value;
139 }
140 
141 int64_t DbusPassive::getScale(void)
142 {
143     return _scale;
144 }
145 
146 std::string DbusPassive::getID(void)
147 {
148     return _id;
149 }
150 
151 double DbusPassive::getMax(void)
152 {
153     return _max;
154 }
155 
156 double DbusPassive::getMin(void)
157 {
158     return _min;
159 }
160 
161 int handleSensorValue(sdbusplus::message::message& msg, DbusPassive* owner)
162 {
163     std::string msgSensor;
164     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
165 
166     msg.read(msgSensor, msgData);
167 
168     if (msgSensor == "xyz.openbmc_project.Sensor.Value")
169     {
170         auto valPropMap = msgData.find("Value");
171         if (valPropMap != msgData.end())
172         {
173             double value =
174                 std::visit(VariantToDoubleVisitor(), valPropMap->second);
175 
176             value *= std::pow(10, owner->getScale());
177 
178             scaleSensorReading(owner->getMin(), owner->getMax(), value);
179 
180             owner->setValue(value);
181         }
182     }
183     else if (msgSensor == "xyz.openbmc_project.Sensor.Threshold.Critical")
184     {
185         auto criticalAlarmLow = msgData.find("CriticalAlarmLow");
186         auto criticalAlarmHigh = msgData.find("CriticalAlarmHigh");
187         if (criticalAlarmHigh == msgData.end() &&
188             criticalAlarmLow == msgData.end())
189         {
190             return 0;
191         }
192 
193         bool asserted = false;
194         if (criticalAlarmLow != msgData.end())
195         {
196             asserted = std::get<bool>(criticalAlarmLow->second);
197         }
198 
199         // checking both as in theory you could de-assert one threshold and
200         // assert the other at the same moment
201         if (!asserted && criticalAlarmHigh != msgData.end())
202         {
203             asserted = std::get<bool>(criticalAlarmHigh->second);
204         }
205         owner->setFailed(asserted);
206     }
207     else if (msgSensor ==
208              "xyz.openbmc_project.State.Decorator.OperationalStatus")
209     {
210         auto functional = msgData.find("Functional");
211         if (functional == msgData.end())
212         {
213             return 0;
214         }
215         bool asserted = std::get<bool>(functional->second);
216         owner->setFunctional(asserted);
217     }
218 
219     return 0;
220 }
221 
222 int dbusHandleSignal(sd_bus_message* msg, void* usrData, sd_bus_error* err)
223 {
224     auto sdbpMsg = sdbusplus::message::message(msg);
225     DbusPassive* obj = static_cast<DbusPassive*>(usrData);
226 
227     return handleSensorValue(sdbpMsg, obj);
228 }
229 
230 } // namespace pid_control
231