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(const pldm_tid_t tid, const bool sensorDisabled,
19                              std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr,
20                              std::string& sensorName,
21                              std::string& associationPath) :
22     tid(tid), sensorName(sensorName), isPriority(false)
23 {
24     if (!pdr)
25     {
26         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
27     }
28 
29     sensorId = pdr->sensor_id;
30     std::string path;
31     SensorUnit sensorUnit = SensorUnit::DegreesC;
32 
33     switch (pdr->base_unit)
34     {
35         case PLDM_SENSOR_UNIT_DEGRESS_C:
36             sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
37             sensorUnit = SensorUnit::DegreesC;
38             break;
39         case PLDM_SENSOR_UNIT_VOLTS:
40             sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
41             sensorUnit = SensorUnit::Volts;
42             break;
43         case PLDM_SENSOR_UNIT_AMPS:
44             sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
45             sensorUnit = SensorUnit::Amperes;
46             break;
47         case PLDM_SENSOR_UNIT_RPM:
48             sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
49             sensorUnit = SensorUnit::RPMS;
50             break;
51         case PLDM_SENSOR_UNIT_WATTS:
52             sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
53             sensorUnit = SensorUnit::Watts;
54             break;
55         case PLDM_SENSOR_UNIT_JOULES:
56             sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
57             sensorUnit = SensorUnit::Joules;
58             break;
59         case PLDM_SENSOR_UNIT_PERCENTAGE:
60             sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
61             sensorUnit = SensorUnit::Percent;
62             break;
63         default:
64             lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
65                        sensorName, "UNIT", pdr->base_unit);
66             throw sdbusplus::xyz::openbmc_project::Common::Error::
67                 InvalidArgument();
68             break;
69     }
70 
71     path = sensorNameSpace + sensorName;
72     try
73     {
74         auto service = pldm::utils::DBusHandler().getService(
75             path.c_str(), "xyz.openbmc_project.Sensor.Value");
76         if (!service.empty())
77         {
78             throw sdbusplus::xyz::openbmc_project::Common::Error::
79                 TooManyResources();
80         }
81     }
82     catch (const std::exception&)
83     {
84         /* The sensor object path is not created */
85     }
86 
87     auto& bus = pldm::utils::DBusHandler::getBus();
88     try
89     {
90         associationDefinitionsIntf =
91             std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
92     }
93     catch (const sdbusplus::exception_t& e)
94     {
95         lg2::error(
96             "Failed to create association interface for numeric sensor {PATH} error - {ERROR}",
97             "PATH", path, "ERROR", e);
98         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
99     }
100 
101     associationDefinitionsIntf->associations(
102         {{"chassis", "all_sensors", associationPath}});
103 
104     double maxValue = std::numeric_limits<double>::quiet_NaN();
105     double minValue = std::numeric_limits<double>::quiet_NaN();
106 
107     switch (pdr->sensor_data_size)
108     {
109         case PLDM_SENSOR_DATA_SIZE_UINT8:
110             maxValue = pdr->max_readable.value_u8;
111             minValue = pdr->min_readable.value_u8;
112             hysteresis = pdr->hysteresis.value_u8;
113             break;
114         case PLDM_SENSOR_DATA_SIZE_SINT8:
115             maxValue = pdr->max_readable.value_s8;
116             minValue = pdr->min_readable.value_s8;
117             hysteresis = pdr->hysteresis.value_s8;
118             break;
119         case PLDM_SENSOR_DATA_SIZE_UINT16:
120             maxValue = pdr->max_readable.value_u16;
121             minValue = pdr->min_readable.value_u16;
122             hysteresis = pdr->hysteresis.value_u16;
123             break;
124         case PLDM_SENSOR_DATA_SIZE_SINT16:
125             maxValue = pdr->max_readable.value_s16;
126             minValue = pdr->min_readable.value_s16;
127             hysteresis = pdr->hysteresis.value_s16;
128             break;
129         case PLDM_SENSOR_DATA_SIZE_UINT32:
130             maxValue = pdr->max_readable.value_u32;
131             minValue = pdr->min_readable.value_u32;
132             hysteresis = pdr->hysteresis.value_u32;
133             break;
134         case PLDM_SENSOR_DATA_SIZE_SINT32:
135             maxValue = pdr->max_readable.value_s32;
136             minValue = pdr->min_readable.value_s32;
137             hysteresis = pdr->hysteresis.value_s32;
138             break;
139     }
140 
141     bool hasCriticalThresholds = false;
142     bool hasWarningThresholds = false;
143     double criticalHigh = std::numeric_limits<double>::quiet_NaN();
144     double criticalLow = std::numeric_limits<double>::quiet_NaN();
145     double warningHigh = std::numeric_limits<double>::quiet_NaN();
146     double warningLow = std::numeric_limits<double>::quiet_NaN();
147 
148     if (pdr->supported_thresholds.bits.bit0)
149     {
150         hasWarningThresholds = true;
151         switch (pdr->range_field_format)
152         {
153             case PLDM_RANGE_FIELD_FORMAT_UINT8:
154                 warningHigh = pdr->warning_high.value_u8;
155                 break;
156             case PLDM_RANGE_FIELD_FORMAT_SINT8:
157                 warningHigh = pdr->warning_high.value_s8;
158                 break;
159             case PLDM_RANGE_FIELD_FORMAT_UINT16:
160                 warningHigh = pdr->warning_high.value_u16;
161                 break;
162             case PLDM_RANGE_FIELD_FORMAT_SINT16:
163                 warningHigh = pdr->warning_high.value_s16;
164                 break;
165             case PLDM_RANGE_FIELD_FORMAT_UINT32:
166                 warningHigh = pdr->warning_high.value_u32;
167                 break;
168             case PLDM_RANGE_FIELD_FORMAT_SINT32:
169                 warningHigh = pdr->warning_high.value_s32;
170                 break;
171             case PLDM_RANGE_FIELD_FORMAT_REAL32:
172                 warningHigh = pdr->warning_high.value_f32;
173                 break;
174         }
175     }
176 
177     if (pdr->supported_thresholds.bits.bit3)
178     {
179         hasWarningThresholds = true;
180         switch (pdr->range_field_format)
181         {
182             case PLDM_RANGE_FIELD_FORMAT_UINT8:
183                 warningLow = pdr->warning_low.value_u8;
184                 break;
185             case PLDM_RANGE_FIELD_FORMAT_SINT8:
186                 warningLow = pdr->warning_low.value_s8;
187                 break;
188             case PLDM_RANGE_FIELD_FORMAT_UINT16:
189                 warningLow = pdr->warning_low.value_u16;
190                 break;
191             case PLDM_RANGE_FIELD_FORMAT_SINT16:
192                 warningLow = pdr->warning_low.value_s16;
193                 break;
194             case PLDM_RANGE_FIELD_FORMAT_UINT32:
195                 warningLow = pdr->warning_low.value_u32;
196                 break;
197             case PLDM_RANGE_FIELD_FORMAT_SINT32:
198                 warningLow = pdr->warning_low.value_s32;
199                 break;
200             case PLDM_RANGE_FIELD_FORMAT_REAL32:
201                 warningLow = pdr->warning_low.value_f32;
202                 break;
203         }
204     }
205 
206     if (pdr->supported_thresholds.bits.bit1)
207     {
208         hasCriticalThresholds = true;
209         switch (pdr->range_field_format)
210         {
211             case PLDM_RANGE_FIELD_FORMAT_UINT8:
212                 criticalHigh = pdr->critical_high.value_u8;
213                 break;
214             case PLDM_RANGE_FIELD_FORMAT_SINT8:
215                 criticalHigh = pdr->critical_high.value_s8;
216                 break;
217             case PLDM_RANGE_FIELD_FORMAT_UINT16:
218                 criticalHigh = pdr->critical_high.value_u16;
219                 break;
220             case PLDM_RANGE_FIELD_FORMAT_SINT16:
221                 criticalHigh = pdr->critical_high.value_s16;
222                 break;
223             case PLDM_RANGE_FIELD_FORMAT_UINT32:
224                 criticalHigh = pdr->critical_high.value_u32;
225                 break;
226             case PLDM_RANGE_FIELD_FORMAT_SINT32:
227                 criticalHigh = pdr->critical_high.value_s32;
228                 break;
229             case PLDM_RANGE_FIELD_FORMAT_REAL32:
230                 criticalHigh = pdr->critical_high.value_f32;
231                 break;
232         }
233     }
234 
235     if (pdr->supported_thresholds.bits.bit4)
236     {
237         hasCriticalThresholds = true;
238         switch (pdr->range_field_format)
239         {
240             case PLDM_RANGE_FIELD_FORMAT_UINT8:
241                 criticalLow = pdr->critical_low.value_u8;
242                 break;
243             case PLDM_RANGE_FIELD_FORMAT_SINT8:
244                 criticalLow = pdr->critical_low.value_s8;
245                 break;
246             case PLDM_RANGE_FIELD_FORMAT_UINT16:
247                 criticalLow = pdr->critical_low.value_u16;
248                 break;
249             case PLDM_RANGE_FIELD_FORMAT_SINT16:
250                 criticalLow = pdr->critical_low.value_s16;
251                 break;
252             case PLDM_RANGE_FIELD_FORMAT_UINT32:
253                 criticalLow = pdr->critical_low.value_u32;
254                 break;
255             case PLDM_RANGE_FIELD_FORMAT_SINT32:
256                 criticalLow = pdr->critical_low.value_s32;
257                 break;
258             case PLDM_RANGE_FIELD_FORMAT_REAL32:
259                 criticalLow = pdr->critical_low.value_f32;
260                 break;
261         }
262     }
263 
264     resolution = pdr->resolution;
265     offset = pdr->offset;
266     baseUnitModifier = pdr->unit_modifier;
267 
268     /**
269      * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
270      * updateTime is in microseconds
271      */
272     updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
273     if (!std::isnan(pdr->update_interval))
274     {
275         updateTime = pdr->update_interval * 1000000;
276     }
277 
278     try
279     {
280         valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
281     }
282     catch (const sdbusplus::exception_t& e)
283     {
284         lg2::error(
285             "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}",
286             "PATH", path, "ERROR", e);
287         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
288     }
289     valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
290     valueIntf->minValue(unitModifier(conversionFormula(minValue)));
291     hysteresis = unitModifier(conversionFormula(hysteresis));
292     valueIntf->unit(sensorUnit);
293 
294     try
295     {
296         availabilityIntf =
297             std::make_unique<AvailabilityIntf>(bus, path.c_str());
298     }
299     catch (const sdbusplus::exception_t& e)
300     {
301         lg2::error(
302             "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}",
303             "PATH", path, "ERROR", e);
304         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
305     }
306     availabilityIntf->available(true);
307 
308     try
309     {
310         operationalStatusIntf =
311             std::make_unique<OperationalStatusIntf>(bus, path.c_str());
312     }
313     catch (const sdbusplus::exception_t& e)
314     {
315         lg2::error(
316             "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}",
317             "PATH", path, "ERROR", e);
318         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
319     }
320     operationalStatusIntf->functional(!sensorDisabled);
321 
322     if (hasWarningThresholds)
323     {
324         try
325         {
326             thresholdWarningIntf =
327                 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
328         }
329         catch (const sdbusplus::exception_t& e)
330         {
331             lg2::error(
332                 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}",
333                 "PATH", path, "ERROR", e);
334             throw sdbusplus::xyz::openbmc_project::Common::Error::
335                 InvalidArgument();
336         }
337         thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
338         thresholdWarningIntf->warningLow(unitModifier(warningLow));
339     }
340 
341     if (hasCriticalThresholds)
342     {
343         try
344         {
345             thresholdCriticalIntf =
346                 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
347         }
348         catch (const sdbusplus::exception_t& e)
349         {
350             lg2::error(
351                 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}",
352                 "PATH", path, "ERROR", e);
353             throw sdbusplus::xyz::openbmc_project::Common::Error::
354                 InvalidArgument();
355         }
356         thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
357         thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
358     }
359 }
360 
361 NumericSensor::NumericSensor(
362     const pldm_tid_t tid, const bool sensorDisabled,
363     std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
364     std::string& sensorName, std::string& associationPath) :
365     tid(tid), sensorName(sensorName), isPriority(false)
366 {
367     if (!pdr)
368     {
369         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
370     }
371 
372     sensorId = pdr->sensor_id;
373     std::string path;
374     SensorUnit sensorUnit = SensorUnit::DegreesC;
375 
376     switch (pdr->base_unit)
377     {
378         case PLDM_SENSOR_UNIT_DEGRESS_C:
379             sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
380             sensorUnit = SensorUnit::DegreesC;
381             break;
382         case PLDM_SENSOR_UNIT_VOLTS:
383             sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
384             sensorUnit = SensorUnit::Volts;
385             break;
386         case PLDM_SENSOR_UNIT_AMPS:
387             sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
388             sensorUnit = SensorUnit::Amperes;
389             break;
390         case PLDM_SENSOR_UNIT_RPM:
391             sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
392             sensorUnit = SensorUnit::RPMS;
393             break;
394         case PLDM_SENSOR_UNIT_WATTS:
395             sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
396             sensorUnit = SensorUnit::Watts;
397             break;
398         case PLDM_SENSOR_UNIT_JOULES:
399             sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
400             sensorUnit = SensorUnit::Joules;
401             break;
402         case PLDM_SENSOR_UNIT_PERCENTAGE:
403             sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
404             sensorUnit = SensorUnit::Percent;
405             break;
406         default:
407             lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
408                        sensorName, "UNIT", pdr->base_unit);
409             throw sdbusplus::xyz::openbmc_project::Common::Error::
410                 InvalidArgument();
411             break;
412     }
413 
414     path = sensorNameSpace + sensorName;
415     try
416     {
417         auto service = pldm::utils::DBusHandler().getService(
418             path.c_str(), "xyz.openbmc_project.Sensor.Value");
419         if (!service.empty())
420         {
421             throw sdbusplus::xyz::openbmc_project::Common::Error::
422                 TooManyResources();
423         }
424     }
425     catch (const std::exception&)
426     {
427         /* The sensor object path is not created */
428     }
429 
430     auto& bus = pldm::utils::DBusHandler::getBus();
431     try
432     {
433         associationDefinitionsIntf =
434             std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
435     }
436     catch (const sdbusplus::exception_t& e)
437     {
438         lg2::error(
439             "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}",
440             "PATH", path, "ERROR", e);
441         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
442     }
443     associationDefinitionsIntf->associations(
444         {{"chassis", "all_sensors", associationPath.c_str()}});
445 
446     double maxValue = std::numeric_limits<double>::quiet_NaN();
447     double minValue = std::numeric_limits<double>::quiet_NaN();
448     bool hasWarningThresholds = false;
449     bool hasCriticalThresholds = false;
450     double criticalHigh = std::numeric_limits<double>::quiet_NaN();
451     double criticalLow = std::numeric_limits<double>::quiet_NaN();
452     double warningHigh = std::numeric_limits<double>::quiet_NaN();
453     double warningLow = std::numeric_limits<double>::quiet_NaN();
454 
455     if (pdr->range_field_support.bits.bit0)
456     {
457         hasWarningThresholds = true;
458         warningHigh = pdr->warning_high;
459     }
460     if (pdr->range_field_support.bits.bit1)
461     {
462         hasWarningThresholds = true;
463         warningLow = pdr->warning_low;
464     }
465 
466     if (pdr->range_field_support.bits.bit2)
467     {
468         hasCriticalThresholds = true;
469         criticalHigh = pdr->critical_high;
470     }
471 
472     if (pdr->range_field_support.bits.bit3)
473     {
474         hasCriticalThresholds = true;
475         criticalLow = pdr->critical_low;
476     }
477 
478     resolution = std::numeric_limits<double>::quiet_NaN();
479     offset = std::numeric_limits<double>::quiet_NaN();
480     baseUnitModifier = pdr->unit_modifier;
481 
482     /**
483      * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
484      * updateTime is in microseconds
485      */
486     updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
487     try
488     {
489         valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
490     }
491     catch (const sdbusplus::exception_t& e)
492     {
493         lg2::error(
494             "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
495             "PATH", path, "ERROR", e);
496         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
497     }
498     valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
499     valueIntf->minValue(unitModifier(conversionFormula(minValue)));
500     hysteresis = unitModifier(conversionFormula(hysteresis));
501     valueIntf->unit(sensorUnit);
502 
503     try
504     {
505         availabilityIntf =
506             std::make_unique<AvailabilityIntf>(bus, path.c_str());
507     }
508     catch (const sdbusplus::exception_t& e)
509     {
510         lg2::error(
511             "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
512             "PATH", path, "ERROR", e);
513         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
514     }
515     availabilityIntf->available(true);
516 
517     try
518     {
519         operationalStatusIntf =
520             std::make_unique<OperationalStatusIntf>(bus, path.c_str());
521     }
522     catch (const sdbusplus::exception_t& e)
523     {
524         lg2::error(
525             "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
526             "PATH", path, "ERROR", e);
527         throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
528     }
529     operationalStatusIntf->functional(!sensorDisabled);
530 
531     if (hasWarningThresholds)
532     {
533         try
534         {
535             thresholdWarningIntf =
536                 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
537         }
538         catch (const sdbusplus::exception_t& e)
539         {
540             lg2::error(
541                 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
542                 "PATH", path, "ERROR", e);
543             throw sdbusplus::xyz::openbmc_project::Common::Error::
544                 InvalidArgument();
545         }
546         thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
547         thresholdWarningIntf->warningLow(unitModifier(warningLow));
548     }
549 
550     if (hasCriticalThresholds)
551     {
552         try
553         {
554             thresholdCriticalIntf =
555                 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
556         }
557         catch (const sdbusplus::exception_t& e)
558         {
559             lg2::error(
560                 "Failed to create Critical threshold 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         thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
566         thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
567     }
568 }
569 
570 double NumericSensor::conversionFormula(double value)
571 {
572     double convertedValue = value;
573     convertedValue *= std::isnan(resolution) ? 1 : resolution;
574     convertedValue += std::isnan(offset) ? 0 : offset;
575     return convertedValue;
576 }
577 
578 double NumericSensor::unitModifier(double value)
579 {
580     return std::isnan(value) ? value : value * std::pow(10, baseUnitModifier);
581 }
582 } // namespace platform_mc
583 } // namespace pldm
584