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/meson.build b/meson.build
index bfbf63e..af6bf6e 100644
--- a/meson.build
+++ b/meson.build
@@ -72,6 +72,7 @@
elif get_option('transport-implementation') == 'af-mctp'
conf_data.set('PLDM_TRANSPORT_WITH_AF_MCTP', 1)
endif
+conf_data.set('DEFAULT_SENSOR_UPDATER_INTERVAL', get_option('default-sensor-update-interval'))
configure_file(output: 'config.h',
configuration: conf_data
@@ -174,6 +175,7 @@
'platform-mc/terminus.cpp',
'platform-mc/platform_manager.cpp',
'platform-mc/manager.cpp',
+ 'platform-mc/numeric_sensor.cpp',
'requester/mctp_endpoint_discovery.cpp',
implicit_include_directories: false,
dependencies: deps,
diff --git a/meson.options b/meson.options
index d52506e..63726dd 100644
--- a/meson.options
+++ b/meson.options
@@ -174,3 +174,18 @@
value: 8384512,
description: 'OEM-IBM: max DMA size'
)
+
+## Default Sensor Update Interval Options
+option(
+ 'default-sensor-update-interval',
+ type: 'integer',
+ min: 1,
+ max: 4294967295,
+ description: '''The default sensor polling interval in milliseconds.
+ The value will be used when the internal is not configured
+ in the PLDM sensor PDRs use `updateInterval` field. `pldmd`
+ will send `GetSensorReading` to get the PLDM sensor values
+ of the monitoring terminus after each configured
+ interval.''',
+ value: 999
+)
\ No newline at end of file
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
diff --git a/platform-mc/numeric_sensor.hpp b/platform-mc/numeric_sensor.hpp
new file mode 100644
index 0000000..315787d
--- /dev/null
+++ b/platform-mc/numeric_sensor.hpp
@@ -0,0 +1,113 @@
+#pragma once
+
+#include "libpldm/platform.h"
+#include "libpldm/pldm.h"
+
+#include "common/types.hpp"
+
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/Sensor/Threshold/Critical/server.hpp>
+#include <xyz/openbmc_project/Sensor/Threshold/Warning/server.hpp>
+#include <xyz/openbmc_project/Sensor/Value/server.hpp>
+#include <xyz/openbmc_project/State/Decorator/Availability/server.hpp>
+#include <xyz/openbmc_project/State/Decorator/OperationalStatus/server.hpp>
+
+#include <string>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+using SensorUnit = sdbusplus::xyz::openbmc_project::Sensor::server::Value::Unit;
+using ValueIntf = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::Sensor::server::Value>;
+using ThresholdWarningIntf = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::Warning>;
+using ThresholdCriticalIntf = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::Critical>;
+using OperationalStatusIntf =
+ sdbusplus::server::object_t<sdbusplus::xyz::openbmc_project::State::
+ Decorator::server::OperationalStatus>;
+using AvailabilityIntf = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::State::Decorator::server::Availability>;
+using AssociationDefinitionsInft = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::Association::server::Definitions>;
+
+/**
+ * @brief NumericSensor
+ *
+ * This class handles sensor reading updated by sensor manager and export
+ * status to D-Bus interface.
+ */
+class NumericSensor
+{
+ public:
+ NumericSensor(const pldm_tid_t tid, const bool sensorDisabled,
+ std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr,
+ std::string& sensorName, std::string& associationPath);
+
+ NumericSensor(const pldm_tid_t tid, const bool sensorDisabled,
+ std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
+ std::string& sensorName, std::string& associationPath);
+
+ ~NumericSensor(){};
+
+ /** @brief ConversionFormula is used to convert raw value to the unit
+ * specified in PDR
+ *
+ * @param[in] value - raw value
+ * @return double - converted value
+ */
+ double conversionFormula(double value);
+
+ /** @brief UnitModifier is used to apply the unit modifier specified in PDR
+ *
+ * @param[in] value - raw value
+ * @return double - converted value
+ */
+ double unitModifier(double value);
+
+ /** @brief Terminus ID which the sensor belongs to */
+ pldm_tid_t tid;
+
+ /** @brief Sensor ID */
+ uint16_t sensorId;
+
+ /** @brief The time of sensor update interval in usec */
+ uint64_t updateTime;
+
+ /** @brief sensorName */
+ std::string sensorName;
+
+ /** @brief sensorNameSpace */
+ std::string sensorNameSpace;
+
+ /** @brief indicate if sensor is polled in priority */
+ bool isPriority;
+
+ private:
+ std::unique_ptr<ValueIntf> valueIntf = nullptr;
+ std::unique_ptr<ThresholdWarningIntf> thresholdWarningIntf = nullptr;
+ std::unique_ptr<ThresholdCriticalIntf> thresholdCriticalIntf = nullptr;
+ std::unique_ptr<AvailabilityIntf> availabilityIntf = nullptr;
+ std::unique_ptr<OperationalStatusIntf> operationalStatusIntf = nullptr;
+ std::unique_ptr<AssociationDefinitionsInft> associationDefinitionsIntf =
+ nullptr;
+
+ /** @brief Amount of hysteresis associated with the sensor thresholds */
+ double hysteresis;
+
+ /** @brief The resolution of sensor in Units */
+ double resolution;
+
+ /** @brief A constant value that is added in as part of conversion process
+ * of converting a raw sensor reading to Units */
+ double offset;
+
+ /** @brief A power-of-10 multiplier for baseUnit */
+ int8_t baseUnitModifier;
+};
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/terminus.cpp b/platform-mc/terminus.cpp
index 2892dc1..5b2ab3c 100644
--- a/platform-mc/terminus.cpp
+++ b/platform-mc/terminus.cpp
@@ -82,6 +82,30 @@
return std::nullopt;
}
+bool Terminus::createInventoryPath(std::string tName)
+{
+ if (tName.empty())
+ {
+ return false;
+ }
+
+ inventoryPath = "/xyz/openbmc_project/inventory/system/board/" + tName;
+ try
+ {
+ inventoryItemBoardInft = std::make_unique<InventoryItemBoardIntf>(
+ utils::DBusHandler::getBus(), inventoryPath.c_str());
+ return true;
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error(
+ "Failed to create Inventory Board interface for device {PATH}",
+ "PATH", inventoryPath);
+ }
+
+ return false;
+}
+
void Terminus::parseTerminusPDRs()
{
std::vector<std::shared_ptr<pldm_numeric_sensor_value_pdr>>
@@ -177,6 +201,30 @@
"NAME", tName.value());
terminusName = static_cast<std::string>(tName.value());
}
+
+ if (terminusName.empty() &&
+ (numericSensorPdrs.size() || compactNumericSensorPdrs.size()))
+ {
+ lg2::error(
+ "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
+ "TID", tid);
+ return;
+ }
+
+ if (createInventoryPath(terminusName))
+ {
+ lg2::error("Terminus ID {TID}: Created Inventory path.", "TID", tid);
+ }
+
+ for (auto pdr : numericSensorPdrs)
+ {
+ addNumericSensor(pdr);
+ }
+
+ for (auto pdr : compactNumericSensorPdrs)
+ {
+ addCompactNumericSensor(pdr);
+ }
}
std::shared_ptr<SensorAuxiliaryNames>
@@ -329,6 +377,55 @@
return parsedPdr;
}
+void Terminus::addNumericSensor(
+ const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr)
+{
+ uint16_t sensorId = pdr->sensor_id;
+ if (terminusName.empty())
+ {
+ lg2::error(
+ "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
+ "TID", tid);
+ return;
+ }
+ std::string sensorName = terminusName + "_" + "Sensor_" +
+ std::to_string(pdr->sensor_id);
+
+ if (pdr->sensor_auxiliary_names_pdr)
+ {
+ auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
+ if (sensorAuxiliaryNames)
+ {
+ const auto& [sensorId, sensorCnt,
+ sensorNames] = *sensorAuxiliaryNames;
+ if (sensorCnt == 1)
+ {
+ for (const auto& [languageTag, name] : sensorNames[0])
+ {
+ if (languageTag == "en" && !name.empty())
+ {
+ sensorName = terminusName + "_" + name;
+ }
+ }
+ }
+ }
+ }
+
+ try
+ {
+ auto sensor = std::make_shared<NumericSensor>(
+ tid, true, pdr, sensorName, inventoryPath);
+ lg2::info("Created NumericSensor {NAME}", "NAME", sensorName);
+ numericSensors.emplace_back(sensor);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error(
+ "Failed to create NumericSensor. error - {ERROR} sensorname - {NAME}",
+ "ERROR", e, "NAME", sensorName);
+ }
+}
+
std::shared_ptr<SensorAuxiliaryNames>
Terminus::parseCompactNumericSensorNames(const std::vector<uint8_t>& sPdr)
{
@@ -393,5 +490,50 @@
return parsedPdr;
}
+void Terminus::addCompactNumericSensor(
+ const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr)
+{
+ uint16_t sensorId = pdr->sensor_id;
+ if (terminusName.empty())
+ {
+ lg2::error(
+ "Terminus ID {TID}: DOES NOT have name. Skip Adding sensors.",
+ "TID", tid);
+ return;
+ }
+ std::string sensorName = terminusName + "_" + "Sensor_" +
+ std::to_string(pdr->sensor_id);
+
+ auto sensorAuxiliaryNames = getSensorAuxiliaryNames(sensorId);
+ if (sensorAuxiliaryNames)
+ {
+ const auto& [sensorId, sensorCnt, sensorNames] = *sensorAuxiliaryNames;
+ if (sensorCnt == 1)
+ {
+ for (const auto& [languageTag, name] : sensorNames[0])
+ {
+ if (languageTag == "en" && !name.empty())
+ {
+ sensorName = terminusName + "_" + name;
+ }
+ }
+ }
+ }
+
+ try
+ {
+ auto sensor = std::make_shared<NumericSensor>(
+ tid, true, pdr, sensorName, inventoryPath);
+ lg2::info("Created Compact NumericSensor {NAME}", "NAME", sensorName);
+ numericSensors.emplace_back(sensor);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error(
+ "Failed to create Compact NumericSensor. error - {ERROR} sensorname - {NAME}",
+ "ERROR", e, "NAME", sensorName);
+ }
+}
+
} // namespace platform_mc
} // namespace pldm
diff --git a/platform-mc/terminus.hpp b/platform-mc/terminus.hpp
index aacf288..8a39b2e 100644
--- a/platform-mc/terminus.hpp
+++ b/platform-mc/terminus.hpp
@@ -3,7 +3,9 @@
#include "libpldm/platform.h"
#include "common/types.hpp"
+#include "numeric_sensor.hpp"
#include "requester/handler.hpp"
+#include "terminus.hpp"
#include <sdbusplus/server/object.hpp>
#include <sdeventplus/event.hpp>
@@ -32,6 +34,8 @@
using SensorAuxiliaryNames = std::tuple<
SensorId, SensorCnt,
std::vector<std::vector<std::pair<NameLanguageTag, SensorName>>>>;
+using InventoryItemBoardIntf = sdbusplus::server::object_t<
+ sdbusplus::xyz::openbmc_project::Inventory::Item::server::Board>;
/** @struct EntityKey
*
@@ -129,6 +133,9 @@
/** @brief A flag to indicate if terminus has been initialized */
bool initialized = false;
+ /** @brief A list of numericSensors */
+ std::vector<std::shared_ptr<NumericSensor>> numericSensors{};
+
/** @brief Get Sensor Auxiliary Names by sensorID
*
* @param[in] id - sensor ID
@@ -143,6 +150,15 @@
*/
std::optional<std::string_view> findTerminusName();
+ /** @brief Construct the NumericSensor sensor class for the PLDM sensor.
+ * The NumericSensor class will handle create D-Bus object path,
+ * provide the APIs to update sensor value, threshold...
+ *
+ * @param[in] pdr - the numeric sensor PDR info
+ */
+ void addNumericSensor(
+ const std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr);
+
/** @brief Parse the numeric sensor PDRs
*
* @param[in] pdrData - the response PDRs from GetPDR command
@@ -167,6 +183,14 @@
std::shared_ptr<EntityAuxiliaryNames>
parseEntityAuxiliaryNamesPDR(const std::vector<uint8_t>& pdrData);
+ /** @brief Construct the NumericSensor sensor class for the compact numeric
+ * PLDM sensor.
+ *
+ * @param[in] pdr - the compact numeric sensor PDR info
+ */
+ void addCompactNumericSensor(
+ const std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr);
+
/** @brief Parse the compact numeric sensor PDRs
*
* @param[in] pdrData - the response PDRs from GetPDR command
@@ -183,6 +207,15 @@
std::shared_ptr<SensorAuxiliaryNames>
parseCompactNumericSensorNames(const std::vector<uint8_t>& pdrData);
+ /** @brief Create the terminus inventory path to
+ * /xyz/openbmc_project/inventory/Item/Board/.
+ *
+ * @param[in] tName - the terminus name
+ * @return true/false: True if there is no error in creating inventory path
+ *
+ */
+ bool createInventoryPath(std::string tName);
+
/* @brief The terminus's TID */
pldm_tid_t tid;
@@ -209,6 +242,11 @@
/** @brief Terminus name */
EntityName terminusName{};
+ /* @brief The pointer of iventory D-Bus interface for the terminus */
+ std::unique_ptr<InventoryItemBoardIntf> inventoryItemBoardInft = nullptr;
+
+ /* @brief Inventory D-Bus object path of the terminus */
+ std::string inventoryPath;
};
} // namespace platform_mc
} // namespace pldm
diff --git a/platform-mc/test/meson.build b/platform-mc/test/meson.build
index 8c4f570..6b95f90 100644
--- a/platform-mc/test/meson.build
+++ b/platform-mc/test/meson.build
@@ -4,6 +4,7 @@
'../terminus.cpp',
'../platform_manager.cpp',
'../manager.cpp',
+ '../numeric_sensor.cpp',
'../../requester/mctp_endpoint_discovery.cpp'],
include_directories: ['../../requester', '../../pldmd'])
diff --git a/platform-mc/test/terminus_test.cpp b/platform-mc/test/terminus_test.cpp
index 8919b19..925f284 100644
--- a/platform-mc/test/terminus_test.cpp
+++ b/platform-mc/test/terminus_test.cpp
@@ -1,5 +1,6 @@
#include "libpldm/entity.h"
+#include "platform-mc/numeric_sensor.hpp"
#include "platform-mc/terminus.hpp"
#include <gtest/gtest.h>
@@ -62,7 +63,32 @@
0x0 // sensorName
};
+ std::vector<uint8_t> pdr2{
+ 0x1, 0x0, 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+ 0x1,
+ 0x0, // recordChangeNumber
+ 0x11,
+ 0, // dataLength
+ /* Entity Auxiliary Names PDR Data*/
+ 3,
+ 0x80, // entityType system software
+ 0x1,
+ 0x0, // Entity instance number =1
+ 0,
+ 0, // Overal system
+ 0, // shared Name Count one name only
+ 01, // nameStringCount
+ 0x65, 0x6e, 0x00,
+ 0x00, // Language Tag "en"
+ 0x53, 0x00, 0x30, 0x00,
+ 0x00 // Entity Name "S0"
+ };
+
t1.pdrs.emplace_back(pdr1);
+ t1.pdrs.emplace_back(pdr2);
t1.parseTerminusPDRs();
auto sensorAuxNames = t1.getSensorAuxiliaryNames(0);
@@ -78,4 +104,41 @@
EXPECT_EQ(1, names[0].size());
EXPECT_EQ("en", names[0][0].first);
EXPECT_EQ("TEMP1", names[0][0].second);
+ EXPECT_EQ(2, t1.pdrs.size());
+ EXPECT_EQ("S0", t1.getTerminusName());
+}
+
+TEST(TerminusTest, parsePDRTestNoSensorPDR)
+{
+ auto t1 = pldm::platform_mc::Terminus(1,
+ 1 << PLDM_BASE | 1 << PLDM_PLATFORM);
+ std::vector<uint8_t> pdr1{
+ 0x1, 0x0, 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+ 0x1,
+ 0x0, // recordChangeNumber
+ 0x11,
+ 0, // dataLength
+ /* Entity Auxiliary Names PDR Data*/
+ 3,
+ 0x80, // entityType system software
+ 0x1,
+ 0x0, // Entity instance number =1
+ 0,
+ 0, // Overal system
+ 0, // shared Name Count one name only
+ 01, // nameStringCount
+ 0x65, 0x6e, 0x00,
+ 0x00, // Language Tag "en"
+ 0x53, 0x00, 0x30, 0x00,
+ 0x00 // Entity Name "S0"
+ };
+
+ t1.pdrs.emplace_back(pdr1);
+ t1.parseTerminusPDRs();
+
+ auto sensorAuxNames = t1.getSensorAuxiliaryNames(1);
+ EXPECT_EQ(nullptr, sensorAuxNames);
}