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