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