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