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