#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) { 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; timeStamp = 0; /** * 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) { 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; timeStamp = 0; /** * 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); } void NumericSensor::updateReading(bool available, bool functional, double value) { if (!availabilityIntf || !operationalStatusIntf || !valueIntf) { lg2::error( "Failed to update sensor {NAME} D-Bus interface don't exist.", "NAME", sensorName); return; } availabilityIntf->available(available); operationalStatusIntf->functional(functional); double curValue = valueIntf->value(); double newValue = std::numeric_limits::quiet_NaN(); if (functional && available) { newValue = unitModifier(conversionFormula(value)); if (newValue != curValue && (!std::isnan(newValue) || !std::isnan(curValue))) { valueIntf->value(newValue); updateThresholds(); } } else { if (newValue != curValue && (!std::isnan(newValue) || !std::isnan(curValue))) { valueIntf->value(std::numeric_limits::quiet_NaN()); } } } void NumericSensor::handleErrGetSensorReading() { if (!operationalStatusIntf || !valueIntf) { lg2::error( "Failed to update sensor {NAME} D-Bus interfaces don't exist.", "NAME", sensorName); return; } operationalStatusIntf->functional(false); valueIntf->value(std::numeric_limits::quiet_NaN()); } bool NumericSensor::checkThreshold(bool alarm, bool direction, double value, double threshold, double hyst) { if (direction) { if (value >= threshold) { return true; } if (value < (threshold - hyst)) { return false; } } else { if (value <= threshold) { return true; } if (value > (threshold + hyst)) { return false; } } return alarm; } void NumericSensor::updateThresholds() { if (!valueIntf) { lg2::error( "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.", "NAME", sensorName); return; } auto value = valueIntf->value(); if (thresholdWarningIntf && !std::isnan(thresholdWarningIntf->warningHigh())) { auto threshold = thresholdWarningIntf->warningHigh(); auto alarm = thresholdWarningIntf->warningAlarmHigh(); auto newAlarm = checkThreshold(alarm, true, value, threshold, hysteresis); if (alarm != newAlarm) { thresholdWarningIntf->warningAlarmHigh(newAlarm); if (newAlarm) { thresholdWarningIntf->warningHighAlarmAsserted(value); } else { thresholdWarningIntf->warningHighAlarmDeasserted(value); } } } if (thresholdWarningIntf && !std::isnan(thresholdWarningIntf->warningLow())) { auto threshold = thresholdWarningIntf->warningLow(); auto alarm = thresholdWarningIntf->warningAlarmLow(); auto newAlarm = checkThreshold(alarm, false, value, threshold, hysteresis); if (alarm != newAlarm) { thresholdWarningIntf->warningAlarmLow(newAlarm); if (newAlarm) { thresholdWarningIntf->warningLowAlarmAsserted(value); } else { thresholdWarningIntf->warningLowAlarmDeasserted(value); } } } if (thresholdCriticalIntf && !std::isnan(thresholdCriticalIntf->criticalHigh())) { auto threshold = thresholdCriticalIntf->criticalHigh(); auto alarm = thresholdCriticalIntf->criticalAlarmHigh(); auto newAlarm = checkThreshold(alarm, true, value, threshold, hysteresis); if (alarm != newAlarm) { thresholdCriticalIntf->criticalAlarmHigh(newAlarm); if (newAlarm) { thresholdCriticalIntf->criticalHighAlarmAsserted(value); } else { thresholdCriticalIntf->criticalHighAlarmDeasserted(value); } } } if (thresholdCriticalIntf && !std::isnan(thresholdCriticalIntf->criticalLow())) { auto threshold = thresholdCriticalIntf->criticalLow(); auto alarm = thresholdCriticalIntf->criticalAlarmLow(); auto newAlarm = checkThreshold(alarm, false, value, threshold, hysteresis); if (alarm != newAlarm) { thresholdCriticalIntf->criticalAlarmLow(newAlarm); if (newAlarm) { thresholdCriticalIntf->criticalLowAlarmAsserted(value); } else { thresholdCriticalIntf->criticalLowAlarmDeasserted(value); } } } } } // namespace platform_mc } // namespace pldm