blob: b5b5b5914eb6e04c5d2316a05d581504c4920807 [file] [log] [blame]
Thu Nguyen3c5486d2024-08-01 08:03:08 +00001#include "numeric_sensor.hpp"
2
Thu Nguyen3c5486d2024-08-01 08:03:08 +00003#include "common/utils.hpp"
4#include "requester/handler.hpp"
5
Manojkiran Edafe252792025-03-13 19:24:19 +05306#include <libpldm/platform.h>
7
Amithash Prasad79931b62025-05-01 15:34:59 -07008#include <phosphor-logging/commit.hpp>
9#include <sdbusplus/asio/property.hpp>
10#include <xyz/openbmc_project/Logging/Entry/client.hpp>
11#include <xyz/openbmc_project/Sensor/Threshold/event.hpp>
12
Thu Nguyen3c5486d2024-08-01 08:03:08 +000013#include <limits>
14#include <regex>
15
16PHOSPHOR_LOG2_USING;
17
18namespace pldm
19{
20namespace platform_mc
21{
Amithash Prasad79931b62025-05-01 15:34:59 -070022
23// This allows code to cleanly iterate through all supported
24// threshold levels and directions.
Amithash Prasad2480c572025-05-01 15:34:34 -070025static const std::array<pldm::utils::Level, 3> allThresholdLevels = {
26 pldm::utils::Level::WARNING, pldm::utils::Level::CRITICAL,
27 pldm::utils::Level::HARDSHUTDOWN};
28static const std::array<pldm::utils::Direction, 2> allThresholdDirections = {
29 pldm::utils::Direction::HIGH, pldm::utils::Direction::LOW};
Thu Nguyen3c5486d2024-08-01 08:03:08 +000030
Chau Lyd197f092023-11-06 07:29:06 +000031inline bool NumericSensor::createInventoryPath(
32 const std::string& associationPath, const std::string& sensorName,
33 const uint16_t entityType, const uint16_t entityInstanceNum,
34 const uint16_t containerId)
35{
36 auto& bus = pldm::utils::DBusHandler::getBus();
37 std::string invPath = associationPath + "/" + sensorName;
38 try
39 {
40 entityIntf = std::make_unique<EntityIntf>(bus, invPath.c_str());
41 }
42 catch (const sdbusplus::exception_t& e)
43 {
44 lg2::error(
45 "Failed to create Entity interface for compact numeric sensor {PATH} error - {ERROR}",
46 "PATH", invPath, "ERROR", e);
47 return false;
48 }
49 entityIntf->entityType(entityType);
50 entityIntf->entityInstanceNumber(entityInstanceNum);
51 entityIntf->containerID(containerId);
52
53 return true;
54}
55
Amithash Prasada1871172024-12-19 14:26:10 -080056inline double getSensorDataValue(uint8_t sensor_data_size,
57 union_sensor_data_size& value)
58{
59 double ret = std::numeric_limits<double>::quiet_NaN();
60 switch (sensor_data_size)
61 {
62 case PLDM_SENSOR_DATA_SIZE_UINT8:
63 ret = value.value_u8;
64 break;
65 case PLDM_SENSOR_DATA_SIZE_SINT8:
66 ret = value.value_s8;
67 break;
68 case PLDM_SENSOR_DATA_SIZE_UINT16:
69 ret = value.value_u16;
70 break;
71 case PLDM_SENSOR_DATA_SIZE_SINT16:
72 ret = value.value_s16;
73 break;
74 case PLDM_SENSOR_DATA_SIZE_UINT32:
75 ret = value.value_u32;
76 break;
77 case PLDM_SENSOR_DATA_SIZE_SINT32:
78 ret = value.value_s32;
79 break;
80 }
81 return ret;
82}
83
84inline double getRangeFieldValue(uint8_t range_field_format,
85 union_range_field_format& value)
86{
87 double ret = std::numeric_limits<double>::quiet_NaN();
88 switch (range_field_format)
89 {
90 case PLDM_RANGE_FIELD_FORMAT_UINT8:
91 ret = value.value_u8;
92 break;
93 case PLDM_RANGE_FIELD_FORMAT_SINT8:
94 ret = value.value_s8;
95 break;
96 case PLDM_RANGE_FIELD_FORMAT_UINT16:
97 ret = value.value_u16;
98 break;
99 case PLDM_RANGE_FIELD_FORMAT_SINT16:
100 ret = value.value_s16;
101 break;
102 case PLDM_RANGE_FIELD_FORMAT_UINT32:
103 ret = value.value_u32;
104 break;
105 case PLDM_RANGE_FIELD_FORMAT_SINT32:
106 ret = value.value_s32;
107 break;
108 case PLDM_RANGE_FIELD_FORMAT_REAL32:
109 ret = value.value_f32;
110 break;
111 }
112 return ret;
113}
114
Amithash Prasad98256602024-12-19 14:02:30 -0800115void NumericSensor::setSensorUnit(uint8_t baseUnit)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000116{
Amithash Prasad98256602024-12-19 14:02:30 -0800117 sensorUnit = SensorUnit::DegreesC;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000118 useMetricInterface = false;
Amithash Prasad98256602024-12-19 14:02:30 -0800119 switch (baseUnit)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000120 {
121 case PLDM_SENSOR_UNIT_DEGRESS_C:
122 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
123 sensorUnit = SensorUnit::DegreesC;
124 break;
125 case PLDM_SENSOR_UNIT_VOLTS:
126 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
127 sensorUnit = SensorUnit::Volts;
128 break;
129 case PLDM_SENSOR_UNIT_AMPS:
130 sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
131 sensorUnit = SensorUnit::Amperes;
132 break;
133 case PLDM_SENSOR_UNIT_RPM:
134 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
135 sensorUnit = SensorUnit::RPMS;
136 break;
137 case PLDM_SENSOR_UNIT_WATTS:
138 sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
139 sensorUnit = SensorUnit::Watts;
140 break;
141 case PLDM_SENSOR_UNIT_JOULES:
142 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
143 sensorUnit = SensorUnit::Joules;
144 break;
145 case PLDM_SENSOR_UNIT_PERCENTAGE:
146 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
147 sensorUnit = SensorUnit::Percent;
148 break;
Aditya Kurdunkar1507b1d2025-08-21 13:26:13 +0530149 case PLDM_SENSOR_UNIT_HERTZ:
150 sensorNameSpace = "/xyz/openbmc_project/sensors/frequency/";
151 sensorUnit = SensorUnit::Hertz;
152 break;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000153 case PLDM_SENSOR_UNIT_COUNTS:
154 case PLDM_SENSOR_UNIT_CORRECTED_ERRORS:
155 case PLDM_SENSOR_UNIT_UNCORRECTABLE_ERRORS:
156 sensorNameSpace = "/xyz/openbmc_project/metric/count/";
157 useMetricInterface = true;
158 break;
159 case PLDM_SENSOR_UNIT_OEMUNIT:
160 sensorNameSpace = "/xyz/openbmc_project/metric/oem/";
161 useMetricInterface = true;
162 break;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000163 default:
164 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
Amithash Prasad98256602024-12-19 14:02:30 -0800165 sensorName, "UNIT", baseUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000166 throw sdbusplus::xyz::openbmc_project::Common::Error::
167 InvalidArgument();
168 break;
169 }
Amithash Prasad98256602024-12-19 14:02:30 -0800170}
171
172NumericSensor::NumericSensor(
173 const pldm_tid_t tid, const bool sensorDisabled,
174 std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr, std::string& sensorName,
175 std::string& associationPath) : tid(tid), sensorName(sensorName)
176{
177 if (!pdr)
178 {
179 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
180 }
181
182 sensorId = pdr->sensor_id;
183 std::string path;
184 MetricUnit metricUnit = MetricUnit::Count;
185 setSensorUnit(pdr->base_unit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000186
187 path = sensorNameSpace + sensorName;
188 try
189 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000190 std::string tmp{};
191 std::string interface = SENSOR_VALUE_INTF;
192 if (useMetricInterface)
193 {
194 interface = METRIC_VALUE_INTF;
195 }
196 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
197 interface.c_str());
198
199 if (!tmp.empty())
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000200 {
201 throw sdbusplus::xyz::openbmc_project::Common::Error::
202 TooManyResources();
203 }
204 }
205 catch (const std::exception&)
206 {
207 /* The sensor object path is not created */
208 }
209
210 auto& bus = pldm::utils::DBusHandler::getBus();
211 try
212 {
213 associationDefinitionsIntf =
214 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
215 }
216 catch (const sdbusplus::exception_t& e)
217 {
218 lg2::error(
219 "Failed to create association interface for numeric sensor {PATH} error - {ERROR}",
220 "PATH", path, "ERROR", e);
221 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
222 }
223
224 associationDefinitionsIntf->associations(
225 {{"chassis", "all_sensors", associationPath}});
226
Amithash Prasada1871172024-12-19 14:26:10 -0800227 double maxValue =
228 getSensorDataValue(pdr->sensor_data_size, pdr->max_readable);
229 double minValue =
230 getSensorDataValue(pdr->sensor_data_size, pdr->min_readable);
231 hysteresis = getSensorDataValue(pdr->sensor_data_size, pdr->hysteresis);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000232
233 bool hasCriticalThresholds = false;
234 bool hasWarningThresholds = false;
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800235 bool hasFatalThresholds = false;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000236 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
237 double criticalLow = std::numeric_limits<double>::quiet_NaN();
238 double warningHigh = std::numeric_limits<double>::quiet_NaN();
239 double warningLow = std::numeric_limits<double>::quiet_NaN();
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800240 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
241 double fatalLow = std::numeric_limits<double>::quiet_NaN();
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000242
243 if (pdr->supported_thresholds.bits.bit0)
244 {
245 hasWarningThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800246 warningHigh =
247 getRangeFieldValue(pdr->range_field_format, pdr->warning_high);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000248 }
249
250 if (pdr->supported_thresholds.bits.bit3)
251 {
252 hasWarningThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800253 warningLow =
254 getRangeFieldValue(pdr->range_field_format, pdr->warning_low);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000255 }
256
257 if (pdr->supported_thresholds.bits.bit1)
258 {
259 hasCriticalThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800260 criticalHigh =
261 getRangeFieldValue(pdr->range_field_format, pdr->critical_high);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000262 }
263
264 if (pdr->supported_thresholds.bits.bit4)
265 {
266 hasCriticalThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800267 criticalLow =
268 getRangeFieldValue(pdr->range_field_format, pdr->critical_low);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000269 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800270 if (pdr->supported_thresholds.bits.bit2)
271 {
272 hasFatalThresholds = true;
273 fatalHigh =
274 getRangeFieldValue(pdr->range_field_format, pdr->fatal_high);
275 }
276 if (pdr->supported_thresholds.bits.bit5)
277 {
278 hasFatalThresholds = true;
279 fatalLow = getRangeFieldValue(pdr->range_field_format, pdr->fatal_low);
280 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000281
282 resolution = pdr->resolution;
283 offset = pdr->offset;
284 baseUnitModifier = pdr->unit_modifier;
Gilbert Cheneac61a42022-02-23 20:56:19 +0000285 timeStamp = 0;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000286
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);
Ed Tanous0469b562025-01-06 12:35:10 -0800292 if (std::isfinite(pdr->update_interval))
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000293 {
294 updateTime = pdr->update_interval * 1000000;
295 }
296
Thu Nguyen6d615f12024-04-24 05:02:03 +0000297 if (!useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000298 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000299 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);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000314 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000315 else
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000316 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000317 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);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000332 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000333
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000334 hysteresis = unitModifier(conversionFormula(hysteresis));
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000335
Chau Lyd197f092023-11-06 07:29:06 +0000336 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
337 pdr->entity_instance_num, pdr->container_id))
338 {
339 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
340 }
341
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000342 try
343 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400344 availabilityIntf =
345 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000346 }
347 catch (const sdbusplus::exception_t& e)
348 {
349 lg2::error(
350 "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}",
351 "PATH", path, "ERROR", e);
352 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
353 }
354 availabilityIntf->available(true);
355
356 try
357 {
358 operationalStatusIntf =
359 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
360 }
361 catch (const sdbusplus::exception_t& e)
362 {
363 lg2::error(
364 "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}",
365 "PATH", path, "ERROR", e);
366 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
367 }
368 operationalStatusIntf->functional(!sensorDisabled);
369
Thu Nguyen6d615f12024-04-24 05:02:03 +0000370 if (hasWarningThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000371 {
372 try
373 {
374 thresholdWarningIntf =
375 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
376 }
377 catch (const sdbusplus::exception_t& e)
378 {
379 lg2::error(
380 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}",
381 "PATH", path, "ERROR", e);
382 throw sdbusplus::xyz::openbmc_project::Common::Error::
383 InvalidArgument();
384 }
385 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
386 thresholdWarningIntf->warningLow(unitModifier(warningLow));
387 }
388
Thu Nguyen6d615f12024-04-24 05:02:03 +0000389 if (hasCriticalThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000390 {
391 try
392 {
393 thresholdCriticalIntf =
394 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
395 }
396 catch (const sdbusplus::exception_t& e)
397 {
398 lg2::error(
399 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}",
400 "PATH", path, "ERROR", e);
401 throw sdbusplus::xyz::openbmc_project::Common::Error::
402 InvalidArgument();
403 }
404 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
405 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
406 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800407
408 if (hasFatalThresholds && !useMetricInterface)
409 {
410 try
411 {
412 thresholdHardShutdownIntf =
413 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
414 }
415 catch (const sdbusplus::exception_t& e)
416 {
417 lg2::error(
418 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
419 "PATH", path, "ERROR", e);
420 throw sdbusplus::xyz::openbmc_project::Common::Error::
421 InvalidArgument();
422 }
423 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
424 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
425 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000426}
427
428NumericSensor::NumericSensor(
429 const pldm_tid_t tid, const bool sensorDisabled,
430 std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
431 std::string& sensorName, std::string& associationPath) :
Gilbert Cheneac61a42022-02-23 20:56:19 +0000432 tid(tid), sensorName(sensorName)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000433{
434 if (!pdr)
435 {
436 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
437 }
438
439 sensorId = pdr->sensor_id;
440 std::string path;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000441 MetricUnit metricUnit = MetricUnit::Count;
Amithash Prasad98256602024-12-19 14:02:30 -0800442 setSensorUnit(pdr->base_unit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000443
444 path = sensorNameSpace + sensorName;
445 try
446 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000447 std::string tmp{};
448 std::string interface = SENSOR_VALUE_INTF;
449 if (useMetricInterface)
450 {
451 interface = METRIC_VALUE_INTF;
452 }
453 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
454 interface.c_str());
455
456 if (!tmp.empty())
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000457 {
458 throw sdbusplus::xyz::openbmc_project::Common::Error::
459 TooManyResources();
460 }
461 }
462 catch (const std::exception&)
463 {
464 /* The sensor object path is not created */
465 }
466
467 auto& bus = pldm::utils::DBusHandler::getBus();
468 try
469 {
470 associationDefinitionsIntf =
471 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
472 }
473 catch (const sdbusplus::exception_t& e)
474 {
475 lg2::error(
476 "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}",
477 "PATH", path, "ERROR", e);
478 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
479 }
480 associationDefinitionsIntf->associations(
481 {{"chassis", "all_sensors", associationPath.c_str()}});
482
483 double maxValue = std::numeric_limits<double>::quiet_NaN();
484 double minValue = std::numeric_limits<double>::quiet_NaN();
485 bool hasWarningThresholds = false;
486 bool hasCriticalThresholds = false;
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800487 bool hasFatalThresholds = false;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000488 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
489 double criticalLow = std::numeric_limits<double>::quiet_NaN();
490 double warningHigh = std::numeric_limits<double>::quiet_NaN();
491 double warningLow = std::numeric_limits<double>::quiet_NaN();
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800492 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
493 double fatalLow = std::numeric_limits<double>::quiet_NaN();
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000494
495 if (pdr->range_field_support.bits.bit0)
496 {
497 hasWarningThresholds = true;
498 warningHigh = pdr->warning_high;
499 }
500 if (pdr->range_field_support.bits.bit1)
501 {
502 hasWarningThresholds = true;
503 warningLow = pdr->warning_low;
504 }
505
506 if (pdr->range_field_support.bits.bit2)
507 {
508 hasCriticalThresholds = true;
509 criticalHigh = pdr->critical_high;
510 }
511
512 if (pdr->range_field_support.bits.bit3)
513 {
514 hasCriticalThresholds = true;
515 criticalLow = pdr->critical_low;
516 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800517 if (pdr->range_field_support.bits.bit4)
518 {
519 hasFatalThresholds = true;
520 fatalHigh = pdr->fatal_high;
521 }
522 if (pdr->range_field_support.bits.bit5)
523 {
524 hasFatalThresholds = true;
525 fatalLow = pdr->fatal_low;
526 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000527
528 resolution = std::numeric_limits<double>::quiet_NaN();
529 offset = std::numeric_limits<double>::quiet_NaN();
530 baseUnitModifier = pdr->unit_modifier;
Gilbert Cheneac61a42022-02-23 20:56:19 +0000531 timeStamp = 0;
Thu Nguyen2027ff52024-10-03 21:58:22 +0000532 hysteresis = 0;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000533
534 /**
535 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
536 * updateTime is in microseconds
537 */
538 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000539
540 if (!useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000541 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000542 try
543 {
544 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
545 }
546 catch (const sdbusplus::exception_t& e)
547 {
548 lg2::error(
549 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
550 "PATH", path, "ERROR", e);
551 throw sdbusplus::xyz::openbmc_project::Common::Error::
552 InvalidArgument();
553 }
554 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
555 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
556 valueIntf->unit(sensorUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000557 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000558 else
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000559 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000560 try
561 {
562 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
563 }
564 catch (const sdbusplus::exception_t& e)
565 {
566 lg2::error(
567 "Failed to create Metric interface for compact numeric sensor {PATH} error - {ERROR}",
568 "PATH", path, "ERROR", e);
569 throw sdbusplus::xyz::openbmc_project::Common::Error::
570 InvalidArgument();
571 }
572 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
573 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
574 metricIntf->unit(metricUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000575 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000576
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000577 hysteresis = unitModifier(conversionFormula(hysteresis));
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000578
Chau Lyd197f092023-11-06 07:29:06 +0000579 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
580 pdr->entity_instance, pdr->container_id))
581 {
582 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
583 }
584
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000585 try
586 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400587 availabilityIntf =
588 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000589 }
590 catch (const sdbusplus::exception_t& e)
591 {
592 lg2::error(
593 "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
594 "PATH", path, "ERROR", e);
595 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
596 }
597 availabilityIntf->available(true);
598
599 try
600 {
601 operationalStatusIntf =
602 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
603 }
604 catch (const sdbusplus::exception_t& e)
605 {
606 lg2::error(
607 "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
608 "PATH", path, "ERROR", e);
609 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
610 }
611 operationalStatusIntf->functional(!sensorDisabled);
612
Thu Nguyen6d615f12024-04-24 05:02:03 +0000613 if (hasWarningThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000614 {
615 try
616 {
617 thresholdWarningIntf =
618 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
619 }
620 catch (const sdbusplus::exception_t& e)
621 {
622 lg2::error(
623 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
624 "PATH", path, "ERROR", e);
625 throw sdbusplus::xyz::openbmc_project::Common::Error::
626 InvalidArgument();
627 }
628 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
629 thresholdWarningIntf->warningLow(unitModifier(warningLow));
630 }
631
Thu Nguyen6d615f12024-04-24 05:02:03 +0000632 if (hasCriticalThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000633 {
634 try
635 {
636 thresholdCriticalIntf =
637 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
638 }
639 catch (const sdbusplus::exception_t& e)
640 {
641 lg2::error(
642 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}",
643 "PATH", path, "ERROR", e);
644 throw sdbusplus::xyz::openbmc_project::Common::Error::
645 InvalidArgument();
646 }
647 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
648 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
649 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800650
651 if (hasFatalThresholds && !useMetricInterface)
652 {
653 try
654 {
655 thresholdHardShutdownIntf =
656 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
657 }
658 catch (const sdbusplus::exception_t& e)
659 {
660 lg2::error(
661 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
662 "PATH", path, "ERROR", e);
663 throw sdbusplus::xyz::openbmc_project::Common::Error::
664 InvalidArgument();
665 }
666 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
667 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
668 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000669}
670
671double NumericSensor::conversionFormula(double value)
672{
673 double convertedValue = value;
Ed Tanous0469b562025-01-06 12:35:10 -0800674 if (std::isfinite(resolution))
675 {
676 convertedValue *= resolution;
677 }
678 if (std::isfinite(offset))
679 {
680 convertedValue += offset;
681 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000682 return convertedValue;
683}
684
685double NumericSensor::unitModifier(double value)
686{
Ed Tanous0469b562025-01-06 12:35:10 -0800687 if (!std::isfinite(value))
688 {
689 return value;
690 }
691 return value * std::pow(10, baseUnitModifier);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000692}
Gilbert Cheneac61a42022-02-23 20:56:19 +0000693
694void NumericSensor::updateReading(bool available, bool functional, double value)
695{
Thu Nguyen6d615f12024-04-24 05:02:03 +0000696 if (!availabilityIntf || !operationalStatusIntf ||
697 (!useMetricInterface && !valueIntf) ||
698 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000699 {
700 lg2::error(
701 "Failed to update sensor {NAME} D-Bus interface don't exist.",
702 "NAME", sensorName);
703 return;
704 }
705 availabilityIntf->available(available);
706 operationalStatusIntf->functional(functional);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000707 double curValue = 0;
708 if (!useMetricInterface)
709 {
710 curValue = valueIntf->value();
711 }
712 else
713 {
714 curValue = metricIntf->value();
715 }
716
Gilbert Cheneac61a42022-02-23 20:56:19 +0000717 double newValue = std::numeric_limits<double>::quiet_NaN();
718 if (functional && available)
719 {
720 newValue = unitModifier(conversionFormula(value));
Peter Yin04d27282025-07-10 16:54:08 +0800721 if (std::isfinite(newValue) || std::isfinite(curValue))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000722 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000723 if (!useMetricInterface)
724 {
725 valueIntf->value(newValue);
726 updateThresholds();
727 }
728 else
729 {
730 metricIntf->value(newValue);
731 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000732 }
733 }
734 else
735 {
736 if (newValue != curValue &&
Ed Tanous0469b562025-01-06 12:35:10 -0800737 (std::isfinite(newValue) || std::isfinite(curValue)))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000738 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000739 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 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000747 }
748 }
749}
750
751void NumericSensor::handleErrGetSensorReading()
752{
Thu Nguyen6d615f12024-04-24 05:02:03 +0000753 if (!operationalStatusIntf || (!useMetricInterface && !valueIntf) ||
754 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000755 {
756 lg2::error(
757 "Failed to update sensor {NAME} D-Bus interfaces don't exist.",
758 "NAME", sensorName);
759 return;
760 }
761 operationalStatusIntf->functional(false);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000762 if (!useMetricInterface)
763 {
764 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
765 }
766 else
767 {
768 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
769 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000770}
771
772bool NumericSensor::checkThreshold(bool alarm, bool direction, double value,
773 double threshold, double hyst)
774{
775 if (direction)
776 {
777 if (value >= threshold)
778 {
779 return true;
780 }
781 if (value < (threshold - hyst))
782 {
783 return false;
784 }
785 }
786 else
787 {
788 if (value <= threshold)
789 {
790 return true;
791 }
792 if (value > (threshold + hyst))
793 {
794 return false;
795 }
796 }
797 return alarm;
798}
Amithash Prasad79931b62025-05-01 15:34:59 -0700799
800bool NumericSensor::hasThresholdAlarm()
801{
802 bool alarm = false;
803 for (auto level : allThresholdLevels)
804 {
805 for (auto direction : allThresholdDirections)
806 {
807 alarm |= getThresholdAlarm(level, direction);
808 }
809 }
810 return alarm;
811}
812
Amithash Prasad2480c572025-05-01 15:34:34 -0700813void NumericSensor::setWarningThresholdAlarm(pldm::utils::Direction direction,
814 double value, bool newAlarm)
815{
816 if (direction == pldm::utils::Direction::HIGH)
817 {
818 thresholdWarningIntf->warningAlarmHigh(newAlarm);
819 if (newAlarm)
820 {
821 thresholdWarningIntf->warningHighAlarmAsserted(value);
822 }
823 else
824 {
825 thresholdWarningIntf->warningHighAlarmDeasserted(value);
826 }
827 }
828 else
829 {
830 thresholdWarningIntf->warningAlarmLow(newAlarm);
831 if (newAlarm)
832 {
833 thresholdWarningIntf->warningLowAlarmAsserted(value);
834 }
835 else
836 {
837 thresholdWarningIntf->warningLowAlarmDeasserted(value);
838 }
839 }
840}
841
842void NumericSensor::setCriticalThresholdAlarm(pldm::utils::Direction direction,
843 double value, bool newAlarm)
844{
845 if (direction == pldm::utils::Direction::HIGH)
846 {
847 thresholdCriticalIntf->criticalAlarmHigh(newAlarm);
848 if (newAlarm)
849 {
850 thresholdCriticalIntf->criticalHighAlarmAsserted(value);
851 }
852 else
853 {
854 thresholdCriticalIntf->criticalHighAlarmDeasserted(value);
855 }
856 }
857 else
858 {
859 thresholdCriticalIntf->criticalAlarmLow(newAlarm);
860 if (newAlarm)
861 {
862 thresholdCriticalIntf->criticalLowAlarmAsserted(value);
863 }
864 else
865 {
866 thresholdCriticalIntf->criticalLowAlarmDeasserted(value);
867 }
868 }
869}
870
871void NumericSensor::setHardShutdownThresholdAlarm(
872 pldm::utils::Direction direction, double value, bool newAlarm)
873{
874 if (direction == pldm::utils::Direction::HIGH)
875 {
876 thresholdHardShutdownIntf->hardShutdownAlarmHigh(newAlarm);
877 if (newAlarm)
878 {
879 thresholdHardShutdownIntf->hardShutdownHighAlarmAsserted(value);
880 }
881 else
882 {
883 thresholdHardShutdownIntf->hardShutdownHighAlarmDeasserted(value);
884 }
885 }
886 else
887 {
888 thresholdHardShutdownIntf->hardShutdownAlarmLow(newAlarm);
889 if (newAlarm)
890 {
891 thresholdHardShutdownIntf->hardShutdownLowAlarmAsserted(value);
892 }
893 else
894 {
895 thresholdHardShutdownIntf->hardShutdownLowAlarmDeasserted(value);
896 }
897 }
898}
899
900int NumericSensor::setThresholdAlarm(pldm::utils::Level level,
901 pldm::utils::Direction direction,
902 double value, bool newAlarm)
903{
904 if (!isThresholdValid(level, direction))
905 {
906 lg2::error(
907 "Error:Trigger sensor warning event for non warning threshold sensors {NAME}",
908 "NAME", sensorName);
909 return PLDM_ERROR;
910 }
911 auto alarm = getThresholdAlarm(level, direction);
912 if (alarm == newAlarm)
913 {
914 return PLDM_SUCCESS;
915 }
916 switch (level)
917 {
918 case pldm::utils::Level::WARNING:
919 setWarningThresholdAlarm(direction, value, newAlarm);
920 break;
921 case pldm::utils::Level::CRITICAL:
922 setCriticalThresholdAlarm(direction, value, newAlarm);
923 break;
924 case pldm::utils::Level::HARDSHUTDOWN:
925 setHardShutdownThresholdAlarm(direction, value, newAlarm);
926 break;
927 default:
928 return PLDM_ERROR;
929 }
Amithash Prasad79931b62025-05-01 15:34:59 -0700930 if (newAlarm)
931 {
932 createThresholdLog(level, direction, value);
933 }
934 else
935 {
936 auto& log = assertedLog[{level, direction}];
937 if (log.has_value())
938 {
939 clearThresholdLog(log);
940 }
941 // If all alarms have cleared. Log normal range.
942 if (!hasThresholdAlarm())
943 {
944 createNormalRangeLog(value);
945 }
946 }
Amithash Prasad2480c572025-05-01 15:34:34 -0700947 return PLDM_SUCCESS;
948}
Gilbert Cheneac61a42022-02-23 20:56:19 +0000949
Amithash Prasad79931b62025-05-01 15:34:59 -0700950void NumericSensor::clearThresholdLog(
951 std::optional<sdbusplus::message::object_path>& log)
952{
953 if (!log)
954 {
955 return;
956 }
957 try
958 {
959 /* empty log entries are returned by commit() if the
960 requested log is being filtered out */
961 if (!log->str.empty())
962 {
963 lg2::resolve(*log);
964 }
965 }
966 catch (std::exception& ec)
967 {
968 lg2::error("Error trying to resolve: {LOG} : {ERROR}", "LOG", log->str,
969 "ERROR", ec);
970 }
971 log.reset();
972}
973
974/** @brief helper function template to create a threshold log
975 *
976 * @tparam[in] errorObj - The error object of the log we want to create.
977 * @param[in] sensorObjPath - The object path of the sensor.
978 * @param[in] value - The current value of the sensor.
979 * @param[in] sensorUnit - The units of the sensor.
980 * @param[in] threshold - The threshold value.
981 *
982 * @return optional object holding the object path of the created
983 * log entry. If the log entry is being filtered, we would return
984 * a optional holding an empty string in the object path. This ensures
985 * we follow our state machine properly even if the log is being filtered.
986 */
987template <typename errorObj>
988auto logThresholdHelper(const std::string& sensorObjPath, double value,
989 SensorUnit sensorUnit, double threshold)
990 -> std::optional<sdbusplus::message::object_path>
991{
992 return lg2::commit(
993 errorObj("SENSOR_NAME", sensorObjPath, "READING_VALUE", value, "UNITS",
994 sensorUnit, "THRESHOLD_VALUE", threshold));
995}
996
997void NumericSensor::createThresholdLog(
998 pldm::utils::Level level, pldm::utils::Direction direction, double value)
999{
1000 namespace Errors =
1001 sdbusplus::error::xyz::openbmc_project::sensor::Threshold;
1002 /* Map from threshold level+direction to a an instantiation of
1003 * logThresholdHelper with the required error object class */
1004 static const std::map<
1005 std::tuple<pldm::utils::Level, pldm::utils::Direction>,
1006 std::function<std::optional<sdbusplus::message::object_path>(
1007 const std::string&, double, SensorUnit, double)>>
1008 thresholdEventMap = {
1009 {{pldm::utils::Level::WARNING, pldm::utils::Direction::HIGH},
1010 &logThresholdHelper<Errors::ReadingAboveUpperWarningThreshold>},
1011 {{pldm::utils::Level::WARNING, pldm::utils::Direction::LOW},
1012 &logThresholdHelper<Errors::ReadingBelowLowerWarningThreshold>},
1013 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::HIGH},
1014 &logThresholdHelper<Errors::ReadingAboveUpperCriticalThreshold>},
1015 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::LOW},
1016 &logThresholdHelper<Errors::ReadingBelowLowerCriticalThreshold>},
1017 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::HIGH},
1018 &logThresholdHelper<
1019 Errors::ReadingAboveUpperHardShutdownThreshold>},
1020 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::LOW},
1021 &logThresholdHelper<
1022 Errors::ReadingBelowLowerHardShutdownThreshold>},
1023 };
1024
1025 std::string sensorObjPath = sensorNameSpace + sensorName;
1026 double threshold = getThreshold(level, direction);
1027 try
1028 {
1029 auto helper = thresholdEventMap.at({level, direction});
1030 assertedLog[{level, direction}] =
1031 helper(sensorObjPath, value, sensorUnit, threshold);
1032 }
1033 catch (std::exception& ec)
1034 {
1035 lg2::error(
1036 "Unable to create threshold log entry for {OBJPATH}: {ERROR}",
1037 "OBJPATH", sensorObjPath, "ERROR", ec);
1038 }
1039}
1040
1041void NumericSensor::createNormalRangeLog(double value)
1042{
1043 namespace Events =
1044 sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
1045 std::string objPath = sensorNameSpace + sensorName;
1046 try
1047 {
1048 lg2::commit(Events::SensorReadingNormalRange(
1049 "SENSOR_NAME", objPath, "READING_VALUE", value, "UNITS",
1050 sensorUnit));
1051 }
1052 catch (std::exception& ec)
1053 {
1054 lg2::error(
1055 "Unable to create SensorReadingNormalRange log entry for {OBJPATH}: {ERROR}",
1056 "OBJPATH", objPath, "ERROR", ec);
1057 }
1058}
1059
Gilbert Cheneac61a42022-02-23 20:56:19 +00001060void NumericSensor::updateThresholds()
1061{
Thu Nguyen6d615f12024-04-24 05:02:03 +00001062 double value = std::numeric_limits<double>::quiet_NaN();
1063
1064 if ((!useMetricInterface && !valueIntf) ||
1065 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +00001066 {
1067 lg2::error(
1068 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1069 "NAME", sensorName);
1070 return;
1071 }
Thu Nguyen6d615f12024-04-24 05:02:03 +00001072 if (!useMetricInterface)
1073 {
1074 value = valueIntf->value();
1075 }
1076 else
1077 {
1078 value = metricIntf->value();
1079 }
Gilbert Cheneac61a42022-02-23 20:56:19 +00001080
Amithash Prasad2480c572025-05-01 15:34:34 -07001081 for (auto level : allThresholdLevels)
Gilbert Cheneac61a42022-02-23 20:56:19 +00001082 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001083 for (auto direction : allThresholdDirections)
Gilbert Cheneac61a42022-02-23 20:56:19 +00001084 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001085 auto threshold = getThreshold(level, direction);
1086 if (!std::isfinite(threshold))
Gilbert Cheneac61a42022-02-23 20:56:19 +00001087 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001088 continue;
Gilbert Cheneac61a42022-02-23 20:56:19 +00001089 }
Amithash Prasad2480c572025-05-01 15:34:34 -07001090 auto alarm = getThresholdAlarm(level, direction);
1091 auto newAlarm =
1092 checkThreshold(alarm, direction == pldm::utils::Direction::HIGH,
1093 value, threshold, hysteresis);
1094 setThresholdAlarm(level, direction, value, newAlarm);
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +08001095 }
1096 }
Gilbert Cheneac61a42022-02-23 20:56:19 +00001097}
Gilbert Chen77e6fe72024-08-06 09:23:30 +00001098
1099int NumericSensor::triggerThresholdEvent(
1100 pldm::utils::Level eventType, pldm::utils::Direction direction,
1101 double rawValue, bool newAlarm, bool assert)
1102{
1103 if (!valueIntf)
1104 {
1105 lg2::error(
1106 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1107 "NAME", sensorName);
1108 return PLDM_ERROR;
1109 }
1110
1111 auto value = unitModifier(conversionFormula(rawValue));
1112 lg2::error(
1113 "triggerThresholdEvent eventType {TID}, direction {SID} value {VAL} newAlarm {PSTATE} assert {ESTATE}",
1114 "TID", eventType, "SID", direction, "VAL", value, "PSTATE", newAlarm,
1115 "ESTATE", assert);
1116
Amithash Prasad2480c572025-05-01 15:34:34 -07001117 return setThresholdAlarm(eventType, direction, value, newAlarm);
Gilbert Chen77e6fe72024-08-06 09:23:30 +00001118}
Thu Nguyen3c5486d2024-08-01 08:03:08 +00001119} // namespace platform_mc
1120} // namespace pldm