#include "numeric_sensor.hpp" #include "libpldm/platform.h" #include "common/utils.hpp" #include "requester/handler.hpp" #include #include PHOSPHOR_LOG2_USING; namespace pldm { namespace platform_mc { NumericSensor::NumericSensor(const pldm_tid_t tid, const bool sensorDisabled, std::shared_ptr pdr, std::string& sensorName, std::string& associationPath) : tid(tid), sensorName(sensorName), isPriority(false) { if (!pdr) { throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } sensorId = pdr->sensor_id; std::string path; SensorUnit sensorUnit = SensorUnit::DegreesC; switch (pdr->base_unit) { case PLDM_SENSOR_UNIT_DEGRESS_C: sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/"; sensorUnit = SensorUnit::DegreesC; break; case PLDM_SENSOR_UNIT_VOLTS: sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/"; sensorUnit = SensorUnit::Volts; break; case PLDM_SENSOR_UNIT_AMPS: sensorNameSpace = "/xyz/openbmc_project/sensors/current/"; sensorUnit = SensorUnit::Amperes; break; case PLDM_SENSOR_UNIT_RPM: sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/"; sensorUnit = SensorUnit::RPMS; break; case PLDM_SENSOR_UNIT_WATTS: sensorNameSpace = "/xyz/openbmc_project/sensors/power/"; sensorUnit = SensorUnit::Watts; break; case PLDM_SENSOR_UNIT_JOULES: sensorNameSpace = "/xyz/openbmc_project/sensors/energy/"; sensorUnit = SensorUnit::Joules; break; case PLDM_SENSOR_UNIT_PERCENTAGE: sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/"; sensorUnit = SensorUnit::Percent; break; default: lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME", sensorName, "UNIT", pdr->base_unit); throw sdbusplus::xyz::openbmc_project::Common::Error:: InvalidArgument(); break; } path = sensorNameSpace + sensorName; try { auto service = pldm::utils::DBusHandler().getService( path.c_str(), "xyz.openbmc_project.Sensor.Value"); if (!service.empty()) { throw sdbusplus::xyz::openbmc_project::Common::Error:: TooManyResources(); } } catch (const std::exception&) { /* The sensor object path is not created */ } auto& bus = pldm::utils::DBusHandler::getBus(); try { associationDefinitionsIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create association interface for numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } associationDefinitionsIntf->associations( {{"chassis", "all_sensors", associationPath}}); double maxValue = std::numeric_limits::quiet_NaN(); double minValue = std::numeric_limits::quiet_NaN(); switch (pdr->sensor_data_size) { case PLDM_SENSOR_DATA_SIZE_UINT8: maxValue = pdr->max_readable.value_u8; minValue = pdr->min_readable.value_u8; hysteresis = pdr->hysteresis.value_u8; break; case PLDM_SENSOR_DATA_SIZE_SINT8: maxValue = pdr->max_readable.value_s8; minValue = pdr->min_readable.value_s8; hysteresis = pdr->hysteresis.value_s8; break; case PLDM_SENSOR_DATA_SIZE_UINT16: maxValue = pdr->max_readable.value_u16; minValue = pdr->min_readable.value_u16; hysteresis = pdr->hysteresis.value_u16; break; case PLDM_SENSOR_DATA_SIZE_SINT16: maxValue = pdr->max_readable.value_s16; minValue = pdr->min_readable.value_s16; hysteresis = pdr->hysteresis.value_s16; break; case PLDM_SENSOR_DATA_SIZE_UINT32: maxValue = pdr->max_readable.value_u32; minValue = pdr->min_readable.value_u32; hysteresis = pdr->hysteresis.value_u32; break; case PLDM_SENSOR_DATA_SIZE_SINT32: maxValue = pdr->max_readable.value_s32; minValue = pdr->min_readable.value_s32; hysteresis = pdr->hysteresis.value_s32; break; } bool hasCriticalThresholds = false; bool hasWarningThresholds = false; double criticalHigh = std::numeric_limits::quiet_NaN(); double criticalLow = std::numeric_limits::quiet_NaN(); double warningHigh = std::numeric_limits::quiet_NaN(); double warningLow = std::numeric_limits::quiet_NaN(); if (pdr->supported_thresholds.bits.bit0) { hasWarningThresholds = true; switch (pdr->range_field_format) { case PLDM_RANGE_FIELD_FORMAT_UINT8: warningHigh = pdr->warning_high.value_u8; break; case PLDM_RANGE_FIELD_FORMAT_SINT8: warningHigh = pdr->warning_high.value_s8; break; case PLDM_RANGE_FIELD_FORMAT_UINT16: warningHigh = pdr->warning_high.value_u16; break; case PLDM_RANGE_FIELD_FORMAT_SINT16: warningHigh = pdr->warning_high.value_s16; break; case PLDM_RANGE_FIELD_FORMAT_UINT32: warningHigh = pdr->warning_high.value_u32; break; case PLDM_RANGE_FIELD_FORMAT_SINT32: warningHigh = pdr->warning_high.value_s32; break; case PLDM_RANGE_FIELD_FORMAT_REAL32: warningHigh = pdr->warning_high.value_f32; break; } } if (pdr->supported_thresholds.bits.bit3) { hasWarningThresholds = true; switch (pdr->range_field_format) { case PLDM_RANGE_FIELD_FORMAT_UINT8: warningLow = pdr->warning_low.value_u8; break; case PLDM_RANGE_FIELD_FORMAT_SINT8: warningLow = pdr->warning_low.value_s8; break; case PLDM_RANGE_FIELD_FORMAT_UINT16: warningLow = pdr->warning_low.value_u16; break; case PLDM_RANGE_FIELD_FORMAT_SINT16: warningLow = pdr->warning_low.value_s16; break; case PLDM_RANGE_FIELD_FORMAT_UINT32: warningLow = pdr->warning_low.value_u32; break; case PLDM_RANGE_FIELD_FORMAT_SINT32: warningLow = pdr->warning_low.value_s32; break; case PLDM_RANGE_FIELD_FORMAT_REAL32: warningLow = pdr->warning_low.value_f32; break; } } if (pdr->supported_thresholds.bits.bit1) { hasCriticalThresholds = true; switch (pdr->range_field_format) { case PLDM_RANGE_FIELD_FORMAT_UINT8: criticalHigh = pdr->critical_high.value_u8; break; case PLDM_RANGE_FIELD_FORMAT_SINT8: criticalHigh = pdr->critical_high.value_s8; break; case PLDM_RANGE_FIELD_FORMAT_UINT16: criticalHigh = pdr->critical_high.value_u16; break; case PLDM_RANGE_FIELD_FORMAT_SINT16: criticalHigh = pdr->critical_high.value_s16; break; case PLDM_RANGE_FIELD_FORMAT_UINT32: criticalHigh = pdr->critical_high.value_u32; break; case PLDM_RANGE_FIELD_FORMAT_SINT32: criticalHigh = pdr->critical_high.value_s32; break; case PLDM_RANGE_FIELD_FORMAT_REAL32: criticalHigh = pdr->critical_high.value_f32; break; } } if (pdr->supported_thresholds.bits.bit4) { hasCriticalThresholds = true; switch (pdr->range_field_format) { case PLDM_RANGE_FIELD_FORMAT_UINT8: criticalLow = pdr->critical_low.value_u8; break; case PLDM_RANGE_FIELD_FORMAT_SINT8: criticalLow = pdr->critical_low.value_s8; break; case PLDM_RANGE_FIELD_FORMAT_UINT16: criticalLow = pdr->critical_low.value_u16; break; case PLDM_RANGE_FIELD_FORMAT_SINT16: criticalLow = pdr->critical_low.value_s16; break; case PLDM_RANGE_FIELD_FORMAT_UINT32: criticalLow = pdr->critical_low.value_u32; break; case PLDM_RANGE_FIELD_FORMAT_SINT32: criticalLow = pdr->critical_low.value_s32; break; case PLDM_RANGE_FIELD_FORMAT_REAL32: criticalLow = pdr->critical_low.value_f32; break; } } resolution = pdr->resolution; offset = pdr->offset; baseUnitModifier = pdr->unit_modifier; /** * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds * updateTime is in microseconds */ updateTime = static_cast(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000); if (!std::isnan(pdr->update_interval)) { updateTime = pdr->update_interval * 1000000; } try { valueIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } valueIntf->maxValue(unitModifier(conversionFormula(maxValue))); valueIntf->minValue(unitModifier(conversionFormula(minValue))); hysteresis = unitModifier(conversionFormula(hysteresis)); valueIntf->unit(sensorUnit); try { availabilityIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } availabilityIntf->available(true); try { operationalStatusIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } operationalStatusIntf->functional(!sensorDisabled); if (hasWarningThresholds) { try { thresholdWarningIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error:: InvalidArgument(); } thresholdWarningIntf->warningHigh(unitModifier(warningHigh)); thresholdWarningIntf->warningLow(unitModifier(warningLow)); } if (hasCriticalThresholds) { try { thresholdCriticalIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error:: InvalidArgument(); } thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh)); thresholdCriticalIntf->criticalLow(unitModifier(criticalLow)); } } NumericSensor::NumericSensor( const pldm_tid_t tid, const bool sensorDisabled, std::shared_ptr pdr, std::string& sensorName, std::string& associationPath) : tid(tid), sensorName(sensorName), isPriority(false) { if (!pdr) { throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } sensorId = pdr->sensor_id; std::string path; SensorUnit sensorUnit = SensorUnit::DegreesC; switch (pdr->base_unit) { case PLDM_SENSOR_UNIT_DEGRESS_C: sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/"; sensorUnit = SensorUnit::DegreesC; break; case PLDM_SENSOR_UNIT_VOLTS: sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/"; sensorUnit = SensorUnit::Volts; break; case PLDM_SENSOR_UNIT_AMPS: sensorNameSpace = "/xyz/openbmc_project/sensors/current/"; sensorUnit = SensorUnit::Amperes; break; case PLDM_SENSOR_UNIT_RPM: sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/"; sensorUnit = SensorUnit::RPMS; break; case PLDM_SENSOR_UNIT_WATTS: sensorNameSpace = "/xyz/openbmc_project/sensors/power/"; sensorUnit = SensorUnit::Watts; break; case PLDM_SENSOR_UNIT_JOULES: sensorNameSpace = "/xyz/openbmc_project/sensors/energy/"; sensorUnit = SensorUnit::Joules; break; case PLDM_SENSOR_UNIT_PERCENTAGE: sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/"; sensorUnit = SensorUnit::Percent; break; default: lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME", sensorName, "UNIT", pdr->base_unit); throw sdbusplus::xyz::openbmc_project::Common::Error:: InvalidArgument(); break; } path = sensorNameSpace + sensorName; try { auto service = pldm::utils::DBusHandler().getService( path.c_str(), "xyz.openbmc_project.Sensor.Value"); if (!service.empty()) { throw sdbusplus::xyz::openbmc_project::Common::Error:: TooManyResources(); } } catch (const std::exception&) { /* The sensor object path is not created */ } auto& bus = pldm::utils::DBusHandler::getBus(); try { associationDefinitionsIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } associationDefinitionsIntf->associations( {{"chassis", "all_sensors", associationPath.c_str()}}); double maxValue = std::numeric_limits::quiet_NaN(); double minValue = std::numeric_limits::quiet_NaN(); bool hasWarningThresholds = false; bool hasCriticalThresholds = false; double criticalHigh = std::numeric_limits::quiet_NaN(); double criticalLow = std::numeric_limits::quiet_NaN(); double warningHigh = std::numeric_limits::quiet_NaN(); double warningLow = std::numeric_limits::quiet_NaN(); if (pdr->range_field_support.bits.bit0) { hasWarningThresholds = true; warningHigh = pdr->warning_high; } if (pdr->range_field_support.bits.bit1) { hasWarningThresholds = true; warningLow = pdr->warning_low; } if (pdr->range_field_support.bits.bit2) { hasCriticalThresholds = true; criticalHigh = pdr->critical_high; } if (pdr->range_field_support.bits.bit3) { hasCriticalThresholds = true; criticalLow = pdr->critical_low; } resolution = std::numeric_limits::quiet_NaN(); offset = std::numeric_limits::quiet_NaN(); baseUnitModifier = pdr->unit_modifier; /** * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds * updateTime is in microseconds */ updateTime = static_cast(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000); try { valueIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } valueIntf->maxValue(unitModifier(conversionFormula(maxValue))); valueIntf->minValue(unitModifier(conversionFormula(minValue))); hysteresis = unitModifier(conversionFormula(hysteresis)); valueIntf->unit(sensorUnit); try { availabilityIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } availabilityIntf->available(true); try { operationalStatusIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); } operationalStatusIntf->functional(!sensorDisabled); if (hasWarningThresholds) { try { thresholdWarningIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error:: InvalidArgument(); } thresholdWarningIntf->warningHigh(unitModifier(warningHigh)); thresholdWarningIntf->warningLow(unitModifier(warningLow)); } if (hasCriticalThresholds) { try { thresholdCriticalIntf = std::make_unique(bus, path.c_str()); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}", "PATH", path, "ERROR", e); throw sdbusplus::xyz::openbmc_project::Common::Error:: InvalidArgument(); } thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh)); thresholdCriticalIntf->criticalLow(unitModifier(criticalLow)); } } double NumericSensor::conversionFormula(double value) { double convertedValue = value; convertedValue *= std::isnan(resolution) ? 1 : resolution; convertedValue += std::isnan(offset) ? 0 : offset; return convertedValue; } double NumericSensor::unitModifier(double value) { return std::isnan(value) ? value : value * std::pow(10, baseUnitModifier); } } // namespace platform_mc } // namespace pldm