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