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