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 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 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 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 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> 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 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 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 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