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 
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 
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 
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 
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 
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         sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
185         auto toBeUpdated = roundRobinSensors[tid].size();
186         while (((t1 - t0) < pollingTimeInUsec) && (toBeUpdated > 0))
187         {
188             if (!getAvailableState(tid))
189             {
190                 lg2::info(
191                     "Terminus ID {TID} is not available for PLDM request from {NOW}.",
192                     "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
193                 co_await stdexec::just_stopped();
194             }
195 
196             auto sensor = roundRobinSensors[tid].front();
197 
198             sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
199             elapsed = t1 - sensor->timeStamp;
200             if ((sensor->updateTime <= elapsed) || (!sensor->timeStamp))
201             {
202                 rc = co_await getSensorReading(sensor);
203 
204                 if ((!sensorPollTimers.contains(tid)) ||
205                     (sensorPollTimers[tid] &&
206                      !sensorPollTimers[tid]->isRunning()))
207                 {
208                     co_return PLDM_ERROR;
209                 }
210                 sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
211                 if (rc == PLDM_SUCCESS)
212                 {
213                     sensor->timeStamp = t1;
214                 }
215                 else
216                 {
217                     lg2::error(
218                         "Failed to get sensor value for terminus {TID}, error: {RC}",
219                         "TID", tid, "RC", rc);
220                 }
221             }
222 
223             toBeUpdated--;
224             if (roundRobinSensors.contains(tid))
225             {
226                 roundRobinSensors[tid].pop();
227                 roundRobinSensors[tid].push(std::move(sensor));
228             }
229             sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
230         }
231 
232         sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
233     } while ((t1 - t0) >= pollingTimeInUsec);
234 
235     co_return PLDM_SUCCESS;
236 }
237 
238 exec::task<int>
239     SensorManager::getSensorReading(std::shared_ptr<NumericSensor> sensor)
240 {
241     if (!sensor)
242     {
243         lg2::error("Call `getSensorReading` with null `sensor` pointer.");
244         co_return PLDM_ERROR_INVALID_DATA;
245     }
246 
247     auto tid = sensor->tid;
248     auto sensorId = sensor->sensorId;
249     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_SENSOR_READING_REQ_BYTES);
250     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
251     auto rc = encode_get_sensor_reading_req(0, sensorId, false, requestMsg);
252     if (rc)
253     {
254         lg2::error(
255             "Failed to encode request GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.",
256             "TID", tid, "ID", sensorId, "RC", rc);
257         co_return rc;
258     }
259 
260     if (!getAvailableState(tid))
261     {
262         lg2::info(
263             "Terminus ID {TID} is not available for PLDM request from {NOW}.",
264             "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
265         co_await stdexec::just_stopped();
266     }
267 
268     const pldm_msg* responseMsg = nullptr;
269     size_t responseLen = 0;
270     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
271                                                   &responseLen);
272     if (rc)
273     {
274         lg2::error(
275             "Failed to send GetSensorReading message for terminus {TID}, sensor Id {ID}, error {RC}",
276             "TID", tid, "ID", sensorId, "RC", rc);
277         co_return rc;
278     }
279 
280     if ((!sensorPollTimers.contains(tid)) ||
281         (sensorPollTimers[tid] && !sensorPollTimers[tid]->isRunning()))
282     {
283         co_return PLDM_ERROR;
284     }
285 
286     uint8_t completionCode = PLDM_SUCCESS;
287     uint8_t sensorDataSize = PLDM_SENSOR_DATA_SIZE_SINT32;
288     uint8_t sensorOperationalState = 0;
289     uint8_t sensorEventMessageEnable = 0;
290     uint8_t presentState = 0;
291     uint8_t previousState = 0;
292     uint8_t eventState = 0;
293     union_sensor_data_size presentReading;
294     rc = decode_get_sensor_reading_resp(
295         responseMsg, responseLen, &completionCode, &sensorDataSize,
296         &sensorOperationalState, &sensorEventMessageEnable, &presentState,
297         &previousState, &eventState,
298         reinterpret_cast<uint8_t*>(&presentReading));
299     if (rc)
300     {
301         lg2::error(
302             "Failed to decode response GetSensorReading for terminus ID {TID}, sensor Id {ID}, error {RC}.",
303             "TID", tid, "ID", sensorId, "RC", rc);
304         sensor->handleErrGetSensorReading();
305         co_return rc;
306     }
307 
308     if (completionCode != PLDM_SUCCESS)
309     {
310         lg2::error(
311             "Error : GetSensorReading for terminus ID {TID}, sensor Id {ID}, complete code {CC}.",
312             "TID", tid, "ID", sensorId, "CC", completionCode);
313         co_return completionCode;
314     }
315 
316     double value = std::numeric_limits<double>::quiet_NaN();
317     switch (sensorOperationalState)
318     {
319         case PLDM_SENSOR_ENABLED:
320             break;
321         case PLDM_SENSOR_DISABLED:
322             sensor->updateReading(false, true, value);
323             co_return completionCode;
324         case PLDM_SENSOR_FAILED:
325             sensor->updateReading(true, false, value);
326             co_return completionCode;
327         case PLDM_SENSOR_UNAVAILABLE:
328         default:
329             sensor->updateReading(false, false, value);
330             co_return completionCode;
331     }
332 
333     switch (sensorDataSize)
334     {
335         case PLDM_SENSOR_DATA_SIZE_UINT8:
336             value = static_cast<double>(presentReading.value_u8);
337             break;
338         case PLDM_SENSOR_DATA_SIZE_SINT8:
339             value = static_cast<double>(presentReading.value_s8);
340             break;
341         case PLDM_SENSOR_DATA_SIZE_UINT16:
342             value = static_cast<double>(presentReading.value_u16);
343             break;
344         case PLDM_SENSOR_DATA_SIZE_SINT16:
345             value = static_cast<double>(presentReading.value_s16);
346             break;
347         case PLDM_SENSOR_DATA_SIZE_UINT32:
348             value = static_cast<double>(presentReading.value_u32);
349             break;
350         case PLDM_SENSOR_DATA_SIZE_SINT32:
351             value = static_cast<double>(presentReading.value_s32);
352             break;
353         default:
354             value = std::numeric_limits<double>::quiet_NaN();
355             break;
356     }
357 
358     sensor->updateReading(true, true, value);
359     co_return completionCode;
360 }
361 
362 } // namespace platform_mc
363 } // namespace pldm
364