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