platform-mc: Add sensor manager
Added sensor_manager class. The sensor_manager class manages the timing
of sensor polling.
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: Gilbert Chen <gilbert.chen@arm.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I4257f823ea26d7fdb322cc82d847e94db056258c
diff --git a/common/types.hpp b/common/types.hpp
index 1a91235..661c2e7 100644
--- a/common/types.hpp
+++ b/common/types.hpp
@@ -15,6 +15,7 @@
namespace pldm
{
+using Availability = bool;
using eid = uint8_t;
using UUID = std::string;
using Request = std::vector<uint8_t>;
diff --git a/meson.build b/meson.build
index af6bf6e..e8c3bc8 100644
--- a/meson.build
+++ b/meson.build
@@ -73,6 +73,7 @@
conf_data.set('PLDM_TRANSPORT_WITH_AF_MCTP', 1)
endif
conf_data.set('DEFAULT_SENSOR_UPDATER_INTERVAL', get_option('default-sensor-update-interval'))
+conf_data.set('SENSOR_POLLING_TIME', get_option('sensor-polling-time'))
configure_file(output: 'config.h',
configuration: conf_data
@@ -175,6 +176,7 @@
'platform-mc/terminus.cpp',
'platform-mc/platform_manager.cpp',
'platform-mc/manager.cpp',
+ 'platform-mc/sensor_manager.cpp',
'platform-mc/numeric_sensor.cpp',
'requester/mctp_endpoint_discovery.cpp',
implicit_include_directories: false,
diff --git a/meson.options b/meson.options
index 63726dd..8a7138a 100644
--- a/meson.options
+++ b/meson.options
@@ -188,4 +188,23 @@
of the monitoring terminus after each configured
interval.''',
value: 999
-)
\ No newline at end of file
+)
+
+# Platform-mc configuration parameters
+
+## Sensor Polling Options
+option(
+ 'sensor-polling-time',
+ type: 'integer',
+ min: 1,
+ max: 10000,
+ description: '''The configured timeout in milliseconds of the common sensor
+ polling timer of each terminus which will trigger the
+ terminus sensor reading task. The task will check
+ whether the sensor in the terminus sensors list need to
+ be updated by comparing the sensor `updateInterval` with the
+ interval between current timestamp and latest updated
+ timestamp of the sensor. The task will send
+ `GetSensorReading` if the sensor need to be updated.''',
+ value: 249
+)
diff --git a/platform-mc/manager.hpp b/platform-mc/manager.hpp
index 7636e94..d8210b4 100644
--- a/platform-mc/manager.hpp
+++ b/platform-mc/manager.hpp
@@ -7,6 +7,7 @@
#include "platform_manager.hpp"
#include "requester/handler.hpp"
#include "requester/mctp_endpoint_discovery.hpp"
+#include "sensor_manager.hpp"
#include "terminus_manager.hpp"
namespace pldm
@@ -33,7 +34,8 @@
explicit Manager(sdeventplus::Event& event, RequesterHandler& handler,
pldm::InstanceIdDb& instanceIdDb) :
terminusManager(event, handler, instanceIdDb, termini, this),
- platformManager(terminusManager, termini)
+ platformManager(terminusManager, termini),
+ sensorManager(event, terminusManager, termini)
{}
/** @brief Helper function to do the actions before discovering terminus
@@ -68,6 +70,33 @@
terminusManager.removeMctpTerminus(mctpInfos);
}
+ /** @brief Helper function to start sensor polling of the terminus TID
+ */
+ void startSensorPolling(pldm_tid_t tid)
+ {
+ sensorManager.startPolling(tid);
+ }
+
+ /** @brief Helper function to set available state for pldm request (sensor
+ * polling and event polling) of the terminus TID. The `false` state
+ * will trigger stop flow in coroutine of sensor polling/event
+ * polling task to stop.
+ */
+ void updateAvailableState(pldm_tid_t tid, Availability state)
+ {
+ if (termini.contains(tid))
+ {
+ sensorManager.updateAvailableState(tid, state);
+ }
+ }
+
+ /** @brief Helper function to stop sensor polling of the terminus TID
+ */
+ void stopSensorPolling(pldm_tid_t tid)
+ {
+ sensorManager.stopPolling(tid);
+ }
+
private:
/** @brief List of discovered termini */
TerminiMapper termini{};
@@ -77,6 +106,9 @@
/** @brief Platform interface for calling the hook functions */
PlatformManager platformManager;
+
+ /** @brief Store platform manager handler */
+ SensorManager sensorManager;
};
} // namespace platform_mc
} // namespace pldm
diff --git a/platform-mc/numeric_sensor.cpp b/platform-mc/numeric_sensor.cpp
index ad563fd..af6efad 100644
--- a/platform-mc/numeric_sensor.cpp
+++ b/platform-mc/numeric_sensor.cpp
@@ -15,11 +15,10 @@
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)
+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)
{
if (!pdr)
{
@@ -264,6 +263,7 @@
resolution = pdr->resolution;
offset = pdr->offset;
baseUnitModifier = pdr->unit_modifier;
+ timeStamp = 0;
/**
* DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
@@ -362,7 +362,7 @@
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)
+ tid(tid), sensorName(sensorName)
{
if (!pdr)
{
@@ -478,6 +478,7 @@
resolution = std::numeric_limits<double>::quiet_NaN();
offset = std::numeric_limits<double>::quiet_NaN();
baseUnitModifier = pdr->unit_modifier;
+ timeStamp = 0;
/**
* DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
@@ -579,5 +580,175 @@
{
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<double>::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<double>::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<double>::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
diff --git a/platform-mc/numeric_sensor.hpp b/platform-mc/numeric_sensor.hpp
index 770a286..7b2fc6a 100644
--- a/platform-mc/numeric_sensor.hpp
+++ b/platform-mc/numeric_sensor.hpp
@@ -54,6 +54,15 @@
~NumericSensor() {};
+ /** @brief The function called by Sensor Manager to set sensor to
+ * error status.
+ */
+ void handleErrGetSensorReading();
+
+ /** @brief Updating the sensor status to D-Bus interface
+ */
+ void updateReading(bool available, bool functional, double value = 0);
+
/** @brief ConversionFormula is used to convert raw value to the unit
* specified in PDR
*
@@ -69,12 +78,103 @@
*/
double unitModifier(double value);
+ /** @brief Check if value is over threshold.
+ *
+ * @param[in] alarm - previous alarm state
+ * @param[in] direction - upper or lower threshold checking
+ * @param[in] value - raw value
+ * @param[in] threshold - threshold value
+ * @param[in] hyst - hysteresis value
+ * @return bool - new alarm state
+ */
+ bool checkThreshold(bool alarm, bool direction, double value,
+ double threshold, double hyst);
+
+ /** @brief Updating the association to D-Bus interface
+ * @param[in] inventoryPath - inventory path of the entity
+ */
+ inline void setInventoryPath(const std::string& inventoryPath)
+ {
+ if (associationDefinitionsIntf)
+ {
+ associationDefinitionsIntf->associations(
+ {{"chassis", "all_sensors", inventoryPath}});
+ }
+ }
+
+ /** @brief Get Upper Critical threshold
+ *
+ * @return double - Upper Critical threshold
+ */
+ double getThresholdUpperCritical()
+ {
+ if (thresholdCriticalIntf)
+ {
+ return thresholdCriticalIntf->criticalHigh();
+ }
+ else
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+ };
+
+ /** @brief Get Lower Critical threshold
+ *
+ * @return double - Lower Critical threshold
+ */
+ double getThresholdLowerCritical()
+ {
+ if (thresholdCriticalIntf)
+ {
+ return thresholdCriticalIntf->criticalLow();
+ }
+ else
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+ };
+
+ /** @brief Get Upper Warning threshold
+ *
+ * @return double - Upper Warning threshold
+ */
+ double getThresholdUpperWarning()
+ {
+ if (thresholdWarningIntf)
+ {
+ return thresholdWarningIntf->warningHigh();
+ }
+ else
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+ };
+
+ /** @brief Get Lower Warning threshold
+ *
+ * @return double - Lower Warning threshold
+ */
+ double getThresholdLowerWarning()
+ {
+ if (thresholdWarningIntf)
+ {
+ return thresholdWarningIntf->warningLow();
+ }
+ else
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+ };
+
/** @brief Terminus ID which the sensor belongs to */
pldm_tid_t tid;
/** @brief Sensor ID */
uint16_t sensorId;
+ /** @brief The time stamp since last getSensorReading command in usec */
+ uint64_t timeStamp;
+
/** @brief The time of sensor update interval in usec */
uint64_t updateTime;
@@ -84,10 +184,13 @@
/** @brief sensorNameSpace */
std::string sensorNameSpace;
- /** @brief indicate if sensor is polled in priority */
- bool isPriority;
-
private:
+ /**
+ * @brief Check sensor reading if any threshold has been crossed and update
+ * Threshold interfaces accordingly
+ */
+ void updateThresholds();
+
std::unique_ptr<ValueIntf> valueIntf = nullptr;
std::unique_ptr<ThresholdWarningIntf> thresholdWarningIntf = nullptr;
std::unique_ptr<ThresholdCriticalIntf> thresholdCriticalIntf = nullptr;
diff --git a/platform-mc/sensor_manager.cpp b/platform-mc/sensor_manager.cpp
new file mode 100644
index 0000000..fcfd0a6
--- /dev/null
+++ b/platform-mc/sensor_manager.cpp
@@ -0,0 +1,362 @@
+#include "sensor_manager.hpp"
+
+#include "terminus_manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <exception>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+SensorManager::SensorManager(sdeventplus::Event& event,
+ TerminusManager& terminusManager,
+ TerminiMapper& termini) :
+ event(event), terminusManager(terminusManager), termini(termini),
+ pollingTime(SENSOR_POLLING_TIME)
+{}
+
+void SensorManager::startPolling(pldm_tid_t tid)
+{
+ if (!termini.contains(tid))
+ {
+ return;
+ }
+
+ /* tid already initializes roundRobinSensors list */
+ if (sensorPollTimers.contains(tid))
+ {
+ lg2::info("Terminus ID {TID}: sensor poll timer already exists.", "TID",
+ tid);
+ return;
+ }
+ // numeric sensor
+ auto terminus = termini[tid];
+ for (auto& sensor : terminus->numericSensors)
+ {
+ roundRobinSensors[tid].push(sensor);
+ }
+
+ updateAvailableState(tid, true);
+
+ if (!roundRobinSensors[tid].size())
+ {
+ lg2::info("Terminus ID {TID}: no sensors to poll.", "TID", tid);
+ return;
+ }
+
+ sensorPollTimers[tid] = std::make_unique<sdbusplus::Timer>(
+ event.get(),
+ std::bind_front(&SensorManager::doSensorPolling, this, tid));
+
+ try
+ {
+ if (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning())
+ {
+ sensorPollTimers[tid]->start(
+ duration_cast<std::chrono::milliseconds>(
+ std::chrono::milliseconds(pollingTime)),
+ true);
+ }
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error(
+ "Terminus ID {TID}: Failed to start sensor polling timer. Exception: {EXCEPTION}",
+ "TID", tid, "EXCEPTION", e);
+ return;
+ }
+}
+
+void SensorManager::stopPolling(pldm_tid_t tid)
+{
+ /* Stop polling timer */
+ if (sensorPollTimers.contains(tid))
+ {
+ sensorPollTimers[tid]->stop();
+ sensorPollTimers.erase(tid);
+ }
+
+ roundRobinSensors.erase(tid);
+
+ if (doSensorPollingTaskHandles.contains(tid))
+ {
+ auto& [scope, rcOpt] = doSensorPollingTaskHandles[tid];
+ scope.request_stop();
+ doSensorPollingTaskHandles.erase(tid);
+ }
+
+ availableState.erase(tid);
+}
+
+void SensorManager::doSensorPolling(pldm_tid_t tid)
+{
+ auto it = doSensorPollingTaskHandles.find(tid);
+ if (it != doSensorPollingTaskHandles.end())
+ {
+ auto& [scope, rcOpt] = it->second;
+ if (!rcOpt.has_value())
+ {
+ return;
+ }
+ doSensorPollingTaskHandles.erase(tid);
+ }
+
+ auto& [scope, rcOpt] =
+ doSensorPollingTaskHandles
+ .emplace(std::piecewise_construct, std::forward_as_tuple(tid),
+ std::forward_as_tuple())
+ .first->second;
+ scope.spawn(
+ stdexec::just() | stdexec::let_value([this, &rcOpt,
+ tid] -> exec::task<void> {
+ auto res =
+ co_await stdexec::stopped_as_optional(doSensorPollingTask(tid));
+ if (res.has_value())
+ {
+ rcOpt = *res;
+ }
+ else
+ {
+ lg2::info("Stopped polling for Terminus ID {TID}", "TID", tid);
+ try
+ {
+ if (sensorPollTimers.contains(tid) &&
+ sensorPollTimers[tid] &&
+ sensorPollTimers[tid]->isRunning())
+ {
+ sensorPollTimers[tid]->stop();
+ }
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error(
+ "Terminus ID {TID}: Failed to stop polling timer. Exception: {EXCEPTION}",
+ "TID", tid, "EXCEPTION", e);
+ }
+ rcOpt = PLDM_SUCCESS;
+ }
+ }),
+ exec::default_task_context<void>(exec::inline_scheduler{}));
+}
+
+exec::task<int> SensorManager::doSensorPollingTask(pldm_tid_t tid)
+{
+ uint64_t t0 = 0;
+ uint64_t t1 = 0;
+ uint64_t elapsed = 0;
+ uint64_t pollingTimeInUsec = pollingTime * 1000;
+ uint8_t rc = PLDM_SUCCESS;
+
+ do
+ {
+ if ((!sensorPollTimers.contains(tid)) ||
+ (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()))
+ {
+ co_return PLDM_ERROR;
+ }
+
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t0);
+
+ /**
+ * Terminus is not available for PLDM request.
+ * The terminus manager will trigger recovery process to recovery the
+ * communication between the local terminus and the remote terminus.
+ * The sensor polling should be stopped while recovering the
+ * communication.
+ */
+ if (!getAvailableState(tid))
+ {
+ lg2::info(
+ "Terminus ID {TID} is not available for PLDM request from {NOW}.",
+ "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
+ co_await stdexec::just_stopped();
+ }
+
+ if (!termini.contains(tid))
+ {
+ co_return PLDM_SUCCESS;
+ }
+
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+ auto toBeUpdated = roundRobinSensors[tid].size();
+ while (((t1 - t0) < pollingTimeInUsec) && (toBeUpdated > 0))
+ {
+ if (!getAvailableState(tid))
+ {
+ lg2::info(
+ "Terminus ID {TID} is not available for PLDM request from {NOW}.",
+ "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
+ co_await stdexec::just_stopped();
+ }
+
+ auto sensor = roundRobinSensors[tid].front();
+
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+ elapsed = t1 - sensor->timeStamp;
+ if ((sensor->updateTime <= elapsed) || (!sensor->timeStamp))
+ {
+ rc = co_await getSensorReading(sensor);
+
+ if ((!sensorPollTimers.contains(tid)) ||
+ (sensorPollTimers[tid] &&
+ !sensorPollTimers[tid]->isRunning()))
+ {
+ co_return PLDM_ERROR;
+ }
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+ if (rc == PLDM_SUCCESS)
+ {
+ sensor->timeStamp = t1;
+ }
+ else
+ {
+ lg2::error(
+ "Failed to get sensor value for terminus {TID}, error: {RC}",
+ "TID", tid, "RC", rc);
+ }
+ }
+
+ toBeUpdated--;
+ if (roundRobinSensors.contains(tid))
+ {
+ roundRobinSensors[tid].pop();
+ roundRobinSensors[tid].push(std::move(sensor));
+ }
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+ }
+
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+ } while ((t1 - t0) >= pollingTimeInUsec);
+
+ co_return PLDM_SUCCESS;
+}
+
+exec::task<int>
+ SensorManager::getSensorReading(std::shared_ptr<NumericSensor> sensor)
+{
+ if (!sensor)
+ {
+ lg2::error("Call `getSensorReading` with null `sensor` pointer.");
+ co_return PLDM_ERROR_INVALID_DATA;
+ }
+
+ auto tid = sensor->tid;
+ auto sensorId = sensor->sensorId;
+ Request request(sizeof(pldm_msg_hdr) + PLDM_GET_SENSOR_READING_REQ_BYTES);
+ auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
+ auto rc = encode_get_sensor_reading_req(0, sensorId, false, requestMsg);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to encode request GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.",
+ "TID", tid, "ID", sensorId, "RC", rc);
+ co_return rc;
+ }
+
+ if (!getAvailableState(tid))
+ {
+ lg2::info(
+ "Terminus ID {TID} is not available for PLDM request from {NOW}.",
+ "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
+ co_await stdexec::just_stopped();
+ }
+
+ const pldm_msg* responseMsg = nullptr;
+ size_t responseLen = 0;
+ rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
+ &responseLen);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to send GetSensorReading message for terminus {TID}, sensor Id {ID}, error {RC}",
+ "TID", tid, "ID", sensorId, "RC", rc);
+ co_return rc;
+ }
+
+ if ((!sensorPollTimers.contains(tid)) ||
+ (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()))
+ {
+ co_return PLDM_ERROR;
+ }
+
+ uint8_t completionCode = PLDM_SUCCESS;
+ uint8_t sensorDataSize = PLDM_SENSOR_DATA_SIZE_SINT32;
+ uint8_t sensorOperationalState = 0;
+ uint8_t sensorEventMessageEnable = 0;
+ uint8_t presentState = 0;
+ uint8_t previousState = 0;
+ uint8_t eventState = 0;
+ union_sensor_data_size presentReading;
+ rc = decode_get_sensor_reading_resp(
+ responseMsg, responseLen, &completionCode, &sensorDataSize,
+ &sensorOperationalState, &sensorEventMessageEnable, &presentState,
+ &previousState, &eventState,
+ reinterpret_cast<uint8_t*>(&presentReading));
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode response GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.",
+ "TID", tid, "ID", sensorId, "RC", rc);
+ sensor->handleErrGetSensorReading();
+ co_return rc;
+ }
+
+ if (completionCode != PLDM_SUCCESS)
+ {
+ lg2::error(
+ "Error : GetSensorReading for terminus ID {TID}, sensor Id {ID}, complete code {CC}.",
+ "TID", tid, "ID", sensorId, "CC", completionCode);
+ co_return completionCode;
+ }
+
+ double value = std::numeric_limits<double>::quiet_NaN();
+ switch (sensorOperationalState)
+ {
+ case PLDM_SENSOR_ENABLED:
+ break;
+ case PLDM_SENSOR_DISABLED:
+ sensor->updateReading(true, false, value);
+ co_return completionCode;
+ case PLDM_SENSOR_FAILED:
+ sensor->updateReading(false, true, value);
+ co_return completionCode;
+ case PLDM_SENSOR_UNAVAILABLE:
+ default:
+ sensor->updateReading(false, false, value);
+ co_return completionCode;
+ }
+
+ switch (sensorDataSize)
+ {
+ case PLDM_SENSOR_DATA_SIZE_UINT8:
+ value = static_cast<double>(presentReading.value_u8);
+ break;
+ case PLDM_SENSOR_DATA_SIZE_SINT8:
+ value = static_cast<double>(presentReading.value_s8);
+ break;
+ case PLDM_SENSOR_DATA_SIZE_UINT16:
+ value = static_cast<double>(presentReading.value_u16);
+ break;
+ case PLDM_SENSOR_DATA_SIZE_SINT16:
+ value = static_cast<double>(presentReading.value_s16);
+ break;
+ case PLDM_SENSOR_DATA_SIZE_UINT32:
+ value = static_cast<double>(presentReading.value_u32);
+ break;
+ case PLDM_SENSOR_DATA_SIZE_SINT32:
+ value = static_cast<double>(presentReading.value_s32);
+ break;
+ default:
+ value = std::numeric_limits<double>::quiet_NaN();
+ break;
+ }
+
+ sensor->updateReading(true, true, value);
+ co_return completionCode;
+}
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/sensor_manager.hpp b/platform-mc/sensor_manager.hpp
new file mode 100644
index 0000000..1c7c788
--- /dev/null
+++ b/platform-mc/sensor_manager.hpp
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "libpldm/platform.h"
+#include "libpldm/pldm.h"
+
+#include "common/types.hpp"
+#include "requester/handler.hpp"
+#include "terminus.hpp"
+#include "terminus_manager.hpp"
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <utility>
+#include <vector>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+/**
+ * @brief SensorManager
+ *
+ * This class manages the sensors found in terminus and provides
+ * function calls for other classes to start/stop sensor monitoring.
+ *
+ */
+class SensorManager
+{
+ public:
+ SensorManager() = delete;
+ SensorManager(const SensorManager&) = delete;
+ SensorManager(SensorManager&&) = delete;
+ SensorManager& operator=(const SensorManager&) = delete;
+ SensorManager& operator=(SensorManager&&) = delete;
+ virtual ~SensorManager() = default;
+
+ explicit SensorManager(sdeventplus::Event& event,
+ TerminusManager& terminusManager,
+ TerminiMapper& termini);
+
+ /** @brief starting sensor polling task
+ */
+ void startPolling(pldm_tid_t tid);
+
+ /** @brief stopping sensor polling task
+ */
+ void stopPolling(pldm_tid_t tid);
+
+ /** @brief Set available state of terminus for pldm request.
+ */
+ void updateAvailableState(pldm_tid_t tid, Availability state)
+ {
+ availableState[tid] = state;
+ };
+
+ /** @brief Get available state of terminus for pldm request.
+ */
+ bool getAvailableState(pldm_tid_t tid)
+ {
+ if (!availableState.contains(tid))
+ {
+ return false;
+ }
+ return availableState[tid];
+ };
+
+ protected:
+ /** @brief start a coroutine for polling all sensors.
+ */
+ virtual void doSensorPolling(pldm_tid_t tid);
+
+ /** @brief polling all sensors in each terminus
+ *
+ * @param[in] tid - Destination TID
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> doSensorPollingTask(pldm_tid_t tid);
+
+ /** @brief Sending getSensorReading command for the sensor
+ *
+ * @param[in] sensor - the sensor to be updated
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> getSensorReading(std::shared_ptr<NumericSensor> sensor);
+
+ /** @brief Reference to to PLDM daemon's main event loop.
+ */
+ sdeventplus::Event& event;
+
+ /** @brief reference of terminusManager */
+ TerminusManager& terminusManager;
+
+ /** @brief List of discovered termini */
+ TerminiMapper& termini;
+
+ /** @brief sensor polling interval in ms. */
+ uint32_t pollingTime;
+
+ /** @brief sensor polling timers */
+ std::map<pldm_tid_t, std::unique_ptr<sdbusplus::Timer>> sensorPollTimers;
+
+ /** @brief coroutine handle of doSensorPollingTasks */
+ std::map<pldm_tid_t, std::pair<exec::async_scope, std::optional<int>>>
+ doSensorPollingTaskHandles;
+
+ /** @brief Available state for pldm request of terminus */
+ std::map<pldm_tid_t, Availability> availableState;
+
+ /** @brief round robin sensor list */
+ std::map<pldm_tid_t, std::queue<std::shared_ptr<NumericSensor>>>
+ roundRobinSensors;
+};
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/terminus_manager.cpp b/platform-mc/terminus_manager.cpp
index 7567d89..cbf56c5 100644
--- a/platform-mc/terminus_manager.cpp
+++ b/platform-mc/terminus_manager.cpp
@@ -162,6 +162,7 @@
exec::task<int> TerminusManager::discoverMctpTerminusTask()
{
+ std::vector<pldm_tid_t> addedTids;
while (!queuedMctpInfos.empty())
{
if (manager)
@@ -177,11 +178,23 @@
{
co_await initMctpTerminus(mctpInfo);
}
+
+ /* Get TID of initialized terminus */
+ auto tid = toTid(mctpInfo);
+ if (!tid)
+ {
+ co_return PLDM_ERROR;
+ }
+ addedTids.push_back(tid.value());
}
if (manager)
{
co_await manager->afterDiscoverTerminus();
+ for (const auto& tid : addedTids)
+ {
+ manager->startSensorPolling(tid);
+ }
}
queuedMctpInfos.pop();
@@ -201,6 +214,11 @@
continue;
}
+ if (manager)
+ {
+ manager->stopSensorPolling(it->second->getTid());
+ }
+
unmapTid(it->first);
termini.erase(it);
}
diff --git a/platform-mc/test/meson.build b/platform-mc/test/meson.build
index 6b95f90..64177cc 100644
--- a/platform-mc/test/meson.build
+++ b/platform-mc/test/meson.build
@@ -4,6 +4,7 @@
'../terminus.cpp',
'../platform_manager.cpp',
'../manager.cpp',
+ '../sensor_manager.cpp',
'../numeric_sensor.cpp',
'../../requester/mctp_endpoint_discovery.cpp'],
include_directories: ['../../requester', '../../pldmd'])
@@ -12,6 +13,8 @@
'terminus_manager_test',
'terminus_test',
'platform_manager_test',
+ 'sensor_manager_test',
+ 'numeric_sensor_test',
]
foreach t : tests
diff --git a/platform-mc/test/mock_sensor_manager.hpp b/platform-mc/test/mock_sensor_manager.hpp
new file mode 100644
index 0000000..a52e4ea
--- /dev/null
+++ b/platform-mc/test/mock_sensor_manager.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "platform-mc/sensor_manager.hpp"
+
+#include <gmock/gmock.h>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+class MockSensorManager : public SensorManager
+{
+ public:
+ MockSensorManager(sdeventplus::Event& event,
+ TerminusManager& terminusManager,
+ TerminiMapper& termini) :
+ SensorManager(event, terminusManager, termini) {};
+
+ MOCK_METHOD(void, doSensorPolling, (pldm_tid_t tid), (override));
+};
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/test/numeric_sensor_test.cpp b/platform-mc/test/numeric_sensor_test.cpp
new file mode 100644
index 0000000..8b59401
--- /dev/null
+++ b/platform-mc/test/numeric_sensor_test.cpp
@@ -0,0 +1,272 @@
+
+#include "libpldm/entity.h"
+#include "libpldm/platform.h"
+
+#include "platform-mc/numeric_sensor.hpp"
+#include "platform-mc/terminus.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(NumericSensor, conversionFormula)
+{
+ std::vector<uint8_t> pdr1{
+ 0x1,
+ 0x0,
+ 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_NUMERIC_SENSOR_PDR, // PDRType
+ 0x0,
+ 0x0, // recordChangeNumber
+ PLDM_PDR_NUMERIC_SENSOR_PDR_FIXED_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_SENSOR_DATA_SIZE_MIN_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_RANGE_FIELD_MIN_LENGTH,
+ 0, // dataLength
+ 0,
+ 0, // PLDMTerminusHandle
+ 0x1,
+ 0x0, // sensorID=1
+ PLDM_ENTITY_POWER_SUPPLY,
+ 0, // entityType=Power Supply(120)
+ 1,
+ 0, // entityInstanceNumber
+ 0x1,
+ 0x0, // containerID=1
+ PLDM_NO_INIT, // sensorInit
+ false, // sensorAuxiliaryNamesPDR
+ PLDM_SENSOR_UNIT_DEGRESS_C, // baseUint(2)=degrees C
+ 1, // unitModifier = 1
+ 0, // rateUnit
+ 0, // baseOEMUnitHandle
+ 0, // auxUnit
+ 0, // auxUnitModifier
+ 0, // auxRateUnit
+ 0, // rel
+ 0, // auxOEMUnitHandle
+ true, // isLinear
+ PLDM_RANGE_FIELD_FORMAT_SINT8, // sensorDataSize
+ 0,
+ 0,
+ 0xc0,
+ 0x3f, // resolution=1.5
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // offset=1.0
+ 0,
+ 0, // accuracy
+ 0, // plusTolerance
+ 0, // minusTolerance
+ 2, // hysteresis
+ 0, // supportedThresholds
+ 0, // thresholdAndHysteresisVolatility
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // stateTransistionInterval=1.0
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // updateInverval=1.0
+ 255, // maxReadable
+ 0, // minReadable
+ PLDM_RANGE_FIELD_FORMAT_UINT8, // rangeFieldFormat
+ 0, // rangeFieldsupport
+ 0, // nominalValue
+ 0, // normalMax
+ 0, // normalMin
+ 0, // warningHigh
+ 0, // warningLow
+ 0, // criticalHigh
+ 0, // criticalLow
+ 0, // fatalHigh
+ 0 // fatalLow
+ };
+
+ auto numericSensorPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
+ std::printf("pdr size=%ld\n", pdr1.size());
+ auto rc = decode_numeric_sensor_pdr_data(pdr1.data(), pdr1.size(),
+ numericSensorPdr.get());
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::string sensorName{"test1"};
+ std::string inventoryPath{
+ "/xyz/openbmc_project/inventroy/Item/Board/PLDM_device_1"};
+ pldm::platform_mc::NumericSensor sensor(0x01, true, numericSensorPdr,
+ sensorName, inventoryPath);
+ double reading = 40.0;
+ double convertedValue = 0;
+ convertedValue = sensor.conversionFormula(reading);
+ convertedValue = sensor.unitModifier(convertedValue);
+
+ // (40*1.5 + 1.0 ) * 10^1 = 610
+ EXPECT_EQ(610, convertedValue);
+}
+
+TEST(NumericSensor, checkThreshold)
+{
+ std::vector<uint8_t> pdr1{
+ 0x1,
+ 0x0,
+ 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_NUMERIC_SENSOR_PDR, // PDRType
+ 0x0,
+ 0x0, // recordChangeNumber
+ PLDM_PDR_NUMERIC_SENSOR_PDR_FIXED_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_SENSOR_DATA_SIZE_MIN_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_RANGE_FIELD_MIN_LENGTH,
+ 0, // dataLength
+ 0,
+ 0, // PLDMTerminusHandle
+ 0x1,
+ 0x0, // sensorID=1
+ PLDM_ENTITY_POWER_SUPPLY,
+ 0, // entityType=Power Supply(120)
+ 1,
+ 0, // entityInstanceNumber
+ 0x1,
+ 0x0, // containerID=1
+ PLDM_NO_INIT, // sensorInit
+ false, // sensorAuxiliaryNamesPDR
+ PLDM_SENSOR_UNIT_DEGRESS_C, // baseUint(2)=degrees C
+ 1, // unitModifier = 1
+ 0, // rateUnit
+ 0, // baseOEMUnitHandle
+ 0, // auxUnit
+ 0, // auxUnitModifier
+ 0, // auxRateUnit
+ 0, // rel
+ 0, // auxOEMUnitHandle
+ true, // isLinear
+ PLDM_RANGE_FIELD_FORMAT_SINT8, // sensorDataSize
+ 0,
+ 0,
+ 0xc0,
+ 0x3f, // resolution=1.5
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // offset=1.0
+ 0,
+ 0, // accuracy
+ 0, // plusTolerance
+ 0, // minusTolerance
+ 2, // hysteresis
+ 0, // supportedThresholds
+ 0, // thresholdAndHysteresisVolatility
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // stateTransistionInterval=1.0
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // updateInverval=1.0
+ 255, // maxReadable
+ 0, // minReadable
+ PLDM_RANGE_FIELD_FORMAT_UINT8, // rangeFieldFormat
+ 0, // rangeFieldsupport
+ 0, // nominalValue
+ 0, // normalMax
+ 0, // normalMin
+ 0, // warningHigh
+ 0, // warningLow
+ 0, // criticalHigh
+ 0, // criticalLow
+ 0, // fatalHigh
+ 0 // fatalLow
+ };
+
+ auto numericSensorPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
+ auto rc = decode_numeric_sensor_pdr_data(pdr1.data(), pdr1.size(),
+ numericSensorPdr.get());
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ std::string sensorName{"test1"};
+ std::string inventoryPath{
+ "/xyz/openbmc_project/inventroy/Item/Board/PLDM_device_1"};
+ pldm::platform_mc::NumericSensor sensor(0x01, true, numericSensorPdr,
+ sensorName, inventoryPath);
+
+ bool highAlarm = false;
+ bool lowAlarm = false;
+ double highThreshold = 40;
+ double lowThreshold = 30;
+ double hysteresis = 2;
+
+ // reading 35->40->45->38->35->30->25->32->35
+ // highAlarm F->T ->T ->T ->F ->F ->F -> F-> F
+ // lowAlarm F->F ->F ->F ->F ->T ->T -> T ->F
+ double reading = 35;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(false, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(false, lowAlarm);
+
+ reading = 40;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(true, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(false, lowAlarm);
+
+ reading = 45;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(true, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(false, lowAlarm);
+
+ reading = 38;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(true, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(false, lowAlarm);
+
+ reading = 35;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(false, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(false, lowAlarm);
+
+ reading = 30;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(false, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(true, lowAlarm);
+
+ reading = 25;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(false, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(true, lowAlarm);
+
+ reading = 32;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(false, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(true, lowAlarm);
+
+ reading = 35;
+ highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+ hysteresis);
+ EXPECT_EQ(false, highAlarm);
+ lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+ hysteresis);
+ EXPECT_EQ(false, lowAlarm);
+}
diff --git a/platform-mc/test/platform_manager_test.cpp b/platform-mc/test/platform_manager_test.cpp
index 8afad35..73baa1a 100644
--- a/platform-mc/test/platform_manager_test.cpp
+++ b/platform-mc/test/platform_manager_test.cpp
@@ -63,7 +63,7 @@
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // updateTime
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // OEMUpdateTime
- 1, 0x0, 0x0, 0x0, // recordCount
+ 2, 0x0, 0x0, 0x0, // recordCount
0x0, 0x1, 0x0, 0x0, // repositorySize
59, 0x0, 0x0, 0x0, // largestRecordSize
0x0 // dataTransferHandleTimeout
@@ -76,12 +76,12 @@
// queue getPDR responses
const size_t getPdrRespLen = 81;
std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrRespLen> getPdrResp{
- 0x0, 0x02, 0x51, PLDM_SUCCESS, 0x0, 0x0, 0x0, 0x0, // nextRecordHandle
+ 0x0, 0x02, 0x51, PLDM_SUCCESS, 0x1, 0x0, 0x0, 0x0, // nextRecordHandle
0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
0x5, // transferFlag
69, 0x0, // responseCount
// numeric Sensor PDR
- 0x1, 0x0, 0x0,
+ 0x0, 0x0, 0x0,
0x0, // record handle
0x1, // PDRHeaderVersion
PLDM_NUMERIC_SENSOR_PDR, // PDRType
@@ -147,9 +147,47 @@
reinterpret_cast<pldm_msg*>(getPdrResp.data()), sizeof(getPdrResp));
EXPECT_EQ(rc, PLDM_SUCCESS);
+ const size_t getPdrAuxNameRespLen = 39;
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrAuxNameRespLen>
+ getPdrAuxNameResp{
+ 0x0, 0x02, 0x51, PLDM_SUCCESS, 0x0, 0x0, 0x0,
+ 0x0, // nextRecordHandle
+ 0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
+ 0x5, // transferFlag
+ 0x1b, 0x0, // responseCount
+ // Common PDR Header
+ 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"
+ };
+ rc = mockTerminusManager.enqueueResponse(
+ reinterpret_cast<pldm_msg*>(getPdrAuxNameResp.data()),
+ sizeof(getPdrAuxNameResp));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
stdexec::sync_wait(platformManager.initTerminus());
EXPECT_EQ(true, terminus->initialized);
- EXPECT_EQ(1, terminus->pdrs.size());
+ EXPECT_EQ(2, terminus->pdrs.size());
+ EXPECT_EQ(1, terminus->numericSensors.size());
+ EXPECT_EQ("S0", terminus->getTerminusName());
}
TEST_F(PlatformManagerTest, parseTerminusNameTest)
@@ -440,6 +478,7 @@
stdexec::sync_wait(platformManager.initTerminus());
EXPECT_EQ(true, terminus->initialized);
EXPECT_EQ(0, terminus->pdrs.size());
+ EXPECT_EQ(0, terminus->numericSensors.size());
}
TEST_F(PlatformManagerTest, negativeInitTerminusTest2)
@@ -471,4 +510,5 @@
stdexec::sync_wait(platformManager.initTerminus());
EXPECT_EQ(true, terminus->initialized);
EXPECT_EQ(0, terminus->pdrs.size());
+ EXPECT_EQ(0, terminus->numericSensors.size());
}
diff --git a/platform-mc/test/sensor_manager_test.cpp b/platform-mc/test/sensor_manager_test.cpp
new file mode 100644
index 0000000..03cb7fd
--- /dev/null
+++ b/platform-mc/test/sensor_manager_test.cpp
@@ -0,0 +1,172 @@
+#include "common/instance_id.hpp"
+#include "common/types.hpp"
+#include "mock_sensor_manager.hpp"
+#include "platform-mc/terminus_manager.hpp"
+#include "test/test_instance_id.hpp"
+
+#include <sdeventplus/event.hpp>
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Between;
+using ::testing::Return;
+
+class SensorManagerTest : public testing::Test
+{
+ protected:
+ SensorManagerTest() :
+ bus(pldm::utils::DBusHandler::getBus()),
+ event(sdeventplus::Event::get_default()), instanceIdDb(),
+ reqHandler(pldmTransport, event, instanceIdDb, false),
+ terminusManager(event, reqHandler, instanceIdDb, termini, nullptr),
+ sensorManager(event, terminusManager, termini)
+ {}
+
+ void runEventLoopForSeconds(uint64_t sec)
+ {
+ uint64_t t0 = 0;
+ uint64_t t1 = 0;
+ uint64_t usec = sec * 1000000;
+ uint64_t elapsed = 0;
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t0);
+ do
+ {
+ if (!sd_event_run(event.get(), usec - elapsed))
+ {
+ break;
+ }
+ sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+ elapsed = t1 - t0;
+ } while (elapsed < usec);
+ }
+
+ PldmTransport* pldmTransport = nullptr;
+ sdbusplus::bus::bus& bus;
+ sdeventplus::Event event;
+ TestInstanceIdDb instanceIdDb;
+ pldm::requester::Handler<pldm::requester::Request> reqHandler;
+ pldm::platform_mc::TerminusManager terminusManager;
+ pldm::platform_mc::MockSensorManager sensorManager;
+ std::map<pldm_tid_t, std::shared_ptr<pldm::platform_mc::Terminus>> termini;
+
+ std::vector<uint8_t> pdr1{
+ 0x1,
+ 0x0,
+ 0x0,
+ 0x0, // record handle
+ 0x1, // PDRHeaderVersion
+ PLDM_NUMERIC_SENSOR_PDR, // PDRType
+ 0x0,
+ 0x0, // recordChangeNumber
+ PLDM_PDR_NUMERIC_SENSOR_PDR_FIXED_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_SENSOR_DATA_SIZE_MIN_LENGTH +
+ PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_RANGE_FIELD_MIN_LENGTH,
+ 0, // dataLength
+ 0,
+ 0, // PLDMTerminusHandle
+ 0x1,
+ 0x0, // sensorID=1
+ PLDM_ENTITY_POWER_SUPPLY,
+ 0, // entityType=Power Supply(120)
+ 1,
+ 0, // entityInstanceNumber
+ 0x1,
+ 0x0, // containerID=1
+ PLDM_NO_INIT, // sensorInit
+ false, // sensorAuxiliaryNamesPDR
+ PLDM_SENSOR_UNIT_DEGRESS_C, // baseUint(2)=degrees C
+ 1, // unitModifier = 1
+ 0, // rateUnit
+ 0, // baseOEMUnitHandle
+ 0, // auxUnit
+ 0, // auxUnitModifier
+ 0, // auxRateUnit
+ 0, // rel
+ 0, // auxOEMUnitHandle
+ true, // isLinear
+ PLDM_RANGE_FIELD_FORMAT_SINT8, // sensorDataSize
+ 0,
+ 0,
+ 0xc0,
+ 0x3f, // resolution=1.5
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // offset=1.0
+ 0,
+ 0, // accuracy
+ 0, // plusTolerance
+ 0, // minusTolerance
+ 2, // hysteresis
+ 0, // supportedThresholds
+ 0, // thresholdAndHysteresisVolatility
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // stateTransistionInterval=1.0
+ 0,
+ 0,
+ 0x80,
+ 0x3f, // updateInverval=1.0
+ 255, // maxReadable
+ 0, // minReadable
+ PLDM_RANGE_FIELD_FORMAT_UINT8, // rangeFieldFormat
+ 0, // rangeFieldsupport
+ 0, // nominalValue
+ 0, // normalMax
+ 0, // normalMin
+ 0, // warningHigh
+ 0, // warningLow
+ 0, // criticalHigh
+ 0, // criticalLow
+ 0, // fatalHigh
+ 0 // fatalLow
+ };
+
+ 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"
+ };
+};
+
+TEST_F(SensorManagerTest, sensorPollingTest)
+{
+ uint64_t seconds = 10;
+ uint64_t expectedTimes = (seconds * 1000) / SENSOR_POLLING_TIME;
+
+ pldm_tid_t tid = 1;
+ termini[tid] = std::make_shared<pldm::platform_mc::Terminus>(tid, 0);
+ termini[tid]->pdrs.push_back(pdr1);
+ termini[tid]->pdrs.push_back(pdr2);
+ termini[tid]->parseTerminusPDRs();
+
+ EXPECT_CALL(sensorManager, doSensorPolling(tid))
+ .Times(Between(expectedTimes - 3, expectedTimes + 3))
+ .WillRepeatedly(Return());
+
+ sensorManager.startPolling(tid);
+
+ runEventLoopForSeconds(seconds);
+
+ sensorManager.stopPolling(tid);
+}
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index 13d28f2..8637a15 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -196,6 +196,8 @@
auto& bus = pldm::utils::DBusHandler::getBus();
sdbusplus::server::manager_t objManager(bus,
"/xyz/openbmc_project/software");
+ sdbusplus::server::manager_t sensorObjManager(
+ bus, "/xyz/openbmc_project/sensors");
InstanceIdDb instanceIdDb;
dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm",