blob: fcfd0a61e4005a9ffbf711a90d04c3771fec614f [file] [log] [blame]
#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