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/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