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 
NumericSensor(const pldm_tid_t tid,const bool sensorDisabled,std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr,std::string & sensorName,std::string & associationPath)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 
NumericSensor(const pldm_tid_t tid,const bool sensorDisabled,std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,std::string & sensorName,std::string & associationPath)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     hysteresis = 0;
544 
545     /**
546      * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
547      * updateTime is in microseconds
548      */
549     updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
550 
551     if (!useMetricInterface)
552     {
553         try
554         {
555             valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
556         }
557         catch (const sdbusplus::exception_t& e)
558         {
559             lg2::error(
560                 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
561                 "PATH", path, "ERROR", e);
562             throw sdbusplus::xyz::openbmc_project::Common::Error::
563                 InvalidArgument();
564         }
565         valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
566         valueIntf->minValue(unitModifier(conversionFormula(minValue)));
567         valueIntf->unit(sensorUnit);
568     }
569     else
570     {
571         try
572         {
573             metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
574         }
575         catch (const sdbusplus::exception_t& e)
576         {
577             lg2::error(
578                 "Failed to create Metric interface for compact numeric sensor {PATH} error - {ERROR}",
579                 "PATH", path, "ERROR", e);
580             throw sdbusplus::xyz::openbmc_project::Common::Error::
581                 InvalidArgument();
582         }
583         metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
584         metricIntf->minValue(unitModifier(conversionFormula(minValue)));
585         metricIntf->unit(metricUnit);
586     }
587 
588     hysteresis = unitModifier(conversionFormula(hysteresis));
589 
590     try
591     {
592         availabilityIntf =
593             std::make_unique<AvailabilityIntf>(bus, path.c_str());
594     }
595     catch (const sdbusplus::exception_t& e)
596     {
597         lg2::error(
598             "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
599             "PATH", path, "ERROR", e);
600         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
601     }
602     availabilityIntf->available(true);
603 
604     try
605     {
606         operationalStatusIntf =
607             std::make_unique<OperationalStatusIntf>(bus, path.c_str());
608     }
609     catch (const sdbusplus::exception_t& e)
610     {
611         lg2::error(
612             "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
613             "PATH", path, "ERROR", e);
614         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
615     }
616     operationalStatusIntf->functional(!sensorDisabled);
617 
618     if (hasWarningThresholds && !useMetricInterface)
619     {
620         try
621         {
622             thresholdWarningIntf =
623                 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
624         }
625         catch (const sdbusplus::exception_t& e)
626         {
627             lg2::error(
628                 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
629                 "PATH", path, "ERROR", e);
630             throw sdbusplus::xyz::openbmc_project::Common::Error::
631                 InvalidArgument();
632         }
633         thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
634         thresholdWarningIntf->warningLow(unitModifier(warningLow));
635     }
636 
637     if (hasCriticalThresholds && !useMetricInterface)
638     {
639         try
640         {
641             thresholdCriticalIntf =
642                 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
643         }
644         catch (const sdbusplus::exception_t& e)
645         {
646             lg2::error(
647                 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}",
648                 "PATH", path, "ERROR", e);
649             throw sdbusplus::xyz::openbmc_project::Common::Error::
650                 InvalidArgument();
651         }
652         thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
653         thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
654     }
655 }
656 
conversionFormula(double value)657 double NumericSensor::conversionFormula(double value)
658 {
659     double convertedValue = value;
660     convertedValue *= std::isnan(resolution) ? 1 : resolution;
661     convertedValue += std::isnan(offset) ? 0 : offset;
662     return convertedValue;
663 }
664 
unitModifier(double value)665 double NumericSensor::unitModifier(double value)
666 {
667     return std::isnan(value) ? value : value * std::pow(10, baseUnitModifier);
668 }
669 
updateReading(bool available,bool functional,double value)670 void NumericSensor::updateReading(bool available, bool functional, double value)
671 {
672     if (!availabilityIntf || !operationalStatusIntf ||
673         (!useMetricInterface && !valueIntf) ||
674         (useMetricInterface && !metricIntf))
675     {
676         lg2::error(
677             "Failed to update sensor {NAME} D-Bus interface don't exist.",
678             "NAME", sensorName);
679         return;
680     }
681     availabilityIntf->available(available);
682     operationalStatusIntf->functional(functional);
683     double curValue = 0;
684     if (!useMetricInterface)
685     {
686         curValue = valueIntf->value();
687     }
688     else
689     {
690         curValue = metricIntf->value();
691     }
692 
693     double newValue = std::numeric_limits<double>::quiet_NaN();
694     if (functional && available)
695     {
696         newValue = unitModifier(conversionFormula(value));
697         if (newValue != curValue &&
698             (!std::isnan(newValue) || !std::isnan(curValue)))
699         {
700             if (!useMetricInterface)
701             {
702                 valueIntf->value(newValue);
703                 updateThresholds();
704             }
705             else
706             {
707                 metricIntf->value(newValue);
708             }
709         }
710     }
711     else
712     {
713         if (newValue != curValue &&
714             (!std::isnan(newValue) || !std::isnan(curValue)))
715         {
716             if (!useMetricInterface)
717             {
718                 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
719             }
720             else
721             {
722                 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
723             }
724         }
725     }
726 }
727 
handleErrGetSensorReading()728 void NumericSensor::handleErrGetSensorReading()
729 {
730     if (!operationalStatusIntf || (!useMetricInterface && !valueIntf) ||
731         (useMetricInterface && !metricIntf))
732     {
733         lg2::error(
734             "Failed to update sensor {NAME} D-Bus interfaces don't exist.",
735             "NAME", sensorName);
736         return;
737     }
738     operationalStatusIntf->functional(false);
739     if (!useMetricInterface)
740     {
741         valueIntf->value(std::numeric_limits<double>::quiet_NaN());
742     }
743     else
744     {
745         metricIntf->value(std::numeric_limits<double>::quiet_NaN());
746     }
747 }
748 
checkThreshold(bool alarm,bool direction,double value,double threshold,double hyst)749 bool NumericSensor::checkThreshold(bool alarm, bool direction, double value,
750                                    double threshold, double hyst)
751 {
752     if (direction)
753     {
754         if (value >= threshold)
755         {
756             return true;
757         }
758         if (value < (threshold - hyst))
759         {
760             return false;
761         }
762     }
763     else
764     {
765         if (value <= threshold)
766         {
767             return true;
768         }
769         if (value > (threshold + hyst))
770         {
771             return false;
772         }
773     }
774     return alarm;
775 }
776 
updateThresholds()777 void NumericSensor::updateThresholds()
778 {
779     double value = std::numeric_limits<double>::quiet_NaN();
780 
781     if ((!useMetricInterface && !valueIntf) ||
782         (useMetricInterface && !metricIntf))
783     {
784         lg2::error(
785             "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
786             "NAME", sensorName);
787         return;
788     }
789     if (!useMetricInterface)
790     {
791         value = valueIntf->value();
792     }
793     else
794     {
795         value = metricIntf->value();
796     }
797     if (thresholdWarningIntf &&
798         !std::isnan(thresholdWarningIntf->warningHigh()))
799     {
800         auto threshold = thresholdWarningIntf->warningHigh();
801         auto alarm = thresholdWarningIntf->warningAlarmHigh();
802         auto newAlarm =
803             checkThreshold(alarm, true, value, threshold, hysteresis);
804         if (alarm != newAlarm)
805         {
806             thresholdWarningIntf->warningAlarmHigh(newAlarm);
807             if (newAlarm)
808             {
809                 thresholdWarningIntf->warningHighAlarmAsserted(value);
810             }
811             else
812             {
813                 thresholdWarningIntf->warningHighAlarmDeasserted(value);
814             }
815         }
816     }
817 
818     if (thresholdWarningIntf && !std::isnan(thresholdWarningIntf->warningLow()))
819     {
820         auto threshold = thresholdWarningIntf->warningLow();
821         auto alarm = thresholdWarningIntf->warningAlarmLow();
822         auto newAlarm =
823             checkThreshold(alarm, false, value, threshold, hysteresis);
824         if (alarm != newAlarm)
825         {
826             thresholdWarningIntf->warningAlarmLow(newAlarm);
827             if (newAlarm)
828             {
829                 thresholdWarningIntf->warningLowAlarmAsserted(value);
830             }
831             else
832             {
833                 thresholdWarningIntf->warningLowAlarmDeasserted(value);
834             }
835         }
836     }
837 
838     if (thresholdCriticalIntf &&
839         !std::isnan(thresholdCriticalIntf->criticalHigh()))
840     {
841         auto threshold = thresholdCriticalIntf->criticalHigh();
842         auto alarm = thresholdCriticalIntf->criticalAlarmHigh();
843         auto newAlarm =
844             checkThreshold(alarm, true, value, threshold, hysteresis);
845         if (alarm != newAlarm)
846         {
847             thresholdCriticalIntf->criticalAlarmHigh(newAlarm);
848             if (newAlarm)
849             {
850                 thresholdCriticalIntf->criticalHighAlarmAsserted(value);
851             }
852             else
853             {
854                 thresholdCriticalIntf->criticalHighAlarmDeasserted(value);
855             }
856         }
857     }
858 
859     if (thresholdCriticalIntf &&
860         !std::isnan(thresholdCriticalIntf->criticalLow()))
861     {
862         auto threshold = thresholdCriticalIntf->criticalLow();
863         auto alarm = thresholdCriticalIntf->criticalAlarmLow();
864         auto newAlarm =
865             checkThreshold(alarm, false, value, threshold, hysteresis);
866         if (alarm != newAlarm)
867         {
868             thresholdCriticalIntf->criticalAlarmLow(newAlarm);
869             if (newAlarm)
870             {
871                 thresholdCriticalIntf->criticalLowAlarmAsserted(value);
872             }
873             else
874             {
875                 thresholdCriticalIntf->criticalLowAlarmDeasserted(value);
876             }
877         }
878     }
879 }
880 
triggerThresholdEvent(pldm::utils::Level eventType,pldm::utils::Direction direction,double rawValue,bool newAlarm,bool assert)881 int NumericSensor::triggerThresholdEvent(
882     pldm::utils::Level eventType, pldm::utils::Direction direction,
883     double rawValue, bool newAlarm, bool assert)
884 {
885     if (!valueIntf)
886     {
887         lg2::error(
888             "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
889             "NAME", sensorName);
890         return PLDM_ERROR;
891     }
892 
893     auto value = unitModifier(conversionFormula(rawValue));
894     lg2::error(
895         "triggerThresholdEvent eventType {TID}, direction {SID} value {VAL} newAlarm {PSTATE} assert {ESTATE}",
896         "TID", eventType, "SID", direction, "VAL", value, "PSTATE", newAlarm,
897         "ESTATE", assert);
898 
899     switch (eventType)
900     {
901         case pldm::utils::Level::WARNING:
902         {
903             if (!thresholdWarningIntf)
904             {
905                 lg2::error(
906                     "Error:Trigger sensor warning event for non warning threshold sensors {NAME}",
907                     "NAME", sensorName);
908                 return PLDM_ERROR;
909             }
910             if (direction == pldm::utils::Direction::HIGH &&
911                 !std::isnan(thresholdWarningIntf->warningHigh()))
912             {
913                 auto alarm = thresholdWarningIntf->warningAlarmHigh();
914                 if (alarm == newAlarm)
915                 {
916                     return PLDM_SUCCESS;
917                 }
918                 thresholdWarningIntf->warningAlarmHigh(newAlarm);
919                 if (assert)
920                 {
921                     thresholdWarningIntf->warningHighAlarmAsserted(value);
922                 }
923                 else
924                 {
925                     thresholdWarningIntf->warningHighAlarmDeasserted(value);
926                 }
927             }
928             else if (direction == pldm::utils::Direction::LOW &&
929                      !std::isnan(thresholdWarningIntf->warningLow()))
930             {
931                 auto alarm = thresholdWarningIntf->warningAlarmLow();
932                 if (alarm == newAlarm)
933                 {
934                     return PLDM_SUCCESS;
935                 }
936                 thresholdWarningIntf->warningAlarmLow(newAlarm);
937                 if (assert)
938                 {
939                     thresholdWarningIntf->warningLowAlarmAsserted(value);
940                 }
941                 else
942                 {
943                     thresholdWarningIntf->warningLowAlarmDeasserted(value);
944                 }
945             }
946             break;
947         }
948         case pldm::utils::Level::CRITICAL:
949         {
950             if (!thresholdCriticalIntf)
951             {
952                 lg2::error(
953                     "Error:Trigger sensor Critical event for non warning threshold sensors {NAME}",
954                     "NAME", sensorName);
955                 return PLDM_ERROR;
956             }
957             if (direction == pldm::utils::Direction::HIGH &&
958                 !std::isnan(thresholdCriticalIntf->criticalHigh()))
959             {
960                 auto alarm = thresholdCriticalIntf->criticalAlarmHigh();
961                 if (alarm == newAlarm)
962                 {
963                     return PLDM_SUCCESS;
964                 }
965                 thresholdCriticalIntf->criticalAlarmHigh(newAlarm);
966                 if (assert)
967                 {
968                     thresholdCriticalIntf->criticalHighAlarmAsserted(value);
969                 }
970                 else
971                 {
972                     thresholdCriticalIntf->criticalHighAlarmDeasserted(value);
973                 }
974             }
975             else if (direction == pldm::utils::Direction::LOW &&
976                      !std::isnan(thresholdCriticalIntf->criticalLow()))
977             {
978                 auto alarm = thresholdCriticalIntf->criticalAlarmLow();
979                 if (alarm == newAlarm)
980                 {
981                     return PLDM_SUCCESS;
982                 }
983                 thresholdCriticalIntf->criticalAlarmLow(newAlarm);
984                 if (assert)
985                 {
986                     thresholdCriticalIntf->criticalLowAlarmAsserted(value);
987                 }
988                 else
989                 {
990                     thresholdCriticalIntf->criticalLowAlarmDeasserted(value);
991                 }
992             }
993             break;
994         }
995 
996         default:
997             break;
998     }
999 
1000     return PLDM_SUCCESS;
1001 }
1002 } // namespace platform_mc
1003 } // namespace pldm
1004