blob: 070dbbb3251641c33d52997b32e07f38d718742a [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;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000149 case PLDM_SENSOR_UNIT_COUNTS:
150 case PLDM_SENSOR_UNIT_CORRECTED_ERRORS:
151 case PLDM_SENSOR_UNIT_UNCORRECTABLE_ERRORS:
152 sensorNameSpace = "/xyz/openbmc_project/metric/count/";
153 useMetricInterface = true;
154 break;
155 case PLDM_SENSOR_UNIT_OEMUNIT:
156 sensorNameSpace = "/xyz/openbmc_project/metric/oem/";
157 useMetricInterface = true;
158 break;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000159 default:
160 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
Amithash Prasad98256602024-12-19 14:02:30 -0800161 sensorName, "UNIT", baseUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000162 throw sdbusplus::xyz::openbmc_project::Common::Error::
163 InvalidArgument();
164 break;
165 }
Amithash Prasad98256602024-12-19 14:02:30 -0800166}
167
168NumericSensor::NumericSensor(
169 const pldm_tid_t tid, const bool sensorDisabled,
170 std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr, std::string& sensorName,
171 std::string& associationPath) : tid(tid), sensorName(sensorName)
172{
173 if (!pdr)
174 {
175 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
176 }
177
178 sensorId = pdr->sensor_id;
179 std::string path;
180 MetricUnit metricUnit = MetricUnit::Count;
181 setSensorUnit(pdr->base_unit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000182
183 path = sensorNameSpace + sensorName;
184 try
185 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000186 std::string tmp{};
187 std::string interface = SENSOR_VALUE_INTF;
188 if (useMetricInterface)
189 {
190 interface = METRIC_VALUE_INTF;
191 }
192 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
193 interface.c_str());
194
195 if (!tmp.empty())
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000196 {
197 throw sdbusplus::xyz::openbmc_project::Common::Error::
198 TooManyResources();
199 }
200 }
201 catch (const std::exception&)
202 {
203 /* The sensor object path is not created */
204 }
205
206 auto& bus = pldm::utils::DBusHandler::getBus();
207 try
208 {
209 associationDefinitionsIntf =
210 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
211 }
212 catch (const sdbusplus::exception_t& e)
213 {
214 lg2::error(
215 "Failed to create association interface for numeric sensor {PATH} error - {ERROR}",
216 "PATH", path, "ERROR", e);
217 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
218 }
219
220 associationDefinitionsIntf->associations(
221 {{"chassis", "all_sensors", associationPath}});
222
Amithash Prasada1871172024-12-19 14:26:10 -0800223 double maxValue =
224 getSensorDataValue(pdr->sensor_data_size, pdr->max_readable);
225 double minValue =
226 getSensorDataValue(pdr->sensor_data_size, pdr->min_readable);
227 hysteresis = getSensorDataValue(pdr->sensor_data_size, pdr->hysteresis);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000228
229 bool hasCriticalThresholds = false;
230 bool hasWarningThresholds = false;
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800231 bool hasFatalThresholds = false;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000232 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
233 double criticalLow = std::numeric_limits<double>::quiet_NaN();
234 double warningHigh = std::numeric_limits<double>::quiet_NaN();
235 double warningLow = std::numeric_limits<double>::quiet_NaN();
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800236 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
237 double fatalLow = std::numeric_limits<double>::quiet_NaN();
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000238
239 if (pdr->supported_thresholds.bits.bit0)
240 {
241 hasWarningThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800242 warningHigh =
243 getRangeFieldValue(pdr->range_field_format, pdr->warning_high);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000244 }
245
246 if (pdr->supported_thresholds.bits.bit3)
247 {
248 hasWarningThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800249 warningLow =
250 getRangeFieldValue(pdr->range_field_format, pdr->warning_low);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000251 }
252
253 if (pdr->supported_thresholds.bits.bit1)
254 {
255 hasCriticalThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800256 criticalHigh =
257 getRangeFieldValue(pdr->range_field_format, pdr->critical_high);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000258 }
259
260 if (pdr->supported_thresholds.bits.bit4)
261 {
262 hasCriticalThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800263 criticalLow =
264 getRangeFieldValue(pdr->range_field_format, pdr->critical_low);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000265 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800266 if (pdr->supported_thresholds.bits.bit2)
267 {
268 hasFatalThresholds = true;
269 fatalHigh =
270 getRangeFieldValue(pdr->range_field_format, pdr->fatal_high);
271 }
272 if (pdr->supported_thresholds.bits.bit5)
273 {
274 hasFatalThresholds = true;
275 fatalLow = getRangeFieldValue(pdr->range_field_format, pdr->fatal_low);
276 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000277
278 resolution = pdr->resolution;
279 offset = pdr->offset;
280 baseUnitModifier = pdr->unit_modifier;
Gilbert Cheneac61a42022-02-23 20:56:19 +0000281 timeStamp = 0;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000282
283 /**
284 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
285 * updateTime is in microseconds
286 */
287 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
Ed Tanous0469b562025-01-06 12:35:10 -0800288 if (std::isfinite(pdr->update_interval))
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000289 {
290 updateTime = pdr->update_interval * 1000000;
291 }
292
Thu Nguyen6d615f12024-04-24 05:02:03 +0000293 if (!useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000294 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000295 try
296 {
297 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
298 }
299 catch (const sdbusplus::exception_t& e)
300 {
301 lg2::error(
302 "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}",
303 "PATH", path, "ERROR", e);
304 throw sdbusplus::xyz::openbmc_project::Common::Error::
305 InvalidArgument();
306 }
307 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
308 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
309 valueIntf->unit(sensorUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000310 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000311 else
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000312 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000313 try
314 {
315 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
316 }
317 catch (const sdbusplus::exception_t& e)
318 {
319 lg2::error(
320 "Failed to create Metric interface for numeric sensor {PATH} error - {ERROR}",
321 "PATH", path, "ERROR", e);
322 throw sdbusplus::xyz::openbmc_project::Common::Error::
323 InvalidArgument();
324 }
325 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
326 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
327 metricIntf->unit(metricUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000328 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000329
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000330 hysteresis = unitModifier(conversionFormula(hysteresis));
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000331
Chau Lyd197f092023-11-06 07:29:06 +0000332 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
333 pdr->entity_instance_num, pdr->container_id))
334 {
335 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
336 }
337
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000338 try
339 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400340 availabilityIntf =
341 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000342 }
343 catch (const sdbusplus::exception_t& e)
344 {
345 lg2::error(
346 "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}",
347 "PATH", path, "ERROR", e);
348 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
349 }
350 availabilityIntf->available(true);
351
352 try
353 {
354 operationalStatusIntf =
355 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
356 }
357 catch (const sdbusplus::exception_t& e)
358 {
359 lg2::error(
360 "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}",
361 "PATH", path, "ERROR", e);
362 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
363 }
364 operationalStatusIntf->functional(!sensorDisabled);
365
Thu Nguyen6d615f12024-04-24 05:02:03 +0000366 if (hasWarningThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000367 {
368 try
369 {
370 thresholdWarningIntf =
371 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
372 }
373 catch (const sdbusplus::exception_t& e)
374 {
375 lg2::error(
376 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}",
377 "PATH", path, "ERROR", e);
378 throw sdbusplus::xyz::openbmc_project::Common::Error::
379 InvalidArgument();
380 }
381 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
382 thresholdWarningIntf->warningLow(unitModifier(warningLow));
383 }
384
Thu Nguyen6d615f12024-04-24 05:02:03 +0000385 if (hasCriticalThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000386 {
387 try
388 {
389 thresholdCriticalIntf =
390 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
391 }
392 catch (const sdbusplus::exception_t& e)
393 {
394 lg2::error(
395 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}",
396 "PATH", path, "ERROR", e);
397 throw sdbusplus::xyz::openbmc_project::Common::Error::
398 InvalidArgument();
399 }
400 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
401 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
402 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800403
404 if (hasFatalThresholds && !useMetricInterface)
405 {
406 try
407 {
408 thresholdHardShutdownIntf =
409 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
410 }
411 catch (const sdbusplus::exception_t& e)
412 {
413 lg2::error(
414 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
415 "PATH", path, "ERROR", e);
416 throw sdbusplus::xyz::openbmc_project::Common::Error::
417 InvalidArgument();
418 }
419 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
420 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
421 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000422}
423
424NumericSensor::NumericSensor(
425 const pldm_tid_t tid, const bool sensorDisabled,
426 std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
427 std::string& sensorName, std::string& associationPath) :
Gilbert Cheneac61a42022-02-23 20:56:19 +0000428 tid(tid), sensorName(sensorName)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000429{
430 if (!pdr)
431 {
432 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
433 }
434
435 sensorId = pdr->sensor_id;
436 std::string path;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000437 MetricUnit metricUnit = MetricUnit::Count;
Amithash Prasad98256602024-12-19 14:02:30 -0800438 setSensorUnit(pdr->base_unit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000439
440 path = sensorNameSpace + sensorName;
441 try
442 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000443 std::string tmp{};
444 std::string interface = SENSOR_VALUE_INTF;
445 if (useMetricInterface)
446 {
447 interface = METRIC_VALUE_INTF;
448 }
449 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
450 interface.c_str());
451
452 if (!tmp.empty())
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000453 {
454 throw sdbusplus::xyz::openbmc_project::Common::Error::
455 TooManyResources();
456 }
457 }
458 catch (const std::exception&)
459 {
460 /* The sensor object path is not created */
461 }
462
463 auto& bus = pldm::utils::DBusHandler::getBus();
464 try
465 {
466 associationDefinitionsIntf =
467 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
468 }
469 catch (const sdbusplus::exception_t& e)
470 {
471 lg2::error(
472 "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}",
473 "PATH", path, "ERROR", e);
474 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
475 }
476 associationDefinitionsIntf->associations(
477 {{"chassis", "all_sensors", associationPath.c_str()}});
478
479 double maxValue = std::numeric_limits<double>::quiet_NaN();
480 double minValue = std::numeric_limits<double>::quiet_NaN();
481 bool hasWarningThresholds = false;
482 bool hasCriticalThresholds = false;
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800483 bool hasFatalThresholds = false;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000484 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
485 double criticalLow = std::numeric_limits<double>::quiet_NaN();
486 double warningHigh = std::numeric_limits<double>::quiet_NaN();
487 double warningLow = std::numeric_limits<double>::quiet_NaN();
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800488 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
489 double fatalLow = std::numeric_limits<double>::quiet_NaN();
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000490
491 if (pdr->range_field_support.bits.bit0)
492 {
493 hasWarningThresholds = true;
494 warningHigh = pdr->warning_high;
495 }
496 if (pdr->range_field_support.bits.bit1)
497 {
498 hasWarningThresholds = true;
499 warningLow = pdr->warning_low;
500 }
501
502 if (pdr->range_field_support.bits.bit2)
503 {
504 hasCriticalThresholds = true;
505 criticalHigh = pdr->critical_high;
506 }
507
508 if (pdr->range_field_support.bits.bit3)
509 {
510 hasCriticalThresholds = true;
511 criticalLow = pdr->critical_low;
512 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800513 if (pdr->range_field_support.bits.bit4)
514 {
515 hasFatalThresholds = true;
516 fatalHigh = pdr->fatal_high;
517 }
518 if (pdr->range_field_support.bits.bit5)
519 {
520 hasFatalThresholds = true;
521 fatalLow = pdr->fatal_low;
522 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000523
524 resolution = std::numeric_limits<double>::quiet_NaN();
525 offset = std::numeric_limits<double>::quiet_NaN();
526 baseUnitModifier = pdr->unit_modifier;
Gilbert Cheneac61a42022-02-23 20:56:19 +0000527 timeStamp = 0;
Thu Nguyen2027ff52024-10-03 21:58:22 +0000528 hysteresis = 0;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000529
530 /**
531 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
532 * updateTime is in microseconds
533 */
534 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000535
536 if (!useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000537 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000538 try
539 {
540 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
541 }
542 catch (const sdbusplus::exception_t& e)
543 {
544 lg2::error(
545 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
546 "PATH", path, "ERROR", e);
547 throw sdbusplus::xyz::openbmc_project::Common::Error::
548 InvalidArgument();
549 }
550 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
551 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
552 valueIntf->unit(sensorUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000553 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000554 else
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000555 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000556 try
557 {
558 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
559 }
560 catch (const sdbusplus::exception_t& e)
561 {
562 lg2::error(
563 "Failed to create Metric interface for compact numeric sensor {PATH} error - {ERROR}",
564 "PATH", path, "ERROR", e);
565 throw sdbusplus::xyz::openbmc_project::Common::Error::
566 InvalidArgument();
567 }
568 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
569 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
570 metricIntf->unit(metricUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000571 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000572
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000573 hysteresis = unitModifier(conversionFormula(hysteresis));
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000574
Chau Lyd197f092023-11-06 07:29:06 +0000575 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
576 pdr->entity_instance, pdr->container_id))
577 {
578 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
579 }
580
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000581 try
582 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400583 availabilityIntf =
584 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000585 }
586 catch (const sdbusplus::exception_t& e)
587 {
588 lg2::error(
589 "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
590 "PATH", path, "ERROR", e);
591 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
592 }
593 availabilityIntf->available(true);
594
595 try
596 {
597 operationalStatusIntf =
598 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
599 }
600 catch (const sdbusplus::exception_t& e)
601 {
602 lg2::error(
603 "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
604 "PATH", path, "ERROR", e);
605 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
606 }
607 operationalStatusIntf->functional(!sensorDisabled);
608
Thu Nguyen6d615f12024-04-24 05:02:03 +0000609 if (hasWarningThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000610 {
611 try
612 {
613 thresholdWarningIntf =
614 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
615 }
616 catch (const sdbusplus::exception_t& e)
617 {
618 lg2::error(
619 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
620 "PATH", path, "ERROR", e);
621 throw sdbusplus::xyz::openbmc_project::Common::Error::
622 InvalidArgument();
623 }
624 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
625 thresholdWarningIntf->warningLow(unitModifier(warningLow));
626 }
627
Thu Nguyen6d615f12024-04-24 05:02:03 +0000628 if (hasCriticalThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000629 {
630 try
631 {
632 thresholdCriticalIntf =
633 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
634 }
635 catch (const sdbusplus::exception_t& e)
636 {
637 lg2::error(
638 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}",
639 "PATH", path, "ERROR", e);
640 throw sdbusplus::xyz::openbmc_project::Common::Error::
641 InvalidArgument();
642 }
643 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
644 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
645 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800646
647 if (hasFatalThresholds && !useMetricInterface)
648 {
649 try
650 {
651 thresholdHardShutdownIntf =
652 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
653 }
654 catch (const sdbusplus::exception_t& e)
655 {
656 lg2::error(
657 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
658 "PATH", path, "ERROR", e);
659 throw sdbusplus::xyz::openbmc_project::Common::Error::
660 InvalidArgument();
661 }
662 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
663 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
664 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000665}
666
667double NumericSensor::conversionFormula(double value)
668{
669 double convertedValue = value;
Ed Tanous0469b562025-01-06 12:35:10 -0800670 if (std::isfinite(resolution))
671 {
672 convertedValue *= resolution;
673 }
674 if (std::isfinite(offset))
675 {
676 convertedValue += offset;
677 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000678 return convertedValue;
679}
680
681double NumericSensor::unitModifier(double value)
682{
Ed Tanous0469b562025-01-06 12:35:10 -0800683 if (!std::isfinite(value))
684 {
685 return value;
686 }
687 return value * std::pow(10, baseUnitModifier);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000688}
Gilbert Cheneac61a42022-02-23 20:56:19 +0000689
690void NumericSensor::updateReading(bool available, bool functional, double value)
691{
Thu Nguyen6d615f12024-04-24 05:02:03 +0000692 if (!availabilityIntf || !operationalStatusIntf ||
693 (!useMetricInterface && !valueIntf) ||
694 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000695 {
696 lg2::error(
697 "Failed to update sensor {NAME} D-Bus interface don't exist.",
698 "NAME", sensorName);
699 return;
700 }
701 availabilityIntf->available(available);
702 operationalStatusIntf->functional(functional);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000703 double curValue = 0;
704 if (!useMetricInterface)
705 {
706 curValue = valueIntf->value();
707 }
708 else
709 {
710 curValue = metricIntf->value();
711 }
712
Gilbert Cheneac61a42022-02-23 20:56:19 +0000713 double newValue = std::numeric_limits<double>::quiet_NaN();
714 if (functional && available)
715 {
716 newValue = unitModifier(conversionFormula(value));
717 if (newValue != curValue &&
Ed Tanous0469b562025-01-06 12:35:10 -0800718 (std::isfinite(newValue) || std::isfinite(curValue)))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000719 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000720 if (!useMetricInterface)
721 {
722 valueIntf->value(newValue);
723 updateThresholds();
724 }
725 else
726 {
727 metricIntf->value(newValue);
728 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000729 }
730 }
731 else
732 {
733 if (newValue != curValue &&
Ed Tanous0469b562025-01-06 12:35:10 -0800734 (std::isfinite(newValue) || std::isfinite(curValue)))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000735 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000736 if (!useMetricInterface)
737 {
738 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
739 }
740 else
741 {
742 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
743 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000744 }
745 }
746}
747
748void NumericSensor::handleErrGetSensorReading()
749{
Thu Nguyen6d615f12024-04-24 05:02:03 +0000750 if (!operationalStatusIntf || (!useMetricInterface && !valueIntf) ||
751 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000752 {
753 lg2::error(
754 "Failed to update sensor {NAME} D-Bus interfaces don't exist.",
755 "NAME", sensorName);
756 return;
757 }
758 operationalStatusIntf->functional(false);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000759 if (!useMetricInterface)
760 {
761 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
762 }
763 else
764 {
765 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
766 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000767}
768
769bool NumericSensor::checkThreshold(bool alarm, bool direction, double value,
770 double threshold, double hyst)
771{
772 if (direction)
773 {
774 if (value >= threshold)
775 {
776 return true;
777 }
778 if (value < (threshold - hyst))
779 {
780 return false;
781 }
782 }
783 else
784 {
785 if (value <= threshold)
786 {
787 return true;
788 }
789 if (value > (threshold + hyst))
790 {
791 return false;
792 }
793 }
794 return alarm;
795}
Amithash Prasad79931b62025-05-01 15:34:59 -0700796
797bool NumericSensor::hasThresholdAlarm()
798{
799 bool alarm = false;
800 for (auto level : allThresholdLevels)
801 {
802 for (auto direction : allThresholdDirections)
803 {
804 alarm |= getThresholdAlarm(level, direction);
805 }
806 }
807 return alarm;
808}
809
Amithash Prasad2480c572025-05-01 15:34:34 -0700810void NumericSensor::setWarningThresholdAlarm(pldm::utils::Direction direction,
811 double value, bool newAlarm)
812{
813 if (direction == pldm::utils::Direction::HIGH)
814 {
815 thresholdWarningIntf->warningAlarmHigh(newAlarm);
816 if (newAlarm)
817 {
818 thresholdWarningIntf->warningHighAlarmAsserted(value);
819 }
820 else
821 {
822 thresholdWarningIntf->warningHighAlarmDeasserted(value);
823 }
824 }
825 else
826 {
827 thresholdWarningIntf->warningAlarmLow(newAlarm);
828 if (newAlarm)
829 {
830 thresholdWarningIntf->warningLowAlarmAsserted(value);
831 }
832 else
833 {
834 thresholdWarningIntf->warningLowAlarmDeasserted(value);
835 }
836 }
837}
838
839void NumericSensor::setCriticalThresholdAlarm(pldm::utils::Direction direction,
840 double value, bool newAlarm)
841{
842 if (direction == pldm::utils::Direction::HIGH)
843 {
844 thresholdCriticalIntf->criticalAlarmHigh(newAlarm);
845 if (newAlarm)
846 {
847 thresholdCriticalIntf->criticalHighAlarmAsserted(value);
848 }
849 else
850 {
851 thresholdCriticalIntf->criticalHighAlarmDeasserted(value);
852 }
853 }
854 else
855 {
856 thresholdCriticalIntf->criticalAlarmLow(newAlarm);
857 if (newAlarm)
858 {
859 thresholdCriticalIntf->criticalLowAlarmAsserted(value);
860 }
861 else
862 {
863 thresholdCriticalIntf->criticalLowAlarmDeasserted(value);
864 }
865 }
866}
867
868void NumericSensor::setHardShutdownThresholdAlarm(
869 pldm::utils::Direction direction, double value, bool newAlarm)
870{
871 if (direction == pldm::utils::Direction::HIGH)
872 {
873 thresholdHardShutdownIntf->hardShutdownAlarmHigh(newAlarm);
874 if (newAlarm)
875 {
876 thresholdHardShutdownIntf->hardShutdownHighAlarmAsserted(value);
877 }
878 else
879 {
880 thresholdHardShutdownIntf->hardShutdownHighAlarmDeasserted(value);
881 }
882 }
883 else
884 {
885 thresholdHardShutdownIntf->hardShutdownAlarmLow(newAlarm);
886 if (newAlarm)
887 {
888 thresholdHardShutdownIntf->hardShutdownLowAlarmAsserted(value);
889 }
890 else
891 {
892 thresholdHardShutdownIntf->hardShutdownLowAlarmDeasserted(value);
893 }
894 }
895}
896
897int NumericSensor::setThresholdAlarm(pldm::utils::Level level,
898 pldm::utils::Direction direction,
899 double value, bool newAlarm)
900{
901 if (!isThresholdValid(level, direction))
902 {
903 lg2::error(
904 "Error:Trigger sensor warning event for non warning threshold sensors {NAME}",
905 "NAME", sensorName);
906 return PLDM_ERROR;
907 }
908 auto alarm = getThresholdAlarm(level, direction);
909 if (alarm == newAlarm)
910 {
911 return PLDM_SUCCESS;
912 }
913 switch (level)
914 {
915 case pldm::utils::Level::WARNING:
916 setWarningThresholdAlarm(direction, value, newAlarm);
917 break;
918 case pldm::utils::Level::CRITICAL:
919 setCriticalThresholdAlarm(direction, value, newAlarm);
920 break;
921 case pldm::utils::Level::HARDSHUTDOWN:
922 setHardShutdownThresholdAlarm(direction, value, newAlarm);
923 break;
924 default:
925 return PLDM_ERROR;
926 }
Amithash Prasad79931b62025-05-01 15:34:59 -0700927 if (newAlarm)
928 {
929 createThresholdLog(level, direction, value);
930 }
931 else
932 {
933 auto& log = assertedLog[{level, direction}];
934 if (log.has_value())
935 {
936 clearThresholdLog(log);
937 }
938 // If all alarms have cleared. Log normal range.
939 if (!hasThresholdAlarm())
940 {
941 createNormalRangeLog(value);
942 }
943 }
Amithash Prasad2480c572025-05-01 15:34:34 -0700944 return PLDM_SUCCESS;
945}
Gilbert Cheneac61a42022-02-23 20:56:19 +0000946
Amithash Prasad79931b62025-05-01 15:34:59 -0700947void NumericSensor::clearThresholdLog(
948 std::optional<sdbusplus::message::object_path>& log)
949{
950 if (!log)
951 {
952 return;
953 }
954 try
955 {
956 /* empty log entries are returned by commit() if the
957 requested log is being filtered out */
958 if (!log->str.empty())
959 {
960 lg2::resolve(*log);
961 }
962 }
963 catch (std::exception& ec)
964 {
965 lg2::error("Error trying to resolve: {LOG} : {ERROR}", "LOG", log->str,
966 "ERROR", ec);
967 }
968 log.reset();
969}
970
971/** @brief helper function template to create a threshold log
972 *
973 * @tparam[in] errorObj - The error object of the log we want to create.
974 * @param[in] sensorObjPath - The object path of the sensor.
975 * @param[in] value - The current value of the sensor.
976 * @param[in] sensorUnit - The units of the sensor.
977 * @param[in] threshold - The threshold value.
978 *
979 * @return optional object holding the object path of the created
980 * log entry. If the log entry is being filtered, we would return
981 * a optional holding an empty string in the object path. This ensures
982 * we follow our state machine properly even if the log is being filtered.
983 */
984template <typename errorObj>
985auto logThresholdHelper(const std::string& sensorObjPath, double value,
986 SensorUnit sensorUnit, double threshold)
987 -> std::optional<sdbusplus::message::object_path>
988{
989 return lg2::commit(
990 errorObj("SENSOR_NAME", sensorObjPath, "READING_VALUE", value, "UNITS",
991 sensorUnit, "THRESHOLD_VALUE", threshold));
992}
993
994void NumericSensor::createThresholdLog(
995 pldm::utils::Level level, pldm::utils::Direction direction, double value)
996{
997 namespace Errors =
998 sdbusplus::error::xyz::openbmc_project::sensor::Threshold;
999 /* Map from threshold level+direction to a an instantiation of
1000 * logThresholdHelper with the required error object class */
1001 static const std::map<
1002 std::tuple<pldm::utils::Level, pldm::utils::Direction>,
1003 std::function<std::optional<sdbusplus::message::object_path>(
1004 const std::string&, double, SensorUnit, double)>>
1005 thresholdEventMap = {
1006 {{pldm::utils::Level::WARNING, pldm::utils::Direction::HIGH},
1007 &logThresholdHelper<Errors::ReadingAboveUpperWarningThreshold>},
1008 {{pldm::utils::Level::WARNING, pldm::utils::Direction::LOW},
1009 &logThresholdHelper<Errors::ReadingBelowLowerWarningThreshold>},
1010 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::HIGH},
1011 &logThresholdHelper<Errors::ReadingAboveUpperCriticalThreshold>},
1012 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::LOW},
1013 &logThresholdHelper<Errors::ReadingBelowLowerCriticalThreshold>},
1014 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::HIGH},
1015 &logThresholdHelper<
1016 Errors::ReadingAboveUpperHardShutdownThreshold>},
1017 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::LOW},
1018 &logThresholdHelper<
1019 Errors::ReadingBelowLowerHardShutdownThreshold>},
1020 };
1021
1022 std::string sensorObjPath = sensorNameSpace + sensorName;
1023 double threshold = getThreshold(level, direction);
1024 try
1025 {
1026 auto helper = thresholdEventMap.at({level, direction});
1027 assertedLog[{level, direction}] =
1028 helper(sensorObjPath, value, sensorUnit, threshold);
1029 }
1030 catch (std::exception& ec)
1031 {
1032 lg2::error(
1033 "Unable to create threshold log entry for {OBJPATH}: {ERROR}",
1034 "OBJPATH", sensorObjPath, "ERROR", ec);
1035 }
1036}
1037
1038void NumericSensor::createNormalRangeLog(double value)
1039{
1040 namespace Events =
1041 sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
1042 std::string objPath = sensorNameSpace + sensorName;
1043 try
1044 {
1045 lg2::commit(Events::SensorReadingNormalRange(
1046 "SENSOR_NAME", objPath, "READING_VALUE", value, "UNITS",
1047 sensorUnit));
1048 }
1049 catch (std::exception& ec)
1050 {
1051 lg2::error(
1052 "Unable to create SensorReadingNormalRange log entry for {OBJPATH}: {ERROR}",
1053 "OBJPATH", objPath, "ERROR", ec);
1054 }
1055}
1056
Gilbert Cheneac61a42022-02-23 20:56:19 +00001057void NumericSensor::updateThresholds()
1058{
Thu Nguyen6d615f12024-04-24 05:02:03 +00001059 double value = std::numeric_limits<double>::quiet_NaN();
1060
1061 if ((!useMetricInterface && !valueIntf) ||
1062 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +00001063 {
1064 lg2::error(
1065 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1066 "NAME", sensorName);
1067 return;
1068 }
Thu Nguyen6d615f12024-04-24 05:02:03 +00001069 if (!useMetricInterface)
1070 {
1071 value = valueIntf->value();
1072 }
1073 else
1074 {
1075 value = metricIntf->value();
1076 }
Gilbert Cheneac61a42022-02-23 20:56:19 +00001077
Amithash Prasad2480c572025-05-01 15:34:34 -07001078 for (auto level : allThresholdLevels)
Gilbert Cheneac61a42022-02-23 20:56:19 +00001079 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001080 for (auto direction : allThresholdDirections)
Gilbert Cheneac61a42022-02-23 20:56:19 +00001081 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001082 auto threshold = getThreshold(level, direction);
1083 if (!std::isfinite(threshold))
Gilbert Cheneac61a42022-02-23 20:56:19 +00001084 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001085 continue;
Gilbert Cheneac61a42022-02-23 20:56:19 +00001086 }
Amithash Prasad2480c572025-05-01 15:34:34 -07001087 auto alarm = getThresholdAlarm(level, direction);
1088 auto newAlarm =
1089 checkThreshold(alarm, direction == pldm::utils::Direction::HIGH,
1090 value, threshold, hysteresis);
1091 setThresholdAlarm(level, direction, value, newAlarm);
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +08001092 }
1093 }
Gilbert Cheneac61a42022-02-23 20:56:19 +00001094}
Gilbert Chen77e6fe72024-08-06 09:23:30 +00001095
1096int NumericSensor::triggerThresholdEvent(
1097 pldm::utils::Level eventType, pldm::utils::Direction direction,
1098 double rawValue, bool newAlarm, bool assert)
1099{
1100 if (!valueIntf)
1101 {
1102 lg2::error(
1103 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1104 "NAME", sensorName);
1105 return PLDM_ERROR;
1106 }
1107
1108 auto value = unitModifier(conversionFormula(rawValue));
1109 lg2::error(
1110 "triggerThresholdEvent eventType {TID}, direction {SID} value {VAL} newAlarm {PSTATE} assert {ESTATE}",
1111 "TID", eventType, "SID", direction, "VAL", value, "PSTATE", newAlarm,
1112 "ESTATE", assert);
1113
Amithash Prasad2480c572025-05-01 15:34:34 -07001114 return setThresholdAlarm(eventType, direction, value, newAlarm);
Gilbert Chen77e6fe72024-08-06 09:23:30 +00001115}
Thu Nguyen3c5486d2024-08-01 08:03:08 +00001116} // namespace platform_mc
1117} // namespace pldm