blob: ad563fdc02ddb09d4ffb026f5e17a65ef9f8ac0b [file] [log] [blame]
Thu Nguyen3c5486d2024-08-01 08:03:08 +00001#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
11PHOSPHOR_LOG2_USING;
12
13namespace pldm
14{
15namespace platform_mc
16{
17
18NumericSensor::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) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -040022 tid(tid), sensorName(sensorName), isPriority(false)
Thu Nguyen3c5486d2024-08-01 08:03:08 +000023{
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 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400296 availabilityIntf =
297 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000298 }
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
361NumericSensor::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) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400365 tid(tid), sensorName(sensorName), isPriority(false)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000366{
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 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400505 availabilityIntf =
506 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000507 }
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
570double 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
578double NumericSensor::unitModifier(double value)
579{
580 return std::isnan(value) ? value : value * std::pow(10, baseUnitModifier);
581}
582} // namespace platform_mc
583} // namespace pldm