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