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