1 #include "numeric_sensor.hpp"
2
3 #include "common/utils.hpp"
4 #include "requester/handler.hpp"
5
6 #include <libpldm/platform.h>
7
8 #include <phosphor-logging/commit.hpp>
9 #include <sdbusplus/asio/property.hpp>
10 #include <xyz/openbmc_project/Logging/Entry/client.hpp>
11 #include <xyz/openbmc_project/Sensor/Threshold/event.hpp>
12
13 #include <limits>
14 #include <regex>
15
16 PHOSPHOR_LOG2_USING;
17
18 namespace pldm
19 {
20 namespace platform_mc
21 {
22
23 // This allows code to cleanly iterate through all supported
24 // threshold levels and directions.
25 static const std::array<pldm::utils::Level, 3> allThresholdLevels = {
26 pldm::utils::Level::WARNING, pldm::utils::Level::CRITICAL,
27 pldm::utils::Level::HARDSHUTDOWN};
28 static const std::array<pldm::utils::Direction, 2> allThresholdDirections = {
29 pldm::utils::Direction::HIGH, pldm::utils::Direction::LOW};
30
createInventoryPath(const std::string & associationPath,const std::string & sensorName,const uint16_t entityType,const uint16_t entityInstanceNum,const uint16_t containerId)31 inline bool NumericSensor::createInventoryPath(
32 const std::string& associationPath, const std::string& sensorName,
33 const uint16_t entityType, const uint16_t entityInstanceNum,
34 const uint16_t containerId)
35 {
36 auto& bus = pldm::utils::DBusHandler::getBus();
37 std::string invPath = associationPath + "/" + sensorName;
38 try
39 {
40 entityIntf = std::make_unique<EntityIntf>(bus, invPath.c_str());
41 }
42 catch (const sdbusplus::exception_t& e)
43 {
44 lg2::error(
45 "Failed to create Entity interface for compact numeric sensor {PATH} error - {ERROR}",
46 "PATH", invPath, "ERROR", e);
47 return false;
48 }
49 entityIntf->entityType(entityType);
50 entityIntf->entityInstanceNumber(entityInstanceNum);
51 entityIntf->containerID(containerId);
52
53 return true;
54 }
55
getSensorDataValue(uint8_t sensor_data_size,union_sensor_data_size & value)56 inline double getSensorDataValue(uint8_t sensor_data_size,
57 union_sensor_data_size& value)
58 {
59 double ret = std::numeric_limits<double>::quiet_NaN();
60 switch (sensor_data_size)
61 {
62 case PLDM_SENSOR_DATA_SIZE_UINT8:
63 ret = value.value_u8;
64 break;
65 case PLDM_SENSOR_DATA_SIZE_SINT8:
66 ret = value.value_s8;
67 break;
68 case PLDM_SENSOR_DATA_SIZE_UINT16:
69 ret = value.value_u16;
70 break;
71 case PLDM_SENSOR_DATA_SIZE_SINT16:
72 ret = value.value_s16;
73 break;
74 case PLDM_SENSOR_DATA_SIZE_UINT32:
75 ret = value.value_u32;
76 break;
77 case PLDM_SENSOR_DATA_SIZE_SINT32:
78 ret = value.value_s32;
79 break;
80 }
81 return ret;
82 }
83
getRangeFieldValue(uint8_t range_field_format,union_range_field_format & value)84 inline double getRangeFieldValue(uint8_t range_field_format,
85 union_range_field_format& value)
86 {
87 double ret = std::numeric_limits<double>::quiet_NaN();
88 switch (range_field_format)
89 {
90 case PLDM_RANGE_FIELD_FORMAT_UINT8:
91 ret = value.value_u8;
92 break;
93 case PLDM_RANGE_FIELD_FORMAT_SINT8:
94 ret = value.value_s8;
95 break;
96 case PLDM_RANGE_FIELD_FORMAT_UINT16:
97 ret = value.value_u16;
98 break;
99 case PLDM_RANGE_FIELD_FORMAT_SINT16:
100 ret = value.value_s16;
101 break;
102 case PLDM_RANGE_FIELD_FORMAT_UINT32:
103 ret = value.value_u32;
104 break;
105 case PLDM_RANGE_FIELD_FORMAT_SINT32:
106 ret = value.value_s32;
107 break;
108 case PLDM_RANGE_FIELD_FORMAT_REAL32:
109 ret = value.value_f32;
110 break;
111 }
112 return ret;
113 }
114
setSensorUnit(uint8_t baseUnit)115 void NumericSensor::setSensorUnit(uint8_t baseUnit)
116 {
117 sensorUnit = SensorUnit::DegreesC;
118 useMetricInterface = false;
119 switch (baseUnit)
120 {
121 case PLDM_SENSOR_UNIT_DEGRESS_C:
122 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
123 sensorUnit = SensorUnit::DegreesC;
124 break;
125 case PLDM_SENSOR_UNIT_VOLTS:
126 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
127 sensorUnit = SensorUnit::Volts;
128 break;
129 case PLDM_SENSOR_UNIT_AMPS:
130 sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
131 sensorUnit = SensorUnit::Amperes;
132 break;
133 case PLDM_SENSOR_UNIT_RPM:
134 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
135 sensorUnit = SensorUnit::RPMS;
136 break;
137 case PLDM_SENSOR_UNIT_WATTS:
138 sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
139 sensorUnit = SensorUnit::Watts;
140 break;
141 case PLDM_SENSOR_UNIT_JOULES:
142 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
143 sensorUnit = SensorUnit::Joules;
144 break;
145 case PLDM_SENSOR_UNIT_PERCENTAGE:
146 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
147 sensorUnit = SensorUnit::Percent;
148 break;
149 case PLDM_SENSOR_UNIT_HERTZ:
150 sensorNameSpace = "/xyz/openbmc_project/sensors/frequency/";
151 sensorUnit = SensorUnit::Hertz;
152 break;
153 case PLDM_SENSOR_UNIT_COUNTS:
154 case PLDM_SENSOR_UNIT_CORRECTED_ERRORS:
155 case PLDM_SENSOR_UNIT_UNCORRECTABLE_ERRORS:
156 sensorNameSpace = "/xyz/openbmc_project/metric/count/";
157 useMetricInterface = true;
158 break;
159 case PLDM_SENSOR_UNIT_OEMUNIT:
160 sensorNameSpace = "/xyz/openbmc_project/metric/oem/";
161 useMetricInterface = true;
162 break;
163 default:
164 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
165 sensorName, "UNIT", baseUnit);
166 throw sdbusplus::xyz::openbmc_project::Common::Error::
167 InvalidArgument();
168 break;
169 }
170 }
171
NumericSensor(const pldm_tid_t tid,const bool sensorDisabled,std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr,std::string & sensorName,std::string & associationPath)172 NumericSensor::NumericSensor(
173 const pldm_tid_t tid, const bool sensorDisabled,
174 std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr, std::string& sensorName,
175 std::string& associationPath) : tid(tid), sensorName(sensorName)
176 {
177 if (!pdr)
178 {
179 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
180 }
181
182 sensorId = pdr->sensor_id;
183 std::string path;
184 MetricUnit metricUnit = MetricUnit::Count;
185 setSensorUnit(pdr->base_unit);
186
187 path = sensorNameSpace + sensorName;
188 try
189 {
190 std::string tmp{};
191 std::string interface = SENSOR_VALUE_INTF;
192 if (useMetricInterface)
193 {
194 interface = METRIC_VALUE_INTF;
195 }
196 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
197 interface.c_str());
198
199 if (!tmp.empty())
200 {
201 throw sdbusplus::xyz::openbmc_project::Common::Error::
202 TooManyResources();
203 }
204 }
205 catch (const std::exception&)
206 {
207 /* The sensor object path is not created */
208 }
209
210 auto& bus = pldm::utils::DBusHandler::getBus();
211 try
212 {
213 associationDefinitionsIntf =
214 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
215 }
216 catch (const sdbusplus::exception_t& e)
217 {
218 lg2::error(
219 "Failed to create association interface for numeric sensor {PATH} error - {ERROR}",
220 "PATH", path, "ERROR", e);
221 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
222 }
223
224 associationDefinitionsIntf->associations(
225 {{"chassis", "all_sensors", associationPath}});
226
227 double maxValue =
228 getSensorDataValue(pdr->sensor_data_size, pdr->max_readable);
229 double minValue =
230 getSensorDataValue(pdr->sensor_data_size, pdr->min_readable);
231 hysteresis = getSensorDataValue(pdr->sensor_data_size, pdr->hysteresis);
232
233 bool hasCriticalThresholds = false;
234 bool hasWarningThresholds = false;
235 bool hasFatalThresholds = false;
236 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
237 double criticalLow = std::numeric_limits<double>::quiet_NaN();
238 double warningHigh = std::numeric_limits<double>::quiet_NaN();
239 double warningLow = std::numeric_limits<double>::quiet_NaN();
240 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
241 double fatalLow = std::numeric_limits<double>::quiet_NaN();
242
243 if (pdr->supported_thresholds.bits.bit0)
244 {
245 hasWarningThresholds = true;
246 warningHigh =
247 getRangeFieldValue(pdr->range_field_format, pdr->warning_high);
248 }
249
250 if (pdr->supported_thresholds.bits.bit3)
251 {
252 hasWarningThresholds = true;
253 warningLow =
254 getRangeFieldValue(pdr->range_field_format, pdr->warning_low);
255 }
256
257 if (pdr->supported_thresholds.bits.bit1)
258 {
259 hasCriticalThresholds = true;
260 criticalHigh =
261 getRangeFieldValue(pdr->range_field_format, pdr->critical_high);
262 }
263
264 if (pdr->supported_thresholds.bits.bit4)
265 {
266 hasCriticalThresholds = true;
267 criticalLow =
268 getRangeFieldValue(pdr->range_field_format, pdr->critical_low);
269 }
270 if (pdr->supported_thresholds.bits.bit2)
271 {
272 hasFatalThresholds = true;
273 fatalHigh =
274 getRangeFieldValue(pdr->range_field_format, pdr->fatal_high);
275 }
276 if (pdr->supported_thresholds.bits.bit5)
277 {
278 hasFatalThresholds = true;
279 fatalLow = getRangeFieldValue(pdr->range_field_format, pdr->fatal_low);
280 }
281
282 resolution = pdr->resolution;
283 offset = pdr->offset;
284 baseUnitModifier = pdr->unit_modifier;
285 timeStamp = 0;
286
287 /**
288 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
289 * updateTime is in microseconds
290 */
291 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
292 if (std::isfinite(pdr->update_interval))
293 {
294 updateTime = pdr->update_interval * 1000000;
295 }
296
297 if (!useMetricInterface)
298 {
299 try
300 {
301 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
302 }
303 catch (const sdbusplus::exception_t& e)
304 {
305 lg2::error(
306 "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}",
307 "PATH", path, "ERROR", e);
308 throw sdbusplus::xyz::openbmc_project::Common::Error::
309 InvalidArgument();
310 }
311 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
312 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
313 valueIntf->unit(sensorUnit);
314 }
315 else
316 {
317 try
318 {
319 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
320 }
321 catch (const sdbusplus::exception_t& e)
322 {
323 lg2::error(
324 "Failed to create Metric interface for numeric sensor {PATH} error - {ERROR}",
325 "PATH", path, "ERROR", e);
326 throw sdbusplus::xyz::openbmc_project::Common::Error::
327 InvalidArgument();
328 }
329 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
330 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
331 metricIntf->unit(metricUnit);
332 }
333
334 hysteresis = unitModifier(conversionFormula(hysteresis));
335
336 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
337 pdr->entity_instance_num, pdr->container_id))
338 {
339 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
340 }
341
342 try
343 {
344 availabilityIntf =
345 std::make_unique<AvailabilityIntf>(bus, path.c_str());
346 }
347 catch (const sdbusplus::exception_t& e)
348 {
349 lg2::error(
350 "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}",
351 "PATH", path, "ERROR", e);
352 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
353 }
354 availabilityIntf->available(true);
355
356 try
357 {
358 operationalStatusIntf =
359 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
360 }
361 catch (const sdbusplus::exception_t& e)
362 {
363 lg2::error(
364 "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}",
365 "PATH", path, "ERROR", e);
366 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
367 }
368 operationalStatusIntf->functional(!sensorDisabled);
369
370 if (hasWarningThresholds && !useMetricInterface)
371 {
372 try
373 {
374 thresholdWarningIntf =
375 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
376 }
377 catch (const sdbusplus::exception_t& e)
378 {
379 lg2::error(
380 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}",
381 "PATH", path, "ERROR", e);
382 throw sdbusplus::xyz::openbmc_project::Common::Error::
383 InvalidArgument();
384 }
385 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
386 thresholdWarningIntf->warningLow(unitModifier(warningLow));
387 }
388
389 if (hasCriticalThresholds && !useMetricInterface)
390 {
391 try
392 {
393 thresholdCriticalIntf =
394 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
395 }
396 catch (const sdbusplus::exception_t& e)
397 {
398 lg2::error(
399 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}",
400 "PATH", path, "ERROR", e);
401 throw sdbusplus::xyz::openbmc_project::Common::Error::
402 InvalidArgument();
403 }
404 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
405 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
406 }
407
408 if (hasFatalThresholds && !useMetricInterface)
409 {
410 try
411 {
412 thresholdHardShutdownIntf =
413 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
414 }
415 catch (const sdbusplus::exception_t& e)
416 {
417 lg2::error(
418 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
419 "PATH", path, "ERROR", e);
420 throw sdbusplus::xyz::openbmc_project::Common::Error::
421 InvalidArgument();
422 }
423 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
424 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
425 }
426 }
427
NumericSensor(const pldm_tid_t tid,const bool sensorDisabled,std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,std::string & sensorName,std::string & associationPath)428 NumericSensor::NumericSensor(
429 const pldm_tid_t tid, const bool sensorDisabled,
430 std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
431 std::string& sensorName, std::string& associationPath) :
432 tid(tid), sensorName(sensorName)
433 {
434 if (!pdr)
435 {
436 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
437 }
438
439 sensorId = pdr->sensor_id;
440 std::string path;
441 MetricUnit metricUnit = MetricUnit::Count;
442 setSensorUnit(pdr->base_unit);
443
444 path = sensorNameSpace + sensorName;
445 try
446 {
447 std::string tmp{};
448 std::string interface = SENSOR_VALUE_INTF;
449 if (useMetricInterface)
450 {
451 interface = METRIC_VALUE_INTF;
452 }
453 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
454 interface.c_str());
455
456 if (!tmp.empty())
457 {
458 throw sdbusplus::xyz::openbmc_project::Common::Error::
459 TooManyResources();
460 }
461 }
462 catch (const std::exception&)
463 {
464 /* The sensor object path is not created */
465 }
466
467 auto& bus = pldm::utils::DBusHandler::getBus();
468 try
469 {
470 associationDefinitionsIntf =
471 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
472 }
473 catch (const sdbusplus::exception_t& e)
474 {
475 lg2::error(
476 "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}",
477 "PATH", path, "ERROR", e);
478 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
479 }
480 associationDefinitionsIntf->associations(
481 {{"chassis", "all_sensors", associationPath.c_str()}});
482
483 double maxValue = std::numeric_limits<double>::quiet_NaN();
484 double minValue = std::numeric_limits<double>::quiet_NaN();
485 bool hasWarningThresholds = false;
486 bool hasCriticalThresholds = false;
487 bool hasFatalThresholds = false;
488 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
489 double criticalLow = std::numeric_limits<double>::quiet_NaN();
490 double warningHigh = std::numeric_limits<double>::quiet_NaN();
491 double warningLow = std::numeric_limits<double>::quiet_NaN();
492 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
493 double fatalLow = std::numeric_limits<double>::quiet_NaN();
494
495 if (pdr->range_field_support.bits.bit0)
496 {
497 hasWarningThresholds = true;
498 warningHigh = pdr->warning_high;
499 }
500 if (pdr->range_field_support.bits.bit1)
501 {
502 hasWarningThresholds = true;
503 warningLow = pdr->warning_low;
504 }
505
506 if (pdr->range_field_support.bits.bit2)
507 {
508 hasCriticalThresholds = true;
509 criticalHigh = pdr->critical_high;
510 }
511
512 if (pdr->range_field_support.bits.bit3)
513 {
514 hasCriticalThresholds = true;
515 criticalLow = pdr->critical_low;
516 }
517 if (pdr->range_field_support.bits.bit4)
518 {
519 hasFatalThresholds = true;
520 fatalHigh = pdr->fatal_high;
521 }
522 if (pdr->range_field_support.bits.bit5)
523 {
524 hasFatalThresholds = true;
525 fatalLow = pdr->fatal_low;
526 }
527
528 resolution = std::numeric_limits<double>::quiet_NaN();
529 offset = std::numeric_limits<double>::quiet_NaN();
530 baseUnitModifier = pdr->unit_modifier;
531 timeStamp = 0;
532 hysteresis = 0;
533
534 /**
535 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
536 * updateTime is in microseconds
537 */
538 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
539
540 if (!useMetricInterface)
541 {
542 try
543 {
544 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
545 }
546 catch (const sdbusplus::exception_t& e)
547 {
548 lg2::error(
549 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
550 "PATH", path, "ERROR", e);
551 throw sdbusplus::xyz::openbmc_project::Common::Error::
552 InvalidArgument();
553 }
554 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
555 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
556 valueIntf->unit(sensorUnit);
557 }
558 else
559 {
560 try
561 {
562 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
563 }
564 catch (const sdbusplus::exception_t& e)
565 {
566 lg2::error(
567 "Failed to create Metric interface for compact numeric sensor {PATH} error - {ERROR}",
568 "PATH", path, "ERROR", e);
569 throw sdbusplus::xyz::openbmc_project::Common::Error::
570 InvalidArgument();
571 }
572 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
573 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
574 metricIntf->unit(metricUnit);
575 }
576
577 hysteresis = unitModifier(conversionFormula(hysteresis));
578
579 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
580 pdr->entity_instance, pdr->container_id))
581 {
582 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
583 }
584
585 try
586 {
587 availabilityIntf =
588 std::make_unique<AvailabilityIntf>(bus, path.c_str());
589 }
590 catch (const sdbusplus::exception_t& e)
591 {
592 lg2::error(
593 "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
594 "PATH", path, "ERROR", e);
595 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
596 }
597 availabilityIntf->available(true);
598
599 try
600 {
601 operationalStatusIntf =
602 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
603 }
604 catch (const sdbusplus::exception_t& e)
605 {
606 lg2::error(
607 "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
608 "PATH", path, "ERROR", e);
609 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
610 }
611 operationalStatusIntf->functional(!sensorDisabled);
612
613 if (hasWarningThresholds && !useMetricInterface)
614 {
615 try
616 {
617 thresholdWarningIntf =
618 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
619 }
620 catch (const sdbusplus::exception_t& e)
621 {
622 lg2::error(
623 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
624 "PATH", path, "ERROR", e);
625 throw sdbusplus::xyz::openbmc_project::Common::Error::
626 InvalidArgument();
627 }
628 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
629 thresholdWarningIntf->warningLow(unitModifier(warningLow));
630 }
631
632 if (hasCriticalThresholds && !useMetricInterface)
633 {
634 try
635 {
636 thresholdCriticalIntf =
637 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
638 }
639 catch (const sdbusplus::exception_t& e)
640 {
641 lg2::error(
642 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}",
643 "PATH", path, "ERROR", e);
644 throw sdbusplus::xyz::openbmc_project::Common::Error::
645 InvalidArgument();
646 }
647 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
648 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
649 }
650
651 if (hasFatalThresholds && !useMetricInterface)
652 {
653 try
654 {
655 thresholdHardShutdownIntf =
656 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
657 }
658 catch (const sdbusplus::exception_t& e)
659 {
660 lg2::error(
661 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
662 "PATH", path, "ERROR", e);
663 throw sdbusplus::xyz::openbmc_project::Common::Error::
664 InvalidArgument();
665 }
666 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
667 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
668 }
669 }
670
conversionFormula(double value)671 double NumericSensor::conversionFormula(double value)
672 {
673 double convertedValue = value;
674 if (std::isfinite(resolution))
675 {
676 convertedValue *= resolution;
677 }
678 if (std::isfinite(offset))
679 {
680 convertedValue += offset;
681 }
682 return convertedValue;
683 }
684
unitModifier(double value)685 double NumericSensor::unitModifier(double value)
686 {
687 if (!std::isfinite(value))
688 {
689 return value;
690 }
691 return value * std::pow(10, baseUnitModifier);
692 }
693
updateReading(bool available,bool functional,double value)694 void NumericSensor::updateReading(bool available, bool functional, double value)
695 {
696 if (!availabilityIntf || !operationalStatusIntf ||
697 (!useMetricInterface && !valueIntf) ||
698 (useMetricInterface && !metricIntf))
699 {
700 lg2::error(
701 "Failed to update sensor {NAME} D-Bus interface don't exist.",
702 "NAME", sensorName);
703 return;
704 }
705 availabilityIntf->available(available);
706 operationalStatusIntf->functional(functional);
707 double curValue = 0;
708 if (!useMetricInterface)
709 {
710 curValue = valueIntf->value();
711 }
712 else
713 {
714 curValue = metricIntf->value();
715 }
716
717 double newValue = std::numeric_limits<double>::quiet_NaN();
718 if (functional && available)
719 {
720 newValue = unitModifier(conversionFormula(value));
721 if (std::isfinite(newValue) || std::isfinite(curValue))
722 {
723 if (!useMetricInterface)
724 {
725 valueIntf->value(newValue);
726 updateThresholds();
727 }
728 else
729 {
730 metricIntf->value(newValue);
731 }
732 }
733 }
734 else
735 {
736 if (newValue != curValue &&
737 (std::isfinite(newValue) || std::isfinite(curValue)))
738 {
739 if (!useMetricInterface)
740 {
741 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
742 }
743 else
744 {
745 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
746 }
747 }
748 }
749 }
750
handleErrGetSensorReading()751 void NumericSensor::handleErrGetSensorReading()
752 {
753 if (!operationalStatusIntf || (!useMetricInterface && !valueIntf) ||
754 (useMetricInterface && !metricIntf))
755 {
756 lg2::error(
757 "Failed to update sensor {NAME} D-Bus interfaces don't exist.",
758 "NAME", sensorName);
759 return;
760 }
761 operationalStatusIntf->functional(false);
762 if (!useMetricInterface)
763 {
764 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
765 }
766 else
767 {
768 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
769 }
770 }
771
checkThreshold(bool alarm,bool direction,double value,double threshold,double hyst)772 bool NumericSensor::checkThreshold(bool alarm, bool direction, double value,
773 double threshold, double hyst)
774 {
775 if (direction)
776 {
777 if (value >= threshold)
778 {
779 return true;
780 }
781 if (value < (threshold - hyst))
782 {
783 return false;
784 }
785 }
786 else
787 {
788 if (value <= threshold)
789 {
790 return true;
791 }
792 if (value > (threshold + hyst))
793 {
794 return false;
795 }
796 }
797 return alarm;
798 }
799
hasThresholdAlarm()800 bool NumericSensor::hasThresholdAlarm()
801 {
802 bool alarm = false;
803 for (auto level : allThresholdLevels)
804 {
805 for (auto direction : allThresholdDirections)
806 {
807 alarm |= getThresholdAlarm(level, direction);
808 }
809 }
810 return alarm;
811 }
812
setWarningThresholdAlarm(pldm::utils::Direction direction,double value,bool newAlarm)813 void NumericSensor::setWarningThresholdAlarm(pldm::utils::Direction direction,
814 double value, bool newAlarm)
815 {
816 if (direction == pldm::utils::Direction::HIGH)
817 {
818 thresholdWarningIntf->warningAlarmHigh(newAlarm);
819 if (newAlarm)
820 {
821 thresholdWarningIntf->warningHighAlarmAsserted(value);
822 }
823 else
824 {
825 thresholdWarningIntf->warningHighAlarmDeasserted(value);
826 }
827 }
828 else
829 {
830 thresholdWarningIntf->warningAlarmLow(newAlarm);
831 if (newAlarm)
832 {
833 thresholdWarningIntf->warningLowAlarmAsserted(value);
834 }
835 else
836 {
837 thresholdWarningIntf->warningLowAlarmDeasserted(value);
838 }
839 }
840 }
841
setCriticalThresholdAlarm(pldm::utils::Direction direction,double value,bool newAlarm)842 void NumericSensor::setCriticalThresholdAlarm(pldm::utils::Direction direction,
843 double value, bool newAlarm)
844 {
845 if (direction == pldm::utils::Direction::HIGH)
846 {
847 thresholdCriticalIntf->criticalAlarmHigh(newAlarm);
848 if (newAlarm)
849 {
850 thresholdCriticalIntf->criticalHighAlarmAsserted(value);
851 }
852 else
853 {
854 thresholdCriticalIntf->criticalHighAlarmDeasserted(value);
855 }
856 }
857 else
858 {
859 thresholdCriticalIntf->criticalAlarmLow(newAlarm);
860 if (newAlarm)
861 {
862 thresholdCriticalIntf->criticalLowAlarmAsserted(value);
863 }
864 else
865 {
866 thresholdCriticalIntf->criticalLowAlarmDeasserted(value);
867 }
868 }
869 }
870
setHardShutdownThresholdAlarm(pldm::utils::Direction direction,double value,bool newAlarm)871 void NumericSensor::setHardShutdownThresholdAlarm(
872 pldm::utils::Direction direction, double value, bool newAlarm)
873 {
874 if (direction == pldm::utils::Direction::HIGH)
875 {
876 thresholdHardShutdownIntf->hardShutdownAlarmHigh(newAlarm);
877 if (newAlarm)
878 {
879 thresholdHardShutdownIntf->hardShutdownHighAlarmAsserted(value);
880 }
881 else
882 {
883 thresholdHardShutdownIntf->hardShutdownHighAlarmDeasserted(value);
884 }
885 }
886 else
887 {
888 thresholdHardShutdownIntf->hardShutdownAlarmLow(newAlarm);
889 if (newAlarm)
890 {
891 thresholdHardShutdownIntf->hardShutdownLowAlarmAsserted(value);
892 }
893 else
894 {
895 thresholdHardShutdownIntf->hardShutdownLowAlarmDeasserted(value);
896 }
897 }
898 }
899
setThresholdAlarm(pldm::utils::Level level,pldm::utils::Direction direction,double value,bool newAlarm)900 int NumericSensor::setThresholdAlarm(pldm::utils::Level level,
901 pldm::utils::Direction direction,
902 double value, bool newAlarm)
903 {
904 if (!isThresholdValid(level, direction))
905 {
906 lg2::error(
907 "Error:Trigger sensor warning event for non warning threshold sensors {NAME}",
908 "NAME", sensorName);
909 return PLDM_ERROR;
910 }
911 auto alarm = getThresholdAlarm(level, direction);
912 if (alarm == newAlarm)
913 {
914 return PLDM_SUCCESS;
915 }
916 switch (level)
917 {
918 case pldm::utils::Level::WARNING:
919 setWarningThresholdAlarm(direction, value, newAlarm);
920 break;
921 case pldm::utils::Level::CRITICAL:
922 setCriticalThresholdAlarm(direction, value, newAlarm);
923 break;
924 case pldm::utils::Level::HARDSHUTDOWN:
925 setHardShutdownThresholdAlarm(direction, value, newAlarm);
926 break;
927 default:
928 return PLDM_ERROR;
929 }
930 if (newAlarm)
931 {
932 createThresholdLog(level, direction, value);
933 }
934 else
935 {
936 auto& log = assertedLog[{level, direction}];
937 if (log.has_value())
938 {
939 clearThresholdLog(log);
940 }
941 // If all alarms have cleared. Log normal range.
942 if (!hasThresholdAlarm())
943 {
944 createNormalRangeLog(value);
945 }
946 }
947 return PLDM_SUCCESS;
948 }
949
clearThresholdLog(std::optional<sdbusplus::message::object_path> & log)950 void NumericSensor::clearThresholdLog(
951 std::optional<sdbusplus::message::object_path>& log)
952 {
953 if (!log)
954 {
955 return;
956 }
957 try
958 {
959 /* empty log entries are returned by commit() if the
960 requested log is being filtered out */
961 if (!log->str.empty())
962 {
963 lg2::resolve(*log);
964 }
965 }
966 catch (std::exception& ec)
967 {
968 lg2::error("Error trying to resolve: {LOG} : {ERROR}", "LOG", log->str,
969 "ERROR", ec);
970 }
971 log.reset();
972 }
973
974 /** @brief helper function template to create a threshold log
975 *
976 * @tparam[in] errorObj - The error object of the log we want to create.
977 * @param[in] sensorObjPath - The object path of the sensor.
978 * @param[in] value - The current value of the sensor.
979 * @param[in] sensorUnit - The units of the sensor.
980 * @param[in] threshold - The threshold value.
981 *
982 * @return optional object holding the object path of the created
983 * log entry. If the log entry is being filtered, we would return
984 * a optional holding an empty string in the object path. This ensures
985 * we follow our state machine properly even if the log is being filtered.
986 */
987 template <typename errorObj>
logThresholdHelper(const std::string & sensorObjPath,double value,SensorUnit sensorUnit,double threshold)988 auto logThresholdHelper(const std::string& sensorObjPath, double value,
989 SensorUnit sensorUnit, double threshold)
990 -> std::optional<sdbusplus::message::object_path>
991 {
992 return lg2::commit(
993 errorObj("SENSOR_NAME", sensorObjPath, "READING_VALUE", value, "UNITS",
994 sensorUnit, "THRESHOLD_VALUE", threshold));
995 }
996
createThresholdLog(pldm::utils::Level level,pldm::utils::Direction direction,double value)997 void NumericSensor::createThresholdLog(
998 pldm::utils::Level level, pldm::utils::Direction direction, double value)
999 {
1000 namespace Errors =
1001 sdbusplus::error::xyz::openbmc_project::sensor::Threshold;
1002 /* Map from threshold level+direction to a an instantiation of
1003 * logThresholdHelper with the required error object class */
1004 static const std::map<
1005 std::tuple<pldm::utils::Level, pldm::utils::Direction>,
1006 std::function<std::optional<sdbusplus::message::object_path>(
1007 const std::string&, double, SensorUnit, double)>>
1008 thresholdEventMap = {
1009 {{pldm::utils::Level::WARNING, pldm::utils::Direction::HIGH},
1010 &logThresholdHelper<Errors::ReadingAboveUpperWarningThreshold>},
1011 {{pldm::utils::Level::WARNING, pldm::utils::Direction::LOW},
1012 &logThresholdHelper<Errors::ReadingBelowLowerWarningThreshold>},
1013 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::HIGH},
1014 &logThresholdHelper<Errors::ReadingAboveUpperCriticalThreshold>},
1015 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::LOW},
1016 &logThresholdHelper<Errors::ReadingBelowLowerCriticalThreshold>},
1017 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::HIGH},
1018 &logThresholdHelper<
1019 Errors::ReadingAboveUpperHardShutdownThreshold>},
1020 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::LOW},
1021 &logThresholdHelper<
1022 Errors::ReadingBelowLowerHardShutdownThreshold>},
1023 };
1024
1025 std::string sensorObjPath = sensorNameSpace + sensorName;
1026 double threshold = getThreshold(level, direction);
1027 try
1028 {
1029 auto helper = thresholdEventMap.at({level, direction});
1030 assertedLog[{level, direction}] =
1031 helper(sensorObjPath, value, sensorUnit, threshold);
1032 }
1033 catch (std::exception& ec)
1034 {
1035 lg2::error(
1036 "Unable to create threshold log entry for {OBJPATH}: {ERROR}",
1037 "OBJPATH", sensorObjPath, "ERROR", ec);
1038 }
1039 }
1040
createNormalRangeLog(double value)1041 void NumericSensor::createNormalRangeLog(double value)
1042 {
1043 namespace Events =
1044 sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
1045 std::string objPath = sensorNameSpace + sensorName;
1046 try
1047 {
1048 lg2::commit(Events::SensorReadingNormalRange(
1049 "SENSOR_NAME", objPath, "READING_VALUE", value, "UNITS",
1050 sensorUnit));
1051 }
1052 catch (std::exception& ec)
1053 {
1054 lg2::error(
1055 "Unable to create SensorReadingNormalRange log entry for {OBJPATH}: {ERROR}",
1056 "OBJPATH", objPath, "ERROR", ec);
1057 }
1058 }
1059
updateThresholds()1060 void NumericSensor::updateThresholds()
1061 {
1062 double value = std::numeric_limits<double>::quiet_NaN();
1063
1064 if ((!useMetricInterface && !valueIntf) ||
1065 (useMetricInterface && !metricIntf))
1066 {
1067 lg2::error(
1068 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1069 "NAME", sensorName);
1070 return;
1071 }
1072 if (!useMetricInterface)
1073 {
1074 value = valueIntf->value();
1075 }
1076 else
1077 {
1078 value = metricIntf->value();
1079 }
1080
1081 for (auto level : allThresholdLevels)
1082 {
1083 for (auto direction : allThresholdDirections)
1084 {
1085 auto threshold = getThreshold(level, direction);
1086 if (!std::isfinite(threshold))
1087 {
1088 continue;
1089 }
1090 auto alarm = getThresholdAlarm(level, direction);
1091 auto newAlarm =
1092 checkThreshold(alarm, direction == pldm::utils::Direction::HIGH,
1093 value, threshold, hysteresis);
1094 setThresholdAlarm(level, direction, value, newAlarm);
1095 }
1096 }
1097 }
1098
triggerThresholdEvent(pldm::utils::Level eventType,pldm::utils::Direction direction,double rawValue,bool newAlarm,bool assert)1099 int NumericSensor::triggerThresholdEvent(
1100 pldm::utils::Level eventType, pldm::utils::Direction direction,
1101 double rawValue, bool newAlarm, bool assert)
1102 {
1103 if (!valueIntf)
1104 {
1105 lg2::error(
1106 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1107 "NAME", sensorName);
1108 return PLDM_ERROR;
1109 }
1110
1111 auto value = unitModifier(conversionFormula(rawValue));
1112 lg2::error(
1113 "triggerThresholdEvent eventType {TID}, direction {SID} value {VAL} newAlarm {PSTATE} assert {ESTATE}",
1114 "TID", eventType, "SID", direction, "VAL", value, "PSTATE", newAlarm,
1115 "ESTATE", assert);
1116
1117 return setThresholdAlarm(eventType, direction, value, newAlarm);
1118 }
1119 } // namespace platform_mc
1120 } // namespace pldm
1121