1 #include "numeric_sensor.hpp" 2 3 #include "libpldm/platform.h" 4 5 #include "common/utils.hpp" 6 #include "requester/handler.hpp" 7 8 #include <limits> 9 #include <regex> 10 11 PHOSPHOR_LOG2_USING; 12 13 namespace pldm 14 { 15 namespace platform_mc 16 { 17 18 NumericSensor::NumericSensor(const pldm_tid_t tid, const bool sensorDisabled, 19 std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr, 20 std::string& sensorName, 21 std::string& associationPath) : 22 tid(tid), 23 sensorName(sensorName), isPriority(false) 24 { 25 if (!pdr) 26 { 27 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 28 } 29 30 sensorId = pdr->sensor_id; 31 std::string path; 32 SensorUnit sensorUnit = SensorUnit::DegreesC; 33 34 switch (pdr->base_unit) 35 { 36 case PLDM_SENSOR_UNIT_DEGRESS_C: 37 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/"; 38 sensorUnit = SensorUnit::DegreesC; 39 break; 40 case PLDM_SENSOR_UNIT_VOLTS: 41 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/"; 42 sensorUnit = SensorUnit::Volts; 43 break; 44 case PLDM_SENSOR_UNIT_AMPS: 45 sensorNameSpace = "/xyz/openbmc_project/sensors/current/"; 46 sensorUnit = SensorUnit::Amperes; 47 break; 48 case PLDM_SENSOR_UNIT_RPM: 49 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/"; 50 sensorUnit = SensorUnit::RPMS; 51 break; 52 case PLDM_SENSOR_UNIT_WATTS: 53 sensorNameSpace = "/xyz/openbmc_project/sensors/power/"; 54 sensorUnit = SensorUnit::Watts; 55 break; 56 case PLDM_SENSOR_UNIT_JOULES: 57 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/"; 58 sensorUnit = SensorUnit::Joules; 59 break; 60 case PLDM_SENSOR_UNIT_PERCENTAGE: 61 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/"; 62 sensorUnit = SensorUnit::Percent; 63 break; 64 default: 65 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME", 66 sensorName, "UNIT", pdr->base_unit); 67 throw sdbusplus::xyz::openbmc_project::Common::Error:: 68 InvalidArgument(); 69 break; 70 } 71 72 path = sensorNameSpace + sensorName; 73 try 74 { 75 auto service = pldm::utils::DBusHandler().getService( 76 path.c_str(), "xyz.openbmc_project.Sensor.Value"); 77 if (!service.empty()) 78 { 79 throw sdbusplus::xyz::openbmc_project::Common::Error:: 80 TooManyResources(); 81 } 82 } 83 catch (const std::exception&) 84 { 85 /* The sensor object path is not created */ 86 } 87 88 auto& bus = pldm::utils::DBusHandler::getBus(); 89 try 90 { 91 associationDefinitionsIntf = 92 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str()); 93 } 94 catch (const sdbusplus::exception_t& e) 95 { 96 lg2::error( 97 "Failed to create association interface for numeric sensor {PATH} error - {ERROR}", 98 "PATH", path, "ERROR", e); 99 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 100 } 101 102 associationDefinitionsIntf->associations( 103 {{"chassis", "all_sensors", associationPath}}); 104 105 double maxValue = std::numeric_limits<double>::quiet_NaN(); 106 double minValue = std::numeric_limits<double>::quiet_NaN(); 107 108 switch (pdr->sensor_data_size) 109 { 110 case PLDM_SENSOR_DATA_SIZE_UINT8: 111 maxValue = pdr->max_readable.value_u8; 112 minValue = pdr->min_readable.value_u8; 113 hysteresis = pdr->hysteresis.value_u8; 114 break; 115 case PLDM_SENSOR_DATA_SIZE_SINT8: 116 maxValue = pdr->max_readable.value_s8; 117 minValue = pdr->min_readable.value_s8; 118 hysteresis = pdr->hysteresis.value_s8; 119 break; 120 case PLDM_SENSOR_DATA_SIZE_UINT16: 121 maxValue = pdr->max_readable.value_u16; 122 minValue = pdr->min_readable.value_u16; 123 hysteresis = pdr->hysteresis.value_u16; 124 break; 125 case PLDM_SENSOR_DATA_SIZE_SINT16: 126 maxValue = pdr->max_readable.value_s16; 127 minValue = pdr->min_readable.value_s16; 128 hysteresis = pdr->hysteresis.value_s16; 129 break; 130 case PLDM_SENSOR_DATA_SIZE_UINT32: 131 maxValue = pdr->max_readable.value_u32; 132 minValue = pdr->min_readable.value_u32; 133 hysteresis = pdr->hysteresis.value_u32; 134 break; 135 case PLDM_SENSOR_DATA_SIZE_SINT32: 136 maxValue = pdr->max_readable.value_s32; 137 minValue = pdr->min_readable.value_s32; 138 hysteresis = pdr->hysteresis.value_s32; 139 break; 140 } 141 142 bool hasCriticalThresholds = false; 143 bool hasWarningThresholds = false; 144 double criticalHigh = std::numeric_limits<double>::quiet_NaN(); 145 double criticalLow = std::numeric_limits<double>::quiet_NaN(); 146 double warningHigh = std::numeric_limits<double>::quiet_NaN(); 147 double warningLow = std::numeric_limits<double>::quiet_NaN(); 148 149 if (pdr->supported_thresholds.bits.bit0) 150 { 151 hasWarningThresholds = true; 152 switch (pdr->range_field_format) 153 { 154 case PLDM_RANGE_FIELD_FORMAT_UINT8: 155 warningHigh = pdr->warning_high.value_u8; 156 break; 157 case PLDM_RANGE_FIELD_FORMAT_SINT8: 158 warningHigh = pdr->warning_high.value_s8; 159 break; 160 case PLDM_RANGE_FIELD_FORMAT_UINT16: 161 warningHigh = pdr->warning_high.value_u16; 162 break; 163 case PLDM_RANGE_FIELD_FORMAT_SINT16: 164 warningHigh = pdr->warning_high.value_s16; 165 break; 166 case PLDM_RANGE_FIELD_FORMAT_UINT32: 167 warningHigh = pdr->warning_high.value_u32; 168 break; 169 case PLDM_RANGE_FIELD_FORMAT_SINT32: 170 warningHigh = pdr->warning_high.value_s32; 171 break; 172 case PLDM_RANGE_FIELD_FORMAT_REAL32: 173 warningHigh = pdr->warning_high.value_f32; 174 break; 175 } 176 } 177 178 if (pdr->supported_thresholds.bits.bit3) 179 { 180 hasWarningThresholds = true; 181 switch (pdr->range_field_format) 182 { 183 case PLDM_RANGE_FIELD_FORMAT_UINT8: 184 warningLow = pdr->warning_low.value_u8; 185 break; 186 case PLDM_RANGE_FIELD_FORMAT_SINT8: 187 warningLow = pdr->warning_low.value_s8; 188 break; 189 case PLDM_RANGE_FIELD_FORMAT_UINT16: 190 warningLow = pdr->warning_low.value_u16; 191 break; 192 case PLDM_RANGE_FIELD_FORMAT_SINT16: 193 warningLow = pdr->warning_low.value_s16; 194 break; 195 case PLDM_RANGE_FIELD_FORMAT_UINT32: 196 warningLow = pdr->warning_low.value_u32; 197 break; 198 case PLDM_RANGE_FIELD_FORMAT_SINT32: 199 warningLow = pdr->warning_low.value_s32; 200 break; 201 case PLDM_RANGE_FIELD_FORMAT_REAL32: 202 warningLow = pdr->warning_low.value_f32; 203 break; 204 } 205 } 206 207 if (pdr->supported_thresholds.bits.bit1) 208 { 209 hasCriticalThresholds = true; 210 switch (pdr->range_field_format) 211 { 212 case PLDM_RANGE_FIELD_FORMAT_UINT8: 213 criticalHigh = pdr->critical_high.value_u8; 214 break; 215 case PLDM_RANGE_FIELD_FORMAT_SINT8: 216 criticalHigh = pdr->critical_high.value_s8; 217 break; 218 case PLDM_RANGE_FIELD_FORMAT_UINT16: 219 criticalHigh = pdr->critical_high.value_u16; 220 break; 221 case PLDM_RANGE_FIELD_FORMAT_SINT16: 222 criticalHigh = pdr->critical_high.value_s16; 223 break; 224 case PLDM_RANGE_FIELD_FORMAT_UINT32: 225 criticalHigh = pdr->critical_high.value_u32; 226 break; 227 case PLDM_RANGE_FIELD_FORMAT_SINT32: 228 criticalHigh = pdr->critical_high.value_s32; 229 break; 230 case PLDM_RANGE_FIELD_FORMAT_REAL32: 231 criticalHigh = pdr->critical_high.value_f32; 232 break; 233 } 234 } 235 236 if (pdr->supported_thresholds.bits.bit4) 237 { 238 hasCriticalThresholds = true; 239 switch (pdr->range_field_format) 240 { 241 case PLDM_RANGE_FIELD_FORMAT_UINT8: 242 criticalLow = pdr->critical_low.value_u8; 243 break; 244 case PLDM_RANGE_FIELD_FORMAT_SINT8: 245 criticalLow = pdr->critical_low.value_s8; 246 break; 247 case PLDM_RANGE_FIELD_FORMAT_UINT16: 248 criticalLow = pdr->critical_low.value_u16; 249 break; 250 case PLDM_RANGE_FIELD_FORMAT_SINT16: 251 criticalLow = pdr->critical_low.value_s16; 252 break; 253 case PLDM_RANGE_FIELD_FORMAT_UINT32: 254 criticalLow = pdr->critical_low.value_u32; 255 break; 256 case PLDM_RANGE_FIELD_FORMAT_SINT32: 257 criticalLow = pdr->critical_low.value_s32; 258 break; 259 case PLDM_RANGE_FIELD_FORMAT_REAL32: 260 criticalLow = pdr->critical_low.value_f32; 261 break; 262 } 263 } 264 265 resolution = pdr->resolution; 266 offset = pdr->offset; 267 baseUnitModifier = pdr->unit_modifier; 268 269 /** 270 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds 271 * updateTime is in microseconds 272 */ 273 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000); 274 if (!std::isnan(pdr->update_interval)) 275 { 276 updateTime = pdr->update_interval * 1000000; 277 } 278 279 try 280 { 281 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str()); 282 } 283 catch (const sdbusplus::exception_t& e) 284 { 285 lg2::error( 286 "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}", 287 "PATH", path, "ERROR", e); 288 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 289 } 290 valueIntf->maxValue(unitModifier(conversionFormula(maxValue))); 291 valueIntf->minValue(unitModifier(conversionFormula(minValue))); 292 hysteresis = unitModifier(conversionFormula(hysteresis)); 293 valueIntf->unit(sensorUnit); 294 295 try 296 { 297 availabilityIntf = std::make_unique<AvailabilityIntf>(bus, 298 path.c_str()); 299 } 300 catch (const sdbusplus::exception_t& e) 301 { 302 lg2::error( 303 "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}", 304 "PATH", path, "ERROR", e); 305 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 306 } 307 availabilityIntf->available(true); 308 309 try 310 { 311 operationalStatusIntf = 312 std::make_unique<OperationalStatusIntf>(bus, path.c_str()); 313 } 314 catch (const sdbusplus::exception_t& e) 315 { 316 lg2::error( 317 "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}", 318 "PATH", path, "ERROR", e); 319 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 320 } 321 operationalStatusIntf->functional(!sensorDisabled); 322 323 if (hasWarningThresholds) 324 { 325 try 326 { 327 thresholdWarningIntf = 328 std::make_unique<ThresholdWarningIntf>(bus, path.c_str()); 329 } 330 catch (const sdbusplus::exception_t& e) 331 { 332 lg2::error( 333 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}", 334 "PATH", path, "ERROR", e); 335 throw sdbusplus::xyz::openbmc_project::Common::Error:: 336 InvalidArgument(); 337 } 338 thresholdWarningIntf->warningHigh(unitModifier(warningHigh)); 339 thresholdWarningIntf->warningLow(unitModifier(warningLow)); 340 } 341 342 if (hasCriticalThresholds) 343 { 344 try 345 { 346 thresholdCriticalIntf = 347 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str()); 348 } 349 catch (const sdbusplus::exception_t& e) 350 { 351 lg2::error( 352 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}", 353 "PATH", path, "ERROR", e); 354 throw sdbusplus::xyz::openbmc_project::Common::Error:: 355 InvalidArgument(); 356 } 357 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh)); 358 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow)); 359 } 360 } 361 362 NumericSensor::NumericSensor( 363 const pldm_tid_t tid, const bool sensorDisabled, 364 std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr, 365 std::string& sensorName, std::string& associationPath) : 366 tid(tid), 367 sensorName(sensorName), isPriority(false) 368 { 369 if (!pdr) 370 { 371 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 372 } 373 374 sensorId = pdr->sensor_id; 375 std::string path; 376 SensorUnit sensorUnit = SensorUnit::DegreesC; 377 378 switch (pdr->base_unit) 379 { 380 case PLDM_SENSOR_UNIT_DEGRESS_C: 381 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/"; 382 sensorUnit = SensorUnit::DegreesC; 383 break; 384 case PLDM_SENSOR_UNIT_VOLTS: 385 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/"; 386 sensorUnit = SensorUnit::Volts; 387 break; 388 case PLDM_SENSOR_UNIT_AMPS: 389 sensorNameSpace = "/xyz/openbmc_project/sensors/current/"; 390 sensorUnit = SensorUnit::Amperes; 391 break; 392 case PLDM_SENSOR_UNIT_RPM: 393 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/"; 394 sensorUnit = SensorUnit::RPMS; 395 break; 396 case PLDM_SENSOR_UNIT_WATTS: 397 sensorNameSpace = "/xyz/openbmc_project/sensors/power/"; 398 sensorUnit = SensorUnit::Watts; 399 break; 400 case PLDM_SENSOR_UNIT_JOULES: 401 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/"; 402 sensorUnit = SensorUnit::Joules; 403 break; 404 case PLDM_SENSOR_UNIT_PERCENTAGE: 405 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/"; 406 sensorUnit = SensorUnit::Percent; 407 break; 408 default: 409 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME", 410 sensorName, "UNIT", pdr->base_unit); 411 throw sdbusplus::xyz::openbmc_project::Common::Error:: 412 InvalidArgument(); 413 break; 414 } 415 416 path = sensorNameSpace + sensorName; 417 try 418 { 419 auto service = pldm::utils::DBusHandler().getService( 420 path.c_str(), "xyz.openbmc_project.Sensor.Value"); 421 if (!service.empty()) 422 { 423 throw sdbusplus::xyz::openbmc_project::Common::Error:: 424 TooManyResources(); 425 } 426 } 427 catch (const std::exception&) 428 { 429 /* The sensor object path is not created */ 430 } 431 432 auto& bus = pldm::utils::DBusHandler::getBus(); 433 try 434 { 435 associationDefinitionsIntf = 436 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str()); 437 } 438 catch (const sdbusplus::exception_t& e) 439 { 440 lg2::error( 441 "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}", 442 "PATH", path, "ERROR", e); 443 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 444 } 445 associationDefinitionsIntf->associations( 446 {{"chassis", "all_sensors", associationPath.c_str()}}); 447 448 double maxValue = std::numeric_limits<double>::quiet_NaN(); 449 double minValue = std::numeric_limits<double>::quiet_NaN(); 450 bool hasWarningThresholds = false; 451 bool hasCriticalThresholds = false; 452 double criticalHigh = std::numeric_limits<double>::quiet_NaN(); 453 double criticalLow = std::numeric_limits<double>::quiet_NaN(); 454 double warningHigh = std::numeric_limits<double>::quiet_NaN(); 455 double warningLow = std::numeric_limits<double>::quiet_NaN(); 456 457 if (pdr->range_field_support.bits.bit0) 458 { 459 hasWarningThresholds = true; 460 warningHigh = pdr->warning_high; 461 } 462 if (pdr->range_field_support.bits.bit1) 463 { 464 hasWarningThresholds = true; 465 warningLow = pdr->warning_low; 466 } 467 468 if (pdr->range_field_support.bits.bit2) 469 { 470 hasCriticalThresholds = true; 471 criticalHigh = pdr->critical_high; 472 } 473 474 if (pdr->range_field_support.bits.bit3) 475 { 476 hasCriticalThresholds = true; 477 criticalLow = pdr->critical_low; 478 } 479 480 resolution = std::numeric_limits<double>::quiet_NaN(); 481 offset = std::numeric_limits<double>::quiet_NaN(); 482 baseUnitModifier = pdr->unit_modifier; 483 484 /** 485 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds 486 * updateTime is in microseconds 487 */ 488 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000); 489 try 490 { 491 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str()); 492 } 493 catch (const sdbusplus::exception_t& e) 494 { 495 lg2::error( 496 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}", 497 "PATH", path, "ERROR", e); 498 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 499 } 500 valueIntf->maxValue(unitModifier(conversionFormula(maxValue))); 501 valueIntf->minValue(unitModifier(conversionFormula(minValue))); 502 hysteresis = unitModifier(conversionFormula(hysteresis)); 503 valueIntf->unit(sensorUnit); 504 505 try 506 { 507 availabilityIntf = std::make_unique<AvailabilityIntf>(bus, 508 path.c_str()); 509 } 510 catch (const sdbusplus::exception_t& e) 511 { 512 lg2::error( 513 "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}", 514 "PATH", path, "ERROR", e); 515 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 516 } 517 availabilityIntf->available(true); 518 519 try 520 { 521 operationalStatusIntf = 522 std::make_unique<OperationalStatusIntf>(bus, path.c_str()); 523 } 524 catch (const sdbusplus::exception_t& e) 525 { 526 lg2::error( 527 "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}", 528 "PATH", path, "ERROR", e); 529 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); 530 } 531 operationalStatusIntf->functional(!sensorDisabled); 532 533 if (hasWarningThresholds) 534 { 535 try 536 { 537 thresholdWarningIntf = 538 std::make_unique<ThresholdWarningIntf>(bus, path.c_str()); 539 } 540 catch (const sdbusplus::exception_t& e) 541 { 542 lg2::error( 543 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}", 544 "PATH", path, "ERROR", e); 545 throw sdbusplus::xyz::openbmc_project::Common::Error:: 546 InvalidArgument(); 547 } 548 thresholdWarningIntf->warningHigh(unitModifier(warningHigh)); 549 thresholdWarningIntf->warningLow(unitModifier(warningLow)); 550 } 551 552 if (hasCriticalThresholds) 553 { 554 try 555 { 556 thresholdCriticalIntf = 557 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str()); 558 } 559 catch (const sdbusplus::exception_t& e) 560 { 561 lg2::error( 562 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}", 563 "PATH", path, "ERROR", e); 564 throw sdbusplus::xyz::openbmc_project::Common::Error:: 565 InvalidArgument(); 566 } 567 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh)); 568 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow)); 569 } 570 } 571 572 double NumericSensor::conversionFormula(double value) 573 { 574 double convertedValue = value; 575 convertedValue *= std::isnan(resolution) ? 1 : resolution; 576 convertedValue += std::isnan(offset) ? 0 : offset; 577 return convertedValue; 578 } 579 580 double NumericSensor::unitModifier(double value) 581 { 582 return std::isnan(value) ? value : value * std::pow(10, baseUnitModifier); 583 } 584 } // namespace platform_mc 585 } // namespace pldm 586