1 #include "virtualSensor.hpp" 2 3 #include "config.hpp" 4 5 #include <fmt/format.h> 6 7 #include <phosphor-logging/log.hpp> 8 #include <sdeventplus/event.hpp> 9 10 #include <fstream> 11 #include <iostream> 12 13 static constexpr bool DEBUG = false; 14 static constexpr auto busName = "xyz.openbmc_project.VirtualSensor"; 15 static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/"; 16 17 using namespace phosphor::logging; 18 19 int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*) 20 { 21 if (usrData == nullptr) 22 { 23 throw std::runtime_error("Invalid match"); 24 } 25 26 auto sdbpMsg = sdbusplus::message::message(msg); 27 std::string msgIfce; 28 std::map<std::string, std::variant<int64_t, double, bool>> msgData; 29 30 sdbpMsg.read(msgIfce, msgData); 31 32 if (msgData.find("Value") != msgData.end()) 33 { 34 using namespace phosphor::virtualSensor; 35 VirtualSensor* obj = static_cast<VirtualSensor*>(usrData); 36 // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should 37 // be changed to take the information we got from the signal, to avoid 38 // having to do numerous dbus queries. 39 obj->updateVirtualSensor(); 40 } 41 return 0; 42 } 43 44 namespace phosphor 45 { 46 namespace virtualSensor 47 { 48 49 void printParams(const VirtualSensor::ParamMap& paramMap) 50 { 51 for (const auto& p : paramMap) 52 { 53 const auto& p1 = p.first; 54 const auto& p2 = p.second; 55 auto val = p2->getParamValue(); 56 std::cout << p1 << " = " << val << "\n"; 57 } 58 } 59 60 double SensorParam::getParamValue() 61 { 62 switch (paramType) 63 { 64 case constParam: 65 return value; 66 break; 67 case dbusParam: 68 return dbusSensor->getSensorValue(); 69 break; 70 default: 71 throw std::invalid_argument("param type not supported"); 72 } 73 } 74 75 void VirtualSensor::initVirtualSensor(const Json& sensorConfig, 76 const std::string& objPath) 77 { 78 79 static const Json empty{}; 80 81 /* Get threshold values if defined in config */ 82 auto threshold = sensorConfig.value("Threshold", empty); 83 if (!threshold.empty()) 84 { 85 // Only create the threshold interfaces if 86 // at least one of their values is present. 87 88 if (threshold.contains("CriticalHigh") || 89 threshold.contains("CriticalLow")) 90 { 91 criticalIface = std::make_unique<Threshold<CriticalObject>>( 92 bus, objPath.c_str()); 93 94 criticalIface->criticalHigh(threshold.value( 95 "CriticalHigh", std::numeric_limits<double>::quiet_NaN())); 96 criticalIface->criticalLow(threshold.value( 97 "CriticalLow", std::numeric_limits<double>::quiet_NaN())); 98 } 99 100 if (threshold.contains("WarningHigh") || 101 threshold.contains("WarningLow")) 102 { 103 warningIface = std::make_unique<Threshold<WarningObject>>( 104 bus, objPath.c_str()); 105 106 warningIface->warningHigh(threshold.value( 107 "WarningHigh", std::numeric_limits<double>::quiet_NaN())); 108 warningIface->warningLow(threshold.value( 109 "WarningLow", std::numeric_limits<double>::quiet_NaN())); 110 } 111 112 if (threshold.contains("HardShutdownHigh") || 113 threshold.contains("HardShutdownLow")) 114 { 115 hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>( 116 bus, objPath.c_str()); 117 118 hardShutdownIface->hardShutdownHigh(threshold.value( 119 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 120 hardShutdownIface->hardShutdownLow(threshold.value( 121 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN())); 122 } 123 124 if (threshold.contains("SoftShutdownHigh") || 125 threshold.contains("SoftShutdownLow")) 126 { 127 softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>( 128 bus, objPath.c_str()); 129 130 softShutdownIface->softShutdownHigh(threshold.value( 131 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 132 softShutdownIface->softShutdownLow(threshold.value( 133 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN())); 134 } 135 136 if (threshold.contains("PerformanceLossHigh") || 137 threshold.contains("PerformanceLossLow")) 138 { 139 perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>( 140 bus, objPath.c_str()); 141 142 perfLossIface->performanceLossHigh( 143 threshold.value("PerformanceLossHigh", 144 std::numeric_limits<double>::quiet_NaN())); 145 perfLossIface->performanceLossLow( 146 threshold.value("PerformanceLossLow", 147 std::numeric_limits<double>::quiet_NaN())); 148 } 149 } 150 151 /* Get expression string */ 152 exprStr = sensorConfig.value("Expression", ""); 153 154 /* Get all the parameter listed in configuration */ 155 auto params = sensorConfig.value("Params", empty); 156 157 /* Check for constant parameter */ 158 const auto& consParams = params.value("ConstParam", empty); 159 if (!consParams.empty()) 160 { 161 for (auto& j : consParams) 162 { 163 if (j.find("ParamName") != j.end()) 164 { 165 auto paramPtr = std::make_unique<SensorParam>(j["Value"]); 166 std::string name = j["ParamName"]; 167 symbols.create_variable(name); 168 paramMap.emplace(std::move(name), std::move(paramPtr)); 169 } 170 else 171 { 172 /* Invalid configuration */ 173 throw std::invalid_argument( 174 "ParamName not found in configuration"); 175 } 176 } 177 } 178 179 /* Check for dbus parameter */ 180 auto dbusParams = params.value("DbusParam", empty); 181 if (!dbusParams.empty()) 182 { 183 for (auto& j : dbusParams) 184 { 185 /* Get parameter dbus sensor descriptor */ 186 auto desc = j.value("Desc", empty); 187 if ((!desc.empty()) && (j.find("ParamName") != j.end())) 188 { 189 std::string sensorType = desc.value("SensorType", ""); 190 std::string name = desc.value("Name", ""); 191 192 if (!sensorType.empty() && !name.empty()) 193 { 194 std::string objPath(sensorDbusPath); 195 objPath += sensorType + "/" + name; 196 197 auto paramPtr = 198 std::make_unique<SensorParam>(bus, objPath, this); 199 std::string name = j["ParamName"]; 200 symbols.create_variable(name); 201 paramMap.emplace(std::move(name), std::move(paramPtr)); 202 } 203 } 204 } 205 } 206 207 symbols.add_constants(); 208 symbols.add_package(vecopsPackage); 209 expression.register_symbol_table(symbols); 210 211 /* parser from exprtk */ 212 exprtk::parser<double> parser{}; 213 if (!parser.compile(exprStr, expression)) 214 { 215 log<level::ERR>("Expression compilation failed"); 216 217 for (std::size_t i = 0; i < parser.error_count(); ++i) 218 { 219 auto error = parser.get_error(i); 220 221 log<level::ERR>( 222 fmt::format( 223 "Position: {} Type: {} Message: {}", error.token.position, 224 exprtk::parser_error::to_str(error.mode), error.diagnostic) 225 .c_str()); 226 } 227 throw std::runtime_error("Expression compilation failed"); 228 } 229 230 /* Print all parameters for debug purpose only */ 231 if (DEBUG) 232 printParams(paramMap); 233 } 234 235 void VirtualSensor::setSensorValue(double value) 236 { 237 ValueIface::value(value); 238 } 239 240 void VirtualSensor::updateVirtualSensor() 241 { 242 for (auto& param : paramMap) 243 { 244 auto& name = param.first; 245 auto& data = param.second; 246 if (auto var = symbols.get_variable(name)) 247 { 248 var->ref() = data->getParamValue(); 249 } 250 else 251 { 252 /* Invalid parameter */ 253 throw std::invalid_argument("ParamName not found in symbols"); 254 } 255 } 256 double val = expression.value(); 257 258 /* Set sensor value to dbus interface */ 259 setSensorValue(val); 260 261 if (DEBUG) 262 std::cout << "Sensor value is " << val << "\n"; 263 264 /* Check sensor thresholds and log required message */ 265 checkThresholds(val, perfLossIface); 266 checkThresholds(val, warningIface); 267 checkThresholds(val, criticalIface); 268 checkThresholds(val, softShutdownIface); 269 checkThresholds(val, hardShutdownIface); 270 } 271 272 /** @brief Parsing Virtual Sensor config JSON file */ 273 Json VirtualSensors::parseConfigFile(const std::string configFile) 274 { 275 std::ifstream jsonFile(configFile); 276 if (!jsonFile.is_open()) 277 { 278 log<level::ERR>("config JSON file not found", 279 entry("FILENAME = %s", configFile.c_str())); 280 throw std::exception{}; 281 } 282 283 auto data = Json::parse(jsonFile, nullptr, false); 284 if (data.is_discarded()) 285 { 286 log<level::ERR>("config readings JSON parser failure", 287 entry("FILENAME = %s", configFile.c_str())); 288 throw std::exception{}; 289 } 290 291 return data; 292 } 293 294 std::map<std::string, ValueIface::Unit> unitMap = { 295 {"temperature", ValueIface::Unit::DegreesC}, 296 {"fan_tach", ValueIface::Unit::RPMS}, 297 {"voltage", ValueIface::Unit::Volts}, 298 {"altitude", ValueIface::Unit::Meters}, 299 {"current", ValueIface::Unit::Amperes}, 300 {"power", ValueIface::Unit::Watts}, 301 {"energy", ValueIface::Unit::Joules}, 302 {"utilization", ValueIface::Unit::Percent}, 303 {"airflow", ValueIface::Unit::CFM}}; 304 305 void VirtualSensors::createVirtualSensors() 306 { 307 static const Json empty{}; 308 309 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE); 310 // print values 311 if (DEBUG) 312 std::cout << "Config json data:\n" << data << "\n\n"; 313 314 /* Get virtual sensors config data */ 315 for (const auto& j : data) 316 { 317 auto desc = j.value("Desc", empty); 318 if (!desc.empty()) 319 { 320 std::string sensorType = desc.value("SensorType", ""); 321 std::string name = desc.value("Name", ""); 322 323 if (!name.empty() && !sensorType.empty()) 324 { 325 if (unitMap.find(sensorType) == unitMap.end()) 326 { 327 log<level::ERR>("Sensor type is not supported", 328 entry("TYPE = %s", sensorType.c_str())); 329 } 330 else 331 { 332 std::string objPath(sensorDbusPath); 333 objPath += sensorType + "/" + name; 334 335 auto virtualSensorPtr = std::make_unique<VirtualSensor>( 336 bus, objPath.c_str(), j, name); 337 338 log<level::INFO>("Added a new virtual sensor", 339 entry("NAME = %s", name.c_str())); 340 virtualSensorPtr->updateVirtualSensor(); 341 342 /* Initialize unit value for virtual sensor */ 343 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 344 345 virtualSensorsMap.emplace(std::move(name), 346 std::move(virtualSensorPtr)); 347 } 348 } 349 else 350 { 351 log<level::ERR>("Sensor type or name not found in config file"); 352 } 353 } 354 else 355 { 356 log<level::ERR>( 357 "Descriptor for new virtual sensor not found in config file"); 358 } 359 } 360 } 361 362 } // namespace virtualSensor 363 } // namespace phosphor 364 365 /** 366 * @brief Main 367 */ 368 int main() 369 { 370 371 // Get a default event loop 372 auto event = sdeventplus::Event::get_default(); 373 374 // Get a handle to system dbus 375 auto bus = sdbusplus::bus::new_default(); 376 377 // Add the ObjectManager interface 378 sdbusplus::server::manager::manager objManager(bus, "/"); 379 380 // Create an virtual sensors object 381 phosphor::virtualSensor::VirtualSensors virtualSensors(bus); 382 383 // Request service bus name 384 bus.request_name(busName); 385 386 // Attach the bus to sd_event to service user requests 387 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 388 event.loop(); 389 390 return 0; 391 } 392