blob: be7ece369ea97f3b2deac2230d8219b9361cb60d [file] [log] [blame]
Thu Nguyen3c5486d2024-08-01 08:03:08 +00001#include "numeric_sensor.hpp"
2
3#include "libpldm/platform.h"
4
5#include "common/utils.hpp"
6#include "requester/handler.hpp"
7
8#include <limits>
9#include <regex>
10
11PHOSPHOR_LOG2_USING;
12
13namespace pldm
14{
15namespace platform_mc
16{
17
18NumericSensor::NumericSensor(const pldm_tid_t tid, const bool sensorDisabled,
19 std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr,
20 std::string& sensorName,
21 std::string& associationPath) :
22 tid(tid),
23 sensorName(sensorName), isPriority(false)
24{
25 if (!pdr)
26 {
27 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
28 }
29
30 sensorId = pdr->sensor_id;
31 std::string path;
32 SensorUnit sensorUnit = SensorUnit::DegreesC;
33
34 switch (pdr->base_unit)
35 {
36 case PLDM_SENSOR_UNIT_DEGRESS_C:
37 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
38 sensorUnit = SensorUnit::DegreesC;
39 break;
40 case PLDM_SENSOR_UNIT_VOLTS:
41 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
42 sensorUnit = SensorUnit::Volts;
43 break;
44 case PLDM_SENSOR_UNIT_AMPS:
45 sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
46 sensorUnit = SensorUnit::Amperes;
47 break;
48 case PLDM_SENSOR_UNIT_RPM:
49 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
50 sensorUnit = SensorUnit::RPMS;
51 break;
52 case PLDM_SENSOR_UNIT_WATTS:
53 sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
54 sensorUnit = SensorUnit::Watts;
55 break;
56 case PLDM_SENSOR_UNIT_JOULES:
57 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
58 sensorUnit = SensorUnit::Joules;
59 break;
60 case PLDM_SENSOR_UNIT_PERCENTAGE:
61 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
62 sensorUnit = SensorUnit::Percent;
63 break;
64 default:
65 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
66 sensorName, "UNIT", pdr->base_unit);
67 throw sdbusplus::xyz::openbmc_project::Common::Error::
68 InvalidArgument();
69 break;
70 }
71
72 path = sensorNameSpace + sensorName;
73 try
74 {
75 auto service = pldm::utils::DBusHandler().getService(
76 path.c_str(), "xyz.openbmc_project.Sensor.Value");
77 if (!service.empty())
78 {
79 throw sdbusplus::xyz::openbmc_project::Common::Error::
80 TooManyResources();
81 }
82 }
83 catch (const std::exception&)
84 {
85 /* The sensor object path is not created */
86 }
87
88 auto& bus = pldm::utils::DBusHandler::getBus();
89 try
90 {
91 associationDefinitionsIntf =
92 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
93 }
94 catch (const sdbusplus::exception_t& e)
95 {
96 lg2::error(
97 "Failed to create association interface for numeric sensor {PATH} error - {ERROR}",
98 "PATH", path, "ERROR", e);
99 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
100 }
101
102 associationDefinitionsIntf->associations(
103 {{"chassis", "all_sensors", associationPath}});
104
105 double maxValue = std::numeric_limits<double>::quiet_NaN();
106 double minValue = std::numeric_limits<double>::quiet_NaN();
107
108 switch (pdr->sensor_data_size)
109 {
110 case PLDM_SENSOR_DATA_SIZE_UINT8:
111 maxValue = pdr->max_readable.value_u8;
112 minValue = pdr->min_readable.value_u8;
113 hysteresis = pdr->hysteresis.value_u8;
114 break;
115 case PLDM_SENSOR_DATA_SIZE_SINT8:
116 maxValue = pdr->max_readable.value_s8;
117 minValue = pdr->min_readable.value_s8;
118 hysteresis = pdr->hysteresis.value_s8;
119 break;
120 case PLDM_SENSOR_DATA_SIZE_UINT16:
121 maxValue = pdr->max_readable.value_u16;
122 minValue = pdr->min_readable.value_u16;
123 hysteresis = pdr->hysteresis.value_u16;
124 break;
125 case PLDM_SENSOR_DATA_SIZE_SINT16:
126 maxValue = pdr->max_readable.value_s16;
127 minValue = pdr->min_readable.value_s16;
128 hysteresis = pdr->hysteresis.value_s16;
129 break;
130 case PLDM_SENSOR_DATA_SIZE_UINT32:
131 maxValue = pdr->max_readable.value_u32;
132 minValue = pdr->min_readable.value_u32;
133 hysteresis = pdr->hysteresis.value_u32;
134 break;
135 case PLDM_SENSOR_DATA_SIZE_SINT32:
136 maxValue = pdr->max_readable.value_s32;
137 minValue = pdr->min_readable.value_s32;
138 hysteresis = pdr->hysteresis.value_s32;
139 break;
140 }
141
142 bool hasCriticalThresholds = false;
143 bool hasWarningThresholds = false;
144 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
145 double criticalLow = std::numeric_limits<double>::quiet_NaN();
146 double warningHigh = std::numeric_limits<double>::quiet_NaN();
147 double warningLow = std::numeric_limits<double>::quiet_NaN();
148
149 if (pdr->supported_thresholds.bits.bit0)
150 {
151 hasWarningThresholds = true;
152 switch (pdr->range_field_format)
153 {
154 case PLDM_RANGE_FIELD_FORMAT_UINT8:
155 warningHigh = pdr->warning_high.value_u8;
156 break;
157 case PLDM_RANGE_FIELD_FORMAT_SINT8:
158 warningHigh = pdr->warning_high.value_s8;
159 break;
160 case PLDM_RANGE_FIELD_FORMAT_UINT16:
161 warningHigh = pdr->warning_high.value_u16;
162 break;
163 case PLDM_RANGE_FIELD_FORMAT_SINT16:
164 warningHigh = pdr->warning_high.value_s16;
165 break;
166 case PLDM_RANGE_FIELD_FORMAT_UINT32:
167 warningHigh = pdr->warning_high.value_u32;
168 break;
169 case PLDM_RANGE_FIELD_FORMAT_SINT32:
170 warningHigh = pdr->warning_high.value_s32;
171 break;
172 case PLDM_RANGE_FIELD_FORMAT_REAL32:
173 warningHigh = pdr->warning_high.value_f32;
174 break;
175 }
176 }
177
178 if (pdr->supported_thresholds.bits.bit3)
179 {
180 hasWarningThresholds = true;
181 switch (pdr->range_field_format)
182 {
183 case PLDM_RANGE_FIELD_FORMAT_UINT8:
184 warningLow = pdr->warning_low.value_u8;
185 break;
186 case PLDM_RANGE_FIELD_FORMAT_SINT8:
187 warningLow = pdr->warning_low.value_s8;
188 break;
189 case PLDM_RANGE_FIELD_FORMAT_UINT16:
190 warningLow = pdr->warning_low.value_u16;
191 break;
192 case PLDM_RANGE_FIELD_FORMAT_SINT16:
193 warningLow = pdr->warning_low.value_s16;
194 break;
195 case PLDM_RANGE_FIELD_FORMAT_UINT32:
196 warningLow = pdr->warning_low.value_u32;
197 break;
198 case PLDM_RANGE_FIELD_FORMAT_SINT32:
199 warningLow = pdr->warning_low.value_s32;
200 break;
201 case PLDM_RANGE_FIELD_FORMAT_REAL32:
202 warningLow = pdr->warning_low.value_f32;
203 break;
204 }
205 }
206
207 if (pdr->supported_thresholds.bits.bit1)
208 {
209 hasCriticalThresholds = true;
210 switch (pdr->range_field_format)
211 {
212 case PLDM_RANGE_FIELD_FORMAT_UINT8:
213 criticalHigh = pdr->critical_high.value_u8;
214 break;
215 case PLDM_RANGE_FIELD_FORMAT_SINT8:
216 criticalHigh = pdr->critical_high.value_s8;
217 break;
218 case PLDM_RANGE_FIELD_FORMAT_UINT16:
219 criticalHigh = pdr->critical_high.value_u16;
220 break;
221 case PLDM_RANGE_FIELD_FORMAT_SINT16:
222 criticalHigh = pdr->critical_high.value_s16;
223 break;
224 case PLDM_RANGE_FIELD_FORMAT_UINT32:
225 criticalHigh = pdr->critical_high.value_u32;
226 break;
227 case PLDM_RANGE_FIELD_FORMAT_SINT32:
228 criticalHigh = pdr->critical_high.value_s32;
229 break;
230 case PLDM_RANGE_FIELD_FORMAT_REAL32:
231 criticalHigh = pdr->critical_high.value_f32;
232 break;
233 }
234 }
235
236 if (pdr->supported_thresholds.bits.bit4)
237 {
238 hasCriticalThresholds = true;
239 switch (pdr->range_field_format)
240 {
241 case PLDM_RANGE_FIELD_FORMAT_UINT8:
242 criticalLow = pdr->critical_low.value_u8;
243 break;
244 case PLDM_RANGE_FIELD_FORMAT_SINT8:
245 criticalLow = pdr->critical_low.value_s8;
246 break;
247 case PLDM_RANGE_FIELD_FORMAT_UINT16:
248 criticalLow = pdr->critical_low.value_u16;
249 break;
250 case PLDM_RANGE_FIELD_FORMAT_SINT16:
251 criticalLow = pdr->critical_low.value_s16;
252 break;
253 case PLDM_RANGE_FIELD_FORMAT_UINT32:
254 criticalLow = pdr->critical_low.value_u32;
255 break;
256 case PLDM_RANGE_FIELD_FORMAT_SINT32:
257 criticalLow = pdr->critical_low.value_s32;
258 break;
259 case PLDM_RANGE_FIELD_FORMAT_REAL32:
260 criticalLow = pdr->critical_low.value_f32;
261 break;
262 }
263 }
264
265 resolution = pdr->resolution;
266 offset = pdr->offset;
267 baseUnitModifier = pdr->unit_modifier;
268
269 /**
270 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
271 * updateTime is in microseconds
272 */
273 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
274 if (!std::isnan(pdr->update_interval))
275 {
276 updateTime = pdr->update_interval * 1000000;
277 }
278
279 try
280 {
281 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
282 }
283 catch (const sdbusplus::exception_t& e)
284 {
285 lg2::error(
286 "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}",
287 "PATH", path, "ERROR", e);
288 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
289 }
290 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
291 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
292 hysteresis = unitModifier(conversionFormula(hysteresis));
293 valueIntf->unit(sensorUnit);
294
295 try
296 {
297 availabilityIntf = std::make_unique<AvailabilityIntf>(bus,
298 path.c_str());
299 }
300 catch (const sdbusplus::exception_t& e)
301 {
302 lg2::error(
303 "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}",
304 "PATH", path, "ERROR", e);
305 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
306 }
307 availabilityIntf->available(true);
308
309 try
310 {
311 operationalStatusIntf =
312 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
313 }
314 catch (const sdbusplus::exception_t& e)
315 {
316 lg2::error(
317 "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}",
318 "PATH", path, "ERROR", e);
319 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
320 }
321 operationalStatusIntf->functional(!sensorDisabled);
322
323 if (hasWarningThresholds)
324 {
325 try
326 {
327 thresholdWarningIntf =
328 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
329 }
330 catch (const sdbusplus::exception_t& e)
331 {
332 lg2::error(
333 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}",
334 "PATH", path, "ERROR", e);
335 throw sdbusplus::xyz::openbmc_project::Common::Error::
336 InvalidArgument();
337 }
338 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
339 thresholdWarningIntf->warningLow(unitModifier(warningLow));
340 }
341
342 if (hasCriticalThresholds)
343 {
344 try
345 {
346 thresholdCriticalIntf =
347 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
348 }
349 catch (const sdbusplus::exception_t& e)
350 {
351 lg2::error(
352 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}",
353 "PATH", path, "ERROR", e);
354 throw sdbusplus::xyz::openbmc_project::Common::Error::
355 InvalidArgument();
356 }
357 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
358 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
359 }
360}
361
362NumericSensor::NumericSensor(
363 const pldm_tid_t tid, const bool sensorDisabled,
364 std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
365 std::string& sensorName, std::string& associationPath) :
366 tid(tid),
367 sensorName(sensorName), isPriority(false)
368{
369 if (!pdr)
370 {
371 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
372 }
373
374 sensorId = pdr->sensor_id;
375 std::string path;
376 SensorUnit sensorUnit = SensorUnit::DegreesC;
377
378 switch (pdr->base_unit)
379 {
380 case PLDM_SENSOR_UNIT_DEGRESS_C:
381 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
382 sensorUnit = SensorUnit::DegreesC;
383 break;
384 case PLDM_SENSOR_UNIT_VOLTS:
385 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
386 sensorUnit = SensorUnit::Volts;
387 break;
388 case PLDM_SENSOR_UNIT_AMPS:
389 sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
390 sensorUnit = SensorUnit::Amperes;
391 break;
392 case PLDM_SENSOR_UNIT_RPM:
393 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
394 sensorUnit = SensorUnit::RPMS;
395 break;
396 case PLDM_SENSOR_UNIT_WATTS:
397 sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
398 sensorUnit = SensorUnit::Watts;
399 break;
400 case PLDM_SENSOR_UNIT_JOULES:
401 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
402 sensorUnit = SensorUnit::Joules;
403 break;
404 case PLDM_SENSOR_UNIT_PERCENTAGE:
405 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
406 sensorUnit = SensorUnit::Percent;
407 break;
408 default:
409 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
410 sensorName, "UNIT", pdr->base_unit);
411 throw sdbusplus::xyz::openbmc_project::Common::Error::
412 InvalidArgument();
413 break;
414 }
415
416 path = sensorNameSpace + sensorName;
417 try
418 {
419 auto service = pldm::utils::DBusHandler().getService(
420 path.c_str(), "xyz.openbmc_project.Sensor.Value");
421 if (!service.empty())
422 {
423 throw sdbusplus::xyz::openbmc_project::Common::Error::
424 TooManyResources();
425 }
426 }
427 catch (const std::exception&)
428 {
429 /* The sensor object path is not created */
430 }
431
432 auto& bus = pldm::utils::DBusHandler::getBus();
433 try
434 {
435 associationDefinitionsIntf =
436 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
437 }
438 catch (const sdbusplus::exception_t& e)
439 {
440 lg2::error(
441 "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}",
442 "PATH", path, "ERROR", e);
443 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
444 }
445 associationDefinitionsIntf->associations(
446 {{"chassis", "all_sensors", associationPath.c_str()}});
447
448 double maxValue = std::numeric_limits<double>::quiet_NaN();
449 double minValue = std::numeric_limits<double>::quiet_NaN();
450 bool hasWarningThresholds = false;
451 bool hasCriticalThresholds = false;
452 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
453 double criticalLow = std::numeric_limits<double>::quiet_NaN();
454 double warningHigh = std::numeric_limits<double>::quiet_NaN();
455 double warningLow = std::numeric_limits<double>::quiet_NaN();
456
457 if (pdr->range_field_support.bits.bit0)
458 {
459 hasWarningThresholds = true;
460 warningHigh = pdr->warning_high;
461 }
462 if (pdr->range_field_support.bits.bit1)
463 {
464 hasWarningThresholds = true;
465 warningLow = pdr->warning_low;
466 }
467
468 if (pdr->range_field_support.bits.bit2)
469 {
470 hasCriticalThresholds = true;
471 criticalHigh = pdr->critical_high;
472 }
473
474 if (pdr->range_field_support.bits.bit3)
475 {
476 hasCriticalThresholds = true;
477 criticalLow = pdr->critical_low;
478 }
479
480 resolution = std::numeric_limits<double>::quiet_NaN();
481 offset = std::numeric_limits<double>::quiet_NaN();
482 baseUnitModifier = pdr->unit_modifier;
483
484 /**
485 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
486 * updateTime is in microseconds
487 */
488 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
489 try
490 {
491 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
492 }
493 catch (const sdbusplus::exception_t& e)
494 {
495 lg2::error(
496 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
497 "PATH", path, "ERROR", e);
498 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
499 }
500 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
501 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
502 hysteresis = unitModifier(conversionFormula(hysteresis));
503 valueIntf->unit(sensorUnit);
504
505 try
506 {
507 availabilityIntf = std::make_unique<AvailabilityIntf>(bus,
508 path.c_str());
509 }
510 catch (const sdbusplus::exception_t& e)
511 {
512 lg2::error(
513 "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
514 "PATH", path, "ERROR", e);
515 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
516 }
517 availabilityIntf->available(true);
518
519 try
520 {
521 operationalStatusIntf =
522 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
523 }
524 catch (const sdbusplus::exception_t& e)
525 {
526 lg2::error(
527 "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
528 "PATH", path, "ERROR", e);
529 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
530 }
531 operationalStatusIntf->functional(!sensorDisabled);
532
533 if (hasWarningThresholds)
534 {
535 try
536 {
537 thresholdWarningIntf =
538 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
539 }
540 catch (const sdbusplus::exception_t& e)
541 {
542 lg2::error(
543 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
544 "PATH", path, "ERROR", e);
545 throw sdbusplus::xyz::openbmc_project::Common::Error::
546 InvalidArgument();
547 }
548 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
549 thresholdWarningIntf->warningLow(unitModifier(warningLow));
550 }
551
552 if (hasCriticalThresholds)
553 {
554 try
555 {
556 thresholdCriticalIntf =
557 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
558 }
559 catch (const sdbusplus::exception_t& e)
560 {
561 lg2::error(
562 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}",
563 "PATH", path, "ERROR", e);
564 throw sdbusplus::xyz::openbmc_project::Common::Error::
565 InvalidArgument();
566 }
567 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
568 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
569 }
570}
571
572double NumericSensor::conversionFormula(double value)
573{
574 double convertedValue = value;
575 convertedValue *= std::isnan(resolution) ? 1 : resolution;
576 convertedValue += std::isnan(offset) ? 0 : offset;
577 return convertedValue;
578}
579
580double NumericSensor::unitModifier(double value)
581{
582 return std::isnan(value) ? value : value * std::pow(10, baseUnitModifier);
583}
584} // namespace platform_mc
585} // namespace pldm