1 /**
2 * Copyright © 2021 IBM Corporation
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 "dbus_sensor.hpp"
18
19 #include <cmath>
20 #include <limits>
21 #include <utility>
22
23 namespace phosphor::power::regulators
24 {
25
26 /**
27 * Constants for current sensors.
28 *
29 * Values are in amperes.
30 */
31 constexpr double currentMinValue = 0.0;
32 constexpr double currentMaxValue = 500.0;
33 constexpr double currentHysteresis = 1.0;
34 constexpr const char* currentNamespace = "current";
35
36 /**
37 * Constants for power sensors.
38 *
39 * Values are in watts.
40 */
41 constexpr double powerMinValue = 0.0;
42 constexpr double powerMaxValue = 1000.0;
43 constexpr double powerHysteresis = 1.0;
44 constexpr const char* powerNamespace = "power";
45
46 /**
47 * Constants for temperature sensors.
48 *
49 * Values are in degrees Celsius.
50 */
51 constexpr double temperatureMinValue = -50.0;
52 constexpr double temperatureMaxValue = 250.0;
53 constexpr double temperatureHysteresis = 1.0;
54 constexpr const char* temperatureNamespace = "temperature";
55
56 /**
57 * Constants for voltage sensors.
58 *
59 * Values are in volts.
60 *
61 * Note the hysteresis value is very low. Small voltage changes can have a big
62 * impact in some systems. The sensors need to reflect these small changes.
63 */
64 constexpr double voltageMinValue = -15.0;
65 constexpr double voltageMaxValue = 15.0;
66 constexpr double voltageHysteresis = 0.001;
67 constexpr const char* voltageNamespace = "voltage";
68
DBusSensor(sdbusplus::bus_t & bus,const std::string & name,SensorType type,double value,const std::string & rail,const std::string & deviceInventoryPath,const std::string & chassisInventoryPath)69 DBusSensor::DBusSensor(sdbusplus::bus_t& bus, const std::string& name,
70 SensorType type, double value, const std::string& rail,
71 const std::string& deviceInventoryPath,
72 const std::string& chassisInventoryPath) :
73 bus{bus}, name{name}, type{type}, rail{rail}
74 {
75 // Get sensor properties that are based on the sensor type
76 std::string objectPath;
77 Unit unit;
78 double minValue, maxValue;
79 getTypeBasedProperties(objectPath, unit, minValue, maxValue);
80
81 // Get the D-Bus associations to create for this sensor
82 std::vector<AssocationTuple> associations =
83 getAssociations(deviceInventoryPath, chassisInventoryPath);
84
85 // Create the sdbusplus object that implements the D-Bus sensor interfaces.
86 // Skip emitting D-Bus signals until the object has been fully created.
87 dbusObject = std::make_unique<DBusSensorObject>(
88 bus, objectPath.c_str(), DBusSensorObject::action::defer_emit);
89
90 // Set properties of the Value interface
91 constexpr auto skipSignal = true;
92 dbusObject->value(value, skipSignal);
93 dbusObject->maxValue(maxValue, skipSignal);
94 dbusObject->minValue(minValue, skipSignal);
95 dbusObject->unit(unit, skipSignal);
96
97 // Set properties of the OperationalStatus interface
98 dbusObject->functional(true, skipSignal);
99
100 // Set properties of the Availability interface
101 dbusObject->available(true, skipSignal);
102
103 // Set properties on the Association.Definitions interface
104 dbusObject->associations(std::move(associations), skipSignal);
105
106 // Now emit signal that object has been created
107 dbusObject->emit_object_added();
108
109 // Set the last update time
110 setLastUpdateTime();
111 }
112
disable()113 void DBusSensor::disable()
114 {
115 // Set sensor value to NaN
116 setValueToNaN();
117
118 // Set the sensor to unavailable since it is disabled
119 dbusObject->available(false);
120
121 // Set the last update time
122 setLastUpdateTime();
123 }
124
setToErrorState()125 void DBusSensor::setToErrorState()
126 {
127 // Set sensor value to NaN
128 setValueToNaN();
129
130 // Set the sensor to non-functional since it could not be read
131 dbusObject->functional(false);
132
133 // Set the last update time
134 setLastUpdateTime();
135 }
136
setValue(double value)137 void DBusSensor::setValue(double value)
138 {
139 // Update value on D-Bus if necessary
140 if (shouldUpdateValue(value))
141 {
142 dbusObject->value(value);
143 }
144
145 // Set the sensor to functional since it has a valid value
146 dbusObject->functional(true);
147
148 // Set the sensor to available since it is not disabled
149 dbusObject->available(true);
150
151 // Set the last update time
152 setLastUpdateTime();
153 }
154
155 std::vector<AssocationTuple>
getAssociations(const std::string & deviceInventoryPath,const std::string & chassisInventoryPath)156 DBusSensor::getAssociations(const std::string& deviceInventoryPath,
157 const std::string& chassisInventoryPath)
158 {
159 std::vector<AssocationTuple> associations{};
160
161 // Add an association between the sensor and the chassis. This is used by
162 // the Redfish support to find all the sensors in a chassis.
163 associations.emplace_back(
164 std::make_tuple("chassis", "all_sensors", chassisInventoryPath));
165
166 // Add an association between the sensor and the voltage regulator device.
167 // This is used by the Redfish support to find the hardware/inventory item
168 // associated with a sensor.
169 associations.emplace_back(
170 std::make_tuple("inventory", "sensors", deviceInventoryPath));
171
172 return associations;
173 }
174
getTypeBasedProperties(std::string & objectPath,Unit & unit,double & minValue,double & maxValue)175 void DBusSensor::getTypeBasedProperties(std::string& objectPath, Unit& unit,
176 double& minValue, double& maxValue)
177 {
178 const char* typeNamespace{""};
179 switch (type)
180 {
181 case SensorType::iout:
182 typeNamespace = currentNamespace;
183 unit = Unit::Amperes;
184 minValue = currentMinValue;
185 maxValue = currentMaxValue;
186 updatePolicy = ValueUpdatePolicy::hysteresis;
187 hysteresis = currentHysteresis;
188 break;
189
190 case SensorType::iout_peak:
191 typeNamespace = currentNamespace;
192 unit = Unit::Amperes;
193 minValue = currentMinValue;
194 maxValue = currentMaxValue;
195 updatePolicy = ValueUpdatePolicy::highest;
196 break;
197
198 case SensorType::iout_valley:
199 typeNamespace = currentNamespace;
200 unit = Unit::Amperes;
201 minValue = currentMinValue;
202 maxValue = currentMaxValue;
203 updatePolicy = ValueUpdatePolicy::lowest;
204 break;
205
206 case SensorType::pout:
207 typeNamespace = powerNamespace;
208 unit = Unit::Watts;
209 minValue = powerMinValue;
210 maxValue = powerMaxValue;
211 updatePolicy = ValueUpdatePolicy::hysteresis;
212 hysteresis = powerHysteresis;
213 break;
214
215 case SensorType::temperature:
216 typeNamespace = temperatureNamespace;
217 unit = Unit::DegreesC;
218 minValue = temperatureMinValue;
219 maxValue = temperatureMaxValue;
220 updatePolicy = ValueUpdatePolicy::hysteresis;
221 hysteresis = temperatureHysteresis;
222 break;
223
224 case SensorType::temperature_peak:
225 typeNamespace = temperatureNamespace;
226 unit = Unit::DegreesC;
227 minValue = temperatureMinValue;
228 maxValue = temperatureMaxValue;
229 updatePolicy = ValueUpdatePolicy::highest;
230 break;
231
232 case SensorType::vout:
233 typeNamespace = voltageNamespace;
234 unit = Unit::Volts;
235 minValue = voltageMinValue;
236 maxValue = voltageMaxValue;
237 updatePolicy = ValueUpdatePolicy::hysteresis;
238 hysteresis = voltageHysteresis;
239 break;
240
241 case SensorType::vout_peak:
242 typeNamespace = voltageNamespace;
243 unit = Unit::Volts;
244 minValue = voltageMinValue;
245 maxValue = voltageMaxValue;
246 updatePolicy = ValueUpdatePolicy::highest;
247 break;
248
249 case SensorType::vout_valley:
250 default:
251 typeNamespace = voltageNamespace;
252 unit = Unit::Volts;
253 minValue = voltageMinValue;
254 maxValue = voltageMaxValue;
255 updatePolicy = ValueUpdatePolicy::lowest;
256 break;
257 }
258
259 // Build object path
260 objectPath = sensorsObjectPath;
261 objectPath += '/';
262 objectPath += typeNamespace;
263 objectPath += '/';
264 objectPath += name;
265 }
266
setValueToNaN()267 void DBusSensor::setValueToNaN()
268 {
269 // Get current value published on D-Bus
270 double currentValue = dbusObject->value();
271
272 // Check if current value is already NaN. We want to avoid an unnecessary
273 // PropertiesChanged signal. The generated C++ code for the Value interface
274 // does check whether the new value is different from the old one. However,
275 // it uses the equality operator, and NaN always returns false when compared
276 // to another NaN value.
277 if (!std::isnan(currentValue))
278 {
279 // Set value to NaN
280 dbusObject->value(std::numeric_limits<double>::quiet_NaN());
281 }
282 }
283
shouldUpdateValue(double value)284 bool DBusSensor::shouldUpdateValue(double value)
285 {
286 // Initially assume we should update the value
287 bool shouldUpdate{true};
288
289 // Get current value published on D-Bus
290 double currentValue = dbusObject->value();
291
292 // Update sensor if the current value is NaN. This indicates it was
293 // disabled or in an error state. Note: you cannot compare a variable to
294 // NaN directly using the equality operator; it will always return false.
295 if (std::isnan(currentValue))
296 {
297 shouldUpdate = true;
298 }
299 else
300 {
301 // Determine whether to update based on policy used by this sensor
302 switch (updatePolicy)
303 {
304 case ValueUpdatePolicy::hysteresis:
305 shouldUpdate = (std::abs(value - currentValue) >= hysteresis);
306 break;
307 case ValueUpdatePolicy::highest:
308 shouldUpdate = (value > currentValue);
309 break;
310 case ValueUpdatePolicy::lowest:
311 shouldUpdate = (value < currentValue);
312 break;
313 }
314 }
315
316 return shouldUpdate;
317 }
318
319 } // namespace phosphor::power::regulators
320