1 #include "sensor_manager.hpp" 2 3 #include "terminus_manager.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 7 #include <exception> 8 9 namespace pldm 10 { 11 namespace platform_mc 12 { 13 14 SensorManager::SensorManager(sdeventplus::Event& event, 15 TerminusManager& terminusManager, 16 TerminiMapper& termini) : 17 event(event), terminusManager(terminusManager), termini(termini), 18 pollingTime(SENSOR_POLLING_TIME) 19 {} 20 21 void SensorManager::startPolling(pldm_tid_t tid) 22 { 23 if (!termini.contains(tid)) 24 { 25 return; 26 } 27 28 /* tid already initializes roundRobinSensors list */ 29 if (sensorPollTimers.contains(tid)) 30 { 31 lg2::info("Terminus ID {TID}: sensor poll timer already exists.", "TID", 32 tid); 33 return; 34 } 35 // numeric sensor 36 auto terminus = termini[tid]; 37 for (auto& sensor : terminus->numericSensors) 38 { 39 roundRobinSensors[tid].push(sensor); 40 } 41 42 updateAvailableState(tid, true); 43 44 if (!roundRobinSensors[tid].size()) 45 { 46 lg2::info("Terminus ID {TID}: no sensors to poll.", "TID", tid); 47 return; 48 } 49 50 sensorPollTimers[tid] = std::make_unique<sdbusplus::Timer>( 51 event.get(), 52 std::bind_front(&SensorManager::doSensorPolling, this, tid)); 53 54 try 55 { 56 if (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()) 57 { 58 sensorPollTimers[tid]->start( 59 duration_cast<std::chrono::milliseconds>( 60 std::chrono::milliseconds(pollingTime)), 61 true); 62 } 63 } 64 catch (const std::exception& e) 65 { 66 lg2::error( 67 "Terminus ID {TID}: Failed to start sensor polling timer. Exception: {EXCEPTION}", 68 "TID", tid, "EXCEPTION", e); 69 return; 70 } 71 } 72 73 void SensorManager::stopPolling(pldm_tid_t tid) 74 { 75 /* Stop polling timer */ 76 if (sensorPollTimers.contains(tid)) 77 { 78 sensorPollTimers[tid]->stop(); 79 sensorPollTimers.erase(tid); 80 } 81 82 roundRobinSensors.erase(tid); 83 84 if (doSensorPollingTaskHandles.contains(tid)) 85 { 86 auto& [scope, rcOpt] = doSensorPollingTaskHandles[tid]; 87 scope.request_stop(); 88 doSensorPollingTaskHandles.erase(tid); 89 } 90 91 availableState.erase(tid); 92 } 93 94 void SensorManager::doSensorPolling(pldm_tid_t tid) 95 { 96 auto it = doSensorPollingTaskHandles.find(tid); 97 if (it != doSensorPollingTaskHandles.end()) 98 { 99 auto& [scope, rcOpt] = it->second; 100 if (!rcOpt.has_value()) 101 { 102 return; 103 } 104 doSensorPollingTaskHandles.erase(tid); 105 } 106 107 auto& [scope, rcOpt] = 108 doSensorPollingTaskHandles 109 .emplace(std::piecewise_construct, std::forward_as_tuple(tid), 110 std::forward_as_tuple()) 111 .first->second; 112 scope.spawn( 113 stdexec::just() | stdexec::let_value([this, &rcOpt, 114 tid] -> exec::task<void> { 115 auto res = 116 co_await stdexec::stopped_as_optional(doSensorPollingTask(tid)); 117 if (res.has_value()) 118 { 119 rcOpt = *res; 120 } 121 else 122 { 123 lg2::info("Stopped polling for Terminus ID {TID}", "TID", tid); 124 try 125 { 126 if (sensorPollTimers.contains(tid) && 127 sensorPollTimers[tid] && 128 sensorPollTimers[tid]->isRunning()) 129 { 130 sensorPollTimers[tid]->stop(); 131 } 132 } 133 catch (const std::exception& e) 134 { 135 lg2::error( 136 "Terminus ID {TID}: Failed to stop polling timer. Exception: {EXCEPTION}", 137 "TID", tid, "EXCEPTION", e); 138 } 139 rcOpt = PLDM_SUCCESS; 140 } 141 }), 142 exec::default_task_context<void>(exec::inline_scheduler{})); 143 } 144 145 exec::task<int> SensorManager::doSensorPollingTask(pldm_tid_t tid) 146 { 147 uint64_t t0 = 0; 148 uint64_t t1 = 0; 149 uint64_t elapsed = 0; 150 uint64_t pollingTimeInUsec = pollingTime * 1000; 151 uint8_t rc = PLDM_SUCCESS; 152 153 do 154 { 155 if ((!sensorPollTimers.contains(tid)) || 156 (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning())) 157 { 158 co_return PLDM_ERROR; 159 } 160 161 sd_event_now(event.get(), CLOCK_MONOTONIC, &t0); 162 163 /** 164 * Terminus is not available for PLDM request. 165 * The terminus manager will trigger recovery process to recovery the 166 * communication between the local terminus and the remote terminus. 167 * The sensor polling should be stopped while recovering the 168 * communication. 169 */ 170 if (!getAvailableState(tid)) 171 { 172 lg2::info( 173 "Terminus ID {TID} is not available for PLDM request from {NOW}.", 174 "TID", tid, "NOW", pldm::utils::getCurrentSystemTime()); 175 co_await stdexec::just_stopped(); 176 } 177 178 if (!termini.contains(tid)) 179 { 180 co_return PLDM_SUCCESS; 181 } 182 183 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1); 184 auto toBeUpdated = roundRobinSensors[tid].size(); 185 while (((t1 - t0) < pollingTimeInUsec) && (toBeUpdated > 0)) 186 { 187 if (!getAvailableState(tid)) 188 { 189 lg2::info( 190 "Terminus ID {TID} is not available for PLDM request from {NOW}.", 191 "TID", tid, "NOW", pldm::utils::getCurrentSystemTime()); 192 co_await stdexec::just_stopped(); 193 } 194 195 auto sensor = roundRobinSensors[tid].front(); 196 197 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1); 198 elapsed = t1 - sensor->timeStamp; 199 if ((sensor->updateTime <= elapsed) || (!sensor->timeStamp)) 200 { 201 rc = co_await getSensorReading(sensor); 202 203 if ((!sensorPollTimers.contains(tid)) || 204 (sensorPollTimers[tid] && 205 !sensorPollTimers[tid]->isRunning())) 206 { 207 co_return PLDM_ERROR; 208 } 209 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1); 210 if (rc == PLDM_SUCCESS) 211 { 212 sensor->timeStamp = t1; 213 } 214 else 215 { 216 lg2::error( 217 "Failed to get sensor value for terminus {TID}, error: {RC}", 218 "TID", tid, "RC", rc); 219 } 220 } 221 222 toBeUpdated--; 223 if (roundRobinSensors.contains(tid)) 224 { 225 roundRobinSensors[tid].pop(); 226 roundRobinSensors[tid].push(std::move(sensor)); 227 } 228 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1); 229 } 230 231 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1); 232 } while ((t1 - t0) >= pollingTimeInUsec); 233 234 co_return PLDM_SUCCESS; 235 } 236 237 exec::task<int> 238 SensorManager::getSensorReading(std::shared_ptr<NumericSensor> sensor) 239 { 240 if (!sensor) 241 { 242 lg2::error("Call `getSensorReading` with null `sensor` pointer."); 243 co_return PLDM_ERROR_INVALID_DATA; 244 } 245 246 auto tid = sensor->tid; 247 auto sensorId = sensor->sensorId; 248 Request request(sizeof(pldm_msg_hdr) + PLDM_GET_SENSOR_READING_REQ_BYTES); 249 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 250 auto rc = encode_get_sensor_reading_req(0, sensorId, false, requestMsg); 251 if (rc) 252 { 253 lg2::error( 254 "Failed to encode request GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.", 255 "TID", tid, "ID", sensorId, "RC", rc); 256 co_return rc; 257 } 258 259 if (!getAvailableState(tid)) 260 { 261 lg2::info( 262 "Terminus ID {TID} is not available for PLDM request from {NOW}.", 263 "TID", tid, "NOW", pldm::utils::getCurrentSystemTime()); 264 co_await stdexec::just_stopped(); 265 } 266 267 const pldm_msg* responseMsg = nullptr; 268 size_t responseLen = 0; 269 rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg, 270 &responseLen); 271 if (rc) 272 { 273 lg2::error( 274 "Failed to send GetSensorReading message for terminus {TID}, sensor Id {ID}, error {RC}", 275 "TID", tid, "ID", sensorId, "RC", rc); 276 co_return rc; 277 } 278 279 if ((!sensorPollTimers.contains(tid)) || 280 (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning())) 281 { 282 co_return PLDM_ERROR; 283 } 284 285 uint8_t completionCode = PLDM_SUCCESS; 286 uint8_t sensorDataSize = PLDM_SENSOR_DATA_SIZE_SINT32; 287 uint8_t sensorOperationalState = 0; 288 uint8_t sensorEventMessageEnable = 0; 289 uint8_t presentState = 0; 290 uint8_t previousState = 0; 291 uint8_t eventState = 0; 292 union_sensor_data_size presentReading; 293 rc = decode_get_sensor_reading_resp( 294 responseMsg, responseLen, &completionCode, &sensorDataSize, 295 &sensorOperationalState, &sensorEventMessageEnable, &presentState, 296 &previousState, &eventState, 297 reinterpret_cast<uint8_t*>(&presentReading)); 298 if (rc) 299 { 300 lg2::error( 301 "Failed to decode response GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.", 302 "TID", tid, "ID", sensorId, "RC", rc); 303 sensor->handleErrGetSensorReading(); 304 co_return rc; 305 } 306 307 if (completionCode != PLDM_SUCCESS) 308 { 309 lg2::error( 310 "Error : GetSensorReading for terminus ID {TID}, sensor Id {ID}, complete code {CC}.", 311 "TID", tid, "ID", sensorId, "CC", completionCode); 312 co_return completionCode; 313 } 314 315 double value = std::numeric_limits<double>::quiet_NaN(); 316 switch (sensorOperationalState) 317 { 318 case PLDM_SENSOR_ENABLED: 319 break; 320 case PLDM_SENSOR_DISABLED: 321 sensor->updateReading(true, false, value); 322 co_return completionCode; 323 case PLDM_SENSOR_FAILED: 324 sensor->updateReading(false, true, value); 325 co_return completionCode; 326 case PLDM_SENSOR_UNAVAILABLE: 327 default: 328 sensor->updateReading(false, false, value); 329 co_return completionCode; 330 } 331 332 switch (sensorDataSize) 333 { 334 case PLDM_SENSOR_DATA_SIZE_UINT8: 335 value = static_cast<double>(presentReading.value_u8); 336 break; 337 case PLDM_SENSOR_DATA_SIZE_SINT8: 338 value = static_cast<double>(presentReading.value_s8); 339 break; 340 case PLDM_SENSOR_DATA_SIZE_UINT16: 341 value = static_cast<double>(presentReading.value_u16); 342 break; 343 case PLDM_SENSOR_DATA_SIZE_SINT16: 344 value = static_cast<double>(presentReading.value_s16); 345 break; 346 case PLDM_SENSOR_DATA_SIZE_UINT32: 347 value = static_cast<double>(presentReading.value_u32); 348 break; 349 case PLDM_SENSOR_DATA_SIZE_SINT32: 350 value = static_cast<double>(presentReading.value_s32); 351 break; 352 default: 353 value = std::numeric_limits<double>::quiet_NaN(); 354 break; 355 } 356 357 sensor->updateReading(true, true, value); 358 co_return completionCode; 359 } 360 361 } // namespace platform_mc 362 } // namespace pldm 363