xref: /openbmc/pldm/platform-mc/numeric_sensor.cpp (revision 1507b1d8fb0401e14e9e654768691ba28e4f5536)
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