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