#include "sensor_manager.hpp" #include "terminus_manager.hpp" #include #include 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( event.get(), std::bind_front(&SensorManager::doSensorPolling, this, tid)); try { if (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()) { sensorPollTimers[tid]->start( duration_cast( 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 { 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(exec::inline_scheduler{})); } exec::task 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 SensorManager::getSensorReading(std::shared_ptr 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(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(&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::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(presentReading.value_u8); break; case PLDM_SENSOR_DATA_SIZE_SINT8: value = static_cast(presentReading.value_s8); break; case PLDM_SENSOR_DATA_SIZE_UINT16: value = static_cast(presentReading.value_u16); break; case PLDM_SENSOR_DATA_SIZE_SINT16: value = static_cast(presentReading.value_s16); break; case PLDM_SENSOR_DATA_SIZE_UINT32: value = static_cast(presentReading.value_u32); break; case PLDM_SENSOR_DATA_SIZE_SINT32: value = static_cast(presentReading.value_s32); break; default: value = std::numeric_limits::quiet_NaN(); break; } sensor->updateReading(true, true, value); co_return completionCode; } } // namespace platform_mc } // namespace pldm