blob: 1fd4bf688455a267c92bd9924264963f14e0f842 [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();
109}
110
111void DBusSensor::disable()
112{
113 // Set sensor value to NaN
114 setValueToNaN();
115
116 // Set the sensor to unavailable since it is disabled
117 dbusObject->available(false);
118}
119
120void DBusSensor::setToErrorState()
121{
122 // Set sensor value to NaN
123 setValueToNaN();
124
125 // Set the sensor to non-functional since it could not be read
126 dbusObject->functional(false);
127}
128
129void DBusSensor::setValue(double value)
130{
131 // Update value on D-Bus if necessary
132 if (shouldUpdateValue(value))
133 {
134 dbusObject->value(value);
135 }
136
137 // Set the sensor to functional since it has a valid value
138 dbusObject->functional(true);
139
140 // Set the sensor to available since it is not disabled
141 dbusObject->available(true);
142}
143
144std::vector<AssocationTuple>
145 DBusSensor::getAssociations(const std::string& deviceInventoryPath,
146 const std::string& chassisInventoryPath)
147{
148 std::vector<AssocationTuple> associations{};
149
150 // Add an association between the sensor and the chassis. This is used by
151 // the Redfish support to find all the sensors in a chassis.
152 associations.emplace_back(
153 std::make_tuple("chassis", "all_sensors", chassisInventoryPath));
154
155 // Add an association between the sensor and the voltage regulator device.
156 // This is used by the Redfish support to find the hardware/inventory item
157 // associated with a sensor.
158 associations.emplace_back(
159 std::make_tuple("inventory", "sensors", deviceInventoryPath));
160
161 return associations;
162}
163
164void DBusSensor::getTypeBasedProperties(std::string& objectPath, Unit& unit,
165 double& minValue, double& maxValue)
166{
167 const char* typeNamespace{""};
168 switch (type)
169 {
170 case SensorType::iout:
171 typeNamespace = currentNamespace;
172 unit = Unit::Amperes;
173 minValue = currentMinValue;
174 maxValue = currentMaxValue;
175 updatePolicy = ValueUpdatePolicy::hysteresis;
176 hysteresis = currentHysteresis;
177 break;
178
179 case SensorType::iout_peak:
180 typeNamespace = currentNamespace;
181 unit = Unit::Amperes;
182 minValue = currentMinValue;
183 maxValue = currentMaxValue;
184 updatePolicy = ValueUpdatePolicy::highest;
185 break;
186
187 case SensorType::iout_valley:
188 typeNamespace = currentNamespace;
189 unit = Unit::Amperes;
190 minValue = currentMinValue;
191 maxValue = currentMaxValue;
192 updatePolicy = ValueUpdatePolicy::lowest;
193 break;
194
195 case SensorType::pout:
196 typeNamespace = powerNamespace;
197 unit = Unit::Watts;
198 minValue = powerMinValue;
199 maxValue = powerMaxValue;
200 updatePolicy = ValueUpdatePolicy::hysteresis;
201 hysteresis = powerHysteresis;
202 break;
203
204 case SensorType::temperature:
205 typeNamespace = temperatureNamespace;
206 unit = Unit::DegreesC;
207 minValue = temperatureMinValue;
208 maxValue = temperatureMaxValue;
209 updatePolicy = ValueUpdatePolicy::hysteresis;
210 hysteresis = temperatureHysteresis;
211 break;
212
213 case SensorType::temperature_peak:
214 typeNamespace = temperatureNamespace;
215 unit = Unit::DegreesC;
216 minValue = temperatureMinValue;
217 maxValue = temperatureMaxValue;
218 updatePolicy = ValueUpdatePolicy::highest;
219 break;
220
221 case SensorType::vout:
222 typeNamespace = voltageNamespace;
223 unit = Unit::Volts;
224 minValue = voltageMinValue;
225 maxValue = voltageMaxValue;
226 updatePolicy = ValueUpdatePolicy::hysteresis;
227 hysteresis = voltageHysteresis;
228 break;
229
230 case SensorType::vout_peak:
231 typeNamespace = voltageNamespace;
232 unit = Unit::Volts;
233 minValue = voltageMinValue;
234 maxValue = voltageMaxValue;
235 updatePolicy = ValueUpdatePolicy::highest;
236 break;
237
238 case SensorType::vout_valley:
239 default:
240 typeNamespace = voltageNamespace;
241 unit = Unit::Volts;
242 minValue = voltageMinValue;
243 maxValue = voltageMaxValue;
244 updatePolicy = ValueUpdatePolicy::lowest;
245 break;
246 }
247
248 // Build object path
249 objectPath = sensorsObjectPath;
250 objectPath += '/';
251 objectPath += typeNamespace;
252 objectPath += '/';
253 objectPath += name;
254}
255
256void DBusSensor::setValueToNaN()
257{
258 // Get current value published on D-Bus
259 double currentValue = dbusObject->value();
260
261 // Check if current value is already NaN. We want to avoid an unnecessary
262 // PropertiesChanged signal. The generated C++ code for the Value interface
263 // does check whether the new value is different from the old one. However,
264 // it uses the equality operator, and NaN always returns false when compared
265 // to another NaN value.
266 if (!std::isnan(currentValue))
267 {
268 // Set value to NaN
269 dbusObject->value(std::numeric_limits<double>::quiet_NaN());
270 }
271}
272
273bool DBusSensor::shouldUpdateValue(double value)
274{
275 // Initially assume we should update the value
276 bool shouldUpdate{true};
277
278 // Get current value published on D-Bus
279 double currentValue = dbusObject->value();
280
281 // Update sensor if the current value is NaN. This indicates it was
282 // disabled or in an error state. Note: you cannot compare a variable to
283 // NaN directly using the equality operator; it will always return false.
284 if (std::isnan(currentValue))
285 {
286 shouldUpdate = true;
287 }
288 else
289 {
290 // Determine whether to update based on policy used by this sensor
291 switch (updatePolicy)
292 {
293 case ValueUpdatePolicy::hysteresis:
294 shouldUpdate = (std::abs(value - currentValue) >= hysteresis);
295 break;
296 case ValueUpdatePolicy::highest:
297 shouldUpdate = (value > currentValue);
298 break;
299 case ValueUpdatePolicy::lowest:
300 shouldUpdate = (value < currentValue);
301 break;
302 }
303 }
304
305 return shouldUpdate;
306}
307
308} // namespace phosphor::power::regulators