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