blob: fcfd0a61e4005a9ffbf711a90d04c3771fec614f [file] [log] [blame]
Gilbert Cheneac61a42022-02-23 20:56:19 +00001#include "sensor_manager.hpp"
2
3#include "terminus_manager.hpp"
4
5#include <phosphor-logging/lg2.hpp>
6
7#include <exception>
8
9namespace pldm
10{
11namespace platform_mc
12{
13
14SensorManager::SensorManager(sdeventplus::Event& event,
15 TerminusManager& terminusManager,
16 TerminiMapper& termini) :
17 event(event), terminusManager(terminusManager), termini(termini),
18 pollingTime(SENSOR_POLLING_TIME)
19{}
20
21void 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
73void 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
94void 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
145exec::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
237exec::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