platform-mc: Create Numeric sensor D-Bus object
Added numeric_sensor class. The NumericSensor class will create the
Numeric sensor D-Bus object. The class also handles sensor status and
exports its status to D-Bus interfaces.
tested: Verified on ast2600 EVB which is connected to a PLDM device
over I2C. bmcweb can display the state of numeric sensor.
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I1c7de2e74100ed787ed2119896d3c5b36098dd96
diff --git a/platform-mc/numeric_sensor.cpp b/platform-mc/numeric_sensor.cpp
new file mode 100644
index 0000000..be7ece3
--- /dev/null
+++ b/platform-mc/numeric_sensor.cpp
@@ -0,0 +1,585 @@
+#include "numeric_sensor.hpp"
+
+#include "libpldm/platform.h"
+
+#include "common/utils.hpp"
+#include "requester/handler.hpp"
+
+#include <limits>
+#include <regex>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+NumericSensor::NumericSensor(const pldm_tid_t tid, const bool sensorDisabled,
+ std::shared_ptr<pldm_numeric_sensor_value_pdr> 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<AssociationDefinitionsInft>(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<double>::quiet_NaN();
+ double minValue = std::numeric_limits<double>::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<double>::quiet_NaN();
+ double criticalLow = std::numeric_limits<double>::quiet_NaN();
+ double warningHigh = std::numeric_limits<double>::quiet_NaN();
+ double warningLow = std::numeric_limits<double>::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<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
+ if (!std::isnan(pdr->update_interval))
+ {
+ updateTime = pdr->update_interval * 1000000;
+ }
+
+ try
+ {
+ valueIntf = std::make_unique<ValueIntf>(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<AvailabilityIntf>(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<OperationalStatusIntf>(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<ThresholdWarningIntf>(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<ThresholdCriticalIntf>(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<pldm_compact_numeric_sensor_pdr> 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<AssociationDefinitionsInft>(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<double>::quiet_NaN();
+ double minValue = std::numeric_limits<double>::quiet_NaN();
+ bool hasWarningThresholds = false;
+ bool hasCriticalThresholds = false;
+ double criticalHigh = std::numeric_limits<double>::quiet_NaN();
+ double criticalLow = std::numeric_limits<double>::quiet_NaN();
+ double warningHigh = std::numeric_limits<double>::quiet_NaN();
+ double warningLow = std::numeric_limits<double>::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<double>::quiet_NaN();
+ offset = std::numeric_limits<double>::quiet_NaN();
+ baseUnitModifier = pdr->unit_modifier;
+
+ /**
+ * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
+ * updateTime is in microseconds
+ */
+ updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
+ try
+ {
+ valueIntf = std::make_unique<ValueIntf>(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<AvailabilityIntf>(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<OperationalStatusIntf>(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<ThresholdWarningIntf>(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<ThresholdCriticalIntf>(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