blob: cfbb4d39b04e7de2c2a9bc1fbabf0bb0d3cab56f [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>
Amithash Prasad79931b62025-05-01 15:34:59 -07009#include <xyz/openbmc_project/Logging/Entry/client.hpp>
10#include <xyz/openbmc_project/Sensor/Threshold/event.hpp>
11
Thu Nguyen3c5486d2024-08-01 08:03:08 +000012#include <limits>
13#include <regex>
14
15PHOSPHOR_LOG2_USING;
16
17namespace pldm
18{
19namespace platform_mc
20{
Amithash Prasad79931b62025-05-01 15:34:59 -070021
22// This allows code to cleanly iterate through all supported
23// threshold levels and directions.
Amithash Prasad2480c572025-05-01 15:34:34 -070024static const std::array<pldm::utils::Level, 3> allThresholdLevels = {
25 pldm::utils::Level::WARNING, pldm::utils::Level::CRITICAL,
26 pldm::utils::Level::HARDSHUTDOWN};
27static const std::array<pldm::utils::Direction, 2> allThresholdDirections = {
28 pldm::utils::Direction::HIGH, pldm::utils::Direction::LOW};
Thu Nguyen3c5486d2024-08-01 08:03:08 +000029
Chau Lyd197f092023-11-06 07:29:06 +000030inline bool NumericSensor::createInventoryPath(
31 const std::string& associationPath, const std::string& sensorName,
32 const uint16_t entityType, const uint16_t entityInstanceNum,
33 const uint16_t containerId)
34{
35 auto& bus = pldm::utils::DBusHandler::getBus();
36 std::string invPath = associationPath + "/" + sensorName;
37 try
38 {
39 entityIntf = std::make_unique<EntityIntf>(bus, invPath.c_str());
40 }
41 catch (const sdbusplus::exception_t& e)
42 {
43 lg2::error(
44 "Failed to create Entity interface for compact numeric sensor {PATH} error - {ERROR}",
45 "PATH", invPath, "ERROR", e);
46 return false;
47 }
48 entityIntf->entityType(entityType);
49 entityIntf->entityInstanceNumber(entityInstanceNum);
50 entityIntf->containerID(containerId);
51
52 return true;
53}
54
Amithash Prasada1871172024-12-19 14:26:10 -080055inline double getSensorDataValue(uint8_t sensor_data_size,
56 union_sensor_data_size& value)
57{
58 double ret = std::numeric_limits<double>::quiet_NaN();
59 switch (sensor_data_size)
60 {
61 case PLDM_SENSOR_DATA_SIZE_UINT8:
62 ret = value.value_u8;
63 break;
64 case PLDM_SENSOR_DATA_SIZE_SINT8:
65 ret = value.value_s8;
66 break;
67 case PLDM_SENSOR_DATA_SIZE_UINT16:
68 ret = value.value_u16;
69 break;
70 case PLDM_SENSOR_DATA_SIZE_SINT16:
71 ret = value.value_s16;
72 break;
73 case PLDM_SENSOR_DATA_SIZE_UINT32:
74 ret = value.value_u32;
75 break;
76 case PLDM_SENSOR_DATA_SIZE_SINT32:
77 ret = value.value_s32;
78 break;
79 }
80 return ret;
81}
82
83inline double getRangeFieldValue(uint8_t range_field_format,
84 union_range_field_format& value)
85{
86 double ret = std::numeric_limits<double>::quiet_NaN();
87 switch (range_field_format)
88 {
89 case PLDM_RANGE_FIELD_FORMAT_UINT8:
90 ret = value.value_u8;
91 break;
92 case PLDM_RANGE_FIELD_FORMAT_SINT8:
93 ret = value.value_s8;
94 break;
95 case PLDM_RANGE_FIELD_FORMAT_UINT16:
96 ret = value.value_u16;
97 break;
98 case PLDM_RANGE_FIELD_FORMAT_SINT16:
99 ret = value.value_s16;
100 break;
101 case PLDM_RANGE_FIELD_FORMAT_UINT32:
102 ret = value.value_u32;
103 break;
104 case PLDM_RANGE_FIELD_FORMAT_SINT32:
105 ret = value.value_s32;
106 break;
107 case PLDM_RANGE_FIELD_FORMAT_REAL32:
108 ret = value.value_f32;
109 break;
110 }
111 return ret;
112}
113
Amithash Prasad98256602024-12-19 14:02:30 -0800114void NumericSensor::setSensorUnit(uint8_t baseUnit)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000115{
Amithash Prasad98256602024-12-19 14:02:30 -0800116 sensorUnit = SensorUnit::DegreesC;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000117 useMetricInterface = false;
Amithash Prasad98256602024-12-19 14:02:30 -0800118 switch (baseUnit)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000119 {
120 case PLDM_SENSOR_UNIT_DEGRESS_C:
121 sensorNameSpace = "/xyz/openbmc_project/sensors/temperature/";
122 sensorUnit = SensorUnit::DegreesC;
123 break;
124 case PLDM_SENSOR_UNIT_VOLTS:
125 sensorNameSpace = "/xyz/openbmc_project/sensors/voltage/";
126 sensorUnit = SensorUnit::Volts;
127 break;
128 case PLDM_SENSOR_UNIT_AMPS:
129 sensorNameSpace = "/xyz/openbmc_project/sensors/current/";
130 sensorUnit = SensorUnit::Amperes;
131 break;
132 case PLDM_SENSOR_UNIT_RPM:
133 sensorNameSpace = "/xyz/openbmc_project/sensors/fan_pwm/";
134 sensorUnit = SensorUnit::RPMS;
135 break;
136 case PLDM_SENSOR_UNIT_WATTS:
137 sensorNameSpace = "/xyz/openbmc_project/sensors/power/";
138 sensorUnit = SensorUnit::Watts;
139 break;
140 case PLDM_SENSOR_UNIT_JOULES:
141 sensorNameSpace = "/xyz/openbmc_project/sensors/energy/";
142 sensorUnit = SensorUnit::Joules;
143 break;
144 case PLDM_SENSOR_UNIT_PERCENTAGE:
145 sensorNameSpace = "/xyz/openbmc_project/sensors/utilization/";
146 sensorUnit = SensorUnit::Percent;
147 break;
Aditya Kurdunkar1507b1d2025-08-21 13:26:13 +0530148 case PLDM_SENSOR_UNIT_HERTZ:
149 sensorNameSpace = "/xyz/openbmc_project/sensors/frequency/";
150 sensorUnit = SensorUnit::Hertz;
151 break;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000152 case PLDM_SENSOR_UNIT_COUNTS:
153 case PLDM_SENSOR_UNIT_CORRECTED_ERRORS:
154 case PLDM_SENSOR_UNIT_UNCORRECTABLE_ERRORS:
155 sensorNameSpace = "/xyz/openbmc_project/metric/count/";
156 useMetricInterface = true;
157 break;
158 case PLDM_SENSOR_UNIT_OEMUNIT:
159 sensorNameSpace = "/xyz/openbmc_project/metric/oem/";
160 useMetricInterface = true;
161 break;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000162 default:
163 lg2::error("Sensor {NAME} has Invalid baseUnit {UNIT}.", "NAME",
Amithash Prasad98256602024-12-19 14:02:30 -0800164 sensorName, "UNIT", baseUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000165 throw sdbusplus::xyz::openbmc_project::Common::Error::
166 InvalidArgument();
167 break;
168 }
Amithash Prasad98256602024-12-19 14:02:30 -0800169}
170
171NumericSensor::NumericSensor(
172 const pldm_tid_t tid, const bool sensorDisabled,
173 std::shared_ptr<pldm_numeric_sensor_value_pdr> pdr, std::string& sensorName,
174 std::string& associationPath) : tid(tid), sensorName(sensorName)
175{
176 if (!pdr)
177 {
178 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
179 }
180
181 sensorId = pdr->sensor_id;
182 std::string path;
183 MetricUnit metricUnit = MetricUnit::Count;
184 setSensorUnit(pdr->base_unit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000185
186 path = sensorNameSpace + sensorName;
187 try
188 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000189 std::string tmp{};
190 std::string interface = SENSOR_VALUE_INTF;
191 if (useMetricInterface)
192 {
193 interface = METRIC_VALUE_INTF;
194 }
195 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
196 interface.c_str());
197
198 if (!tmp.empty())
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000199 {
200 throw sdbusplus::xyz::openbmc_project::Common::Error::
201 TooManyResources();
202 }
203 }
204 catch (const std::exception&)
205 {
206 /* The sensor object path is not created */
207 }
208
209 auto& bus = pldm::utils::DBusHandler::getBus();
210 try
211 {
212 associationDefinitionsIntf =
213 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
214 }
215 catch (const sdbusplus::exception_t& e)
216 {
217 lg2::error(
218 "Failed to create association interface for numeric sensor {PATH} error - {ERROR}",
219 "PATH", path, "ERROR", e);
220 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
221 }
222
223 associationDefinitionsIntf->associations(
224 {{"chassis", "all_sensors", associationPath}});
225
Amithash Prasada1871172024-12-19 14:26:10 -0800226 double maxValue =
227 getSensorDataValue(pdr->sensor_data_size, pdr->max_readable);
228 double minValue =
229 getSensorDataValue(pdr->sensor_data_size, pdr->min_readable);
230 hysteresis = getSensorDataValue(pdr->sensor_data_size, pdr->hysteresis);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000231
232 bool hasCriticalThresholds = false;
233 bool hasWarningThresholds = false;
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800234 bool hasFatalThresholds = false;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000235 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
236 double criticalLow = std::numeric_limits<double>::quiet_NaN();
237 double warningHigh = std::numeric_limits<double>::quiet_NaN();
238 double warningLow = std::numeric_limits<double>::quiet_NaN();
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800239 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
240 double fatalLow = std::numeric_limits<double>::quiet_NaN();
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000241
242 if (pdr->supported_thresholds.bits.bit0)
243 {
244 hasWarningThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800245 warningHigh =
246 getRangeFieldValue(pdr->range_field_format, pdr->warning_high);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000247 }
248
249 if (pdr->supported_thresholds.bits.bit3)
250 {
251 hasWarningThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800252 warningLow =
253 getRangeFieldValue(pdr->range_field_format, pdr->warning_low);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000254 }
255
256 if (pdr->supported_thresholds.bits.bit1)
257 {
258 hasCriticalThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800259 criticalHigh =
260 getRangeFieldValue(pdr->range_field_format, pdr->critical_high);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000261 }
262
263 if (pdr->supported_thresholds.bits.bit4)
264 {
265 hasCriticalThresholds = true;
Amithash Prasada1871172024-12-19 14:26:10 -0800266 criticalLow =
267 getRangeFieldValue(pdr->range_field_format, pdr->critical_low);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000268 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800269 if (pdr->supported_thresholds.bits.bit2)
270 {
271 hasFatalThresholds = true;
272 fatalHigh =
273 getRangeFieldValue(pdr->range_field_format, pdr->fatal_high);
274 }
275 if (pdr->supported_thresholds.bits.bit5)
276 {
277 hasFatalThresholds = true;
278 fatalLow = getRangeFieldValue(pdr->range_field_format, pdr->fatal_low);
279 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000280
281 resolution = pdr->resolution;
282 offset = pdr->offset;
283 baseUnitModifier = pdr->unit_modifier;
Gilbert Cheneac61a42022-02-23 20:56:19 +0000284 timeStamp = 0;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000285
286 /**
287 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
288 * updateTime is in microseconds
289 */
290 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
Ed Tanous0469b562025-01-06 12:35:10 -0800291 if (std::isfinite(pdr->update_interval))
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000292 {
293 updateTime = pdr->update_interval * 1000000;
294 }
295
Thu Nguyen6d615f12024-04-24 05:02:03 +0000296 if (!useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000297 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000298 try
299 {
300 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
301 }
302 catch (const sdbusplus::exception_t& e)
303 {
304 lg2::error(
305 "Failed to create Value interface for numeric sensor {PATH} error - {ERROR}",
306 "PATH", path, "ERROR", e);
307 throw sdbusplus::xyz::openbmc_project::Common::Error::
308 InvalidArgument();
309 }
310 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
311 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
312 valueIntf->unit(sensorUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000313 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000314 else
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000315 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000316 try
317 {
318 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
319 }
320 catch (const sdbusplus::exception_t& e)
321 {
322 lg2::error(
323 "Failed to create Metric interface for numeric sensor {PATH} error - {ERROR}",
324 "PATH", path, "ERROR", e);
325 throw sdbusplus::xyz::openbmc_project::Common::Error::
326 InvalidArgument();
327 }
328 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
329 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
330 metricIntf->unit(metricUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000331 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000332
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000333 hysteresis = unitModifier(conversionFormula(hysteresis));
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000334
Chau Lyd197f092023-11-06 07:29:06 +0000335 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
336 pdr->entity_instance_num, pdr->container_id))
337 {
338 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
339 }
340
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000341 try
342 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400343 availabilityIntf =
344 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000345 }
346 catch (const sdbusplus::exception_t& e)
347 {
348 lg2::error(
349 "Failed to create Availability interface for numeric sensor {PATH} error - {ERROR}",
350 "PATH", path, "ERROR", e);
351 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
352 }
353 availabilityIntf->available(true);
354
355 try
356 {
357 operationalStatusIntf =
358 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
359 }
360 catch (const sdbusplus::exception_t& e)
361 {
362 lg2::error(
363 "Failed to create Operation status interface for numeric sensor {PATH} error - {ERROR}",
364 "PATH", path, "ERROR", e);
365 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
366 }
367 operationalStatusIntf->functional(!sensorDisabled);
368
Thu Nguyen6d615f12024-04-24 05:02:03 +0000369 if (hasWarningThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000370 {
371 try
372 {
373 thresholdWarningIntf =
374 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
375 }
376 catch (const sdbusplus::exception_t& e)
377 {
378 lg2::error(
379 "Failed to create Threshold warning interface for numeric sensor {PATH} error - {ERROR}",
380 "PATH", path, "ERROR", e);
381 throw sdbusplus::xyz::openbmc_project::Common::Error::
382 InvalidArgument();
383 }
384 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
385 thresholdWarningIntf->warningLow(unitModifier(warningLow));
386 }
387
Thu Nguyen6d615f12024-04-24 05:02:03 +0000388 if (hasCriticalThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000389 {
390 try
391 {
392 thresholdCriticalIntf =
393 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
394 }
395 catch (const sdbusplus::exception_t& e)
396 {
397 lg2::error(
398 "Failed to create Threshold critical interface for numeric sensor {PATH} error - {ERROR}",
399 "PATH", path, "ERROR", e);
400 throw sdbusplus::xyz::openbmc_project::Common::Error::
401 InvalidArgument();
402 }
403 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
404 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
405 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800406
407 if (hasFatalThresholds && !useMetricInterface)
408 {
409 try
410 {
411 thresholdHardShutdownIntf =
412 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
413 }
414 catch (const sdbusplus::exception_t& e)
415 {
416 lg2::error(
417 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
418 "PATH", path, "ERROR", e);
419 throw sdbusplus::xyz::openbmc_project::Common::Error::
420 InvalidArgument();
421 }
422 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
423 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
424 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000425}
426
427NumericSensor::NumericSensor(
428 const pldm_tid_t tid, const bool sensorDisabled,
429 std::shared_ptr<pldm_compact_numeric_sensor_pdr> pdr,
430 std::string& sensorName, std::string& associationPath) :
Gilbert Cheneac61a42022-02-23 20:56:19 +0000431 tid(tid), sensorName(sensorName)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000432{
433 if (!pdr)
434 {
435 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
436 }
437
438 sensorId = pdr->sensor_id;
439 std::string path;
Thu Nguyen6d615f12024-04-24 05:02:03 +0000440 MetricUnit metricUnit = MetricUnit::Count;
Amithash Prasad98256602024-12-19 14:02:30 -0800441 setSensorUnit(pdr->base_unit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000442
443 path = sensorNameSpace + sensorName;
444 try
445 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000446 std::string tmp{};
447 std::string interface = SENSOR_VALUE_INTF;
448 if (useMetricInterface)
449 {
450 interface = METRIC_VALUE_INTF;
451 }
452 tmp = pldm::utils::DBusHandler().getService(path.c_str(),
453 interface.c_str());
454
455 if (!tmp.empty())
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000456 {
457 throw sdbusplus::xyz::openbmc_project::Common::Error::
458 TooManyResources();
459 }
460 }
461 catch (const std::exception&)
462 {
463 /* The sensor object path is not created */
464 }
465
466 auto& bus = pldm::utils::DBusHandler::getBus();
467 try
468 {
469 associationDefinitionsIntf =
470 std::make_unique<AssociationDefinitionsInft>(bus, path.c_str());
471 }
472 catch (const sdbusplus::exception_t& e)
473 {
474 lg2::error(
475 "Failed to create Association interface for compact numeric sensor {PATH} error - {ERROR}",
476 "PATH", path, "ERROR", e);
477 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
478 }
479 associationDefinitionsIntf->associations(
480 {{"chassis", "all_sensors", associationPath.c_str()}});
481
482 double maxValue = std::numeric_limits<double>::quiet_NaN();
483 double minValue = std::numeric_limits<double>::quiet_NaN();
484 bool hasWarningThresholds = false;
485 bool hasCriticalThresholds = false;
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800486 bool hasFatalThresholds = false;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000487 double criticalHigh = std::numeric_limits<double>::quiet_NaN();
488 double criticalLow = std::numeric_limits<double>::quiet_NaN();
489 double warningHigh = std::numeric_limits<double>::quiet_NaN();
490 double warningLow = std::numeric_limits<double>::quiet_NaN();
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800491 double fatalHigh = std::numeric_limits<double>::quiet_NaN();
492 double fatalLow = std::numeric_limits<double>::quiet_NaN();
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000493
494 if (pdr->range_field_support.bits.bit0)
495 {
496 hasWarningThresholds = true;
497 warningHigh = pdr->warning_high;
498 }
499 if (pdr->range_field_support.bits.bit1)
500 {
501 hasWarningThresholds = true;
502 warningLow = pdr->warning_low;
503 }
504
505 if (pdr->range_field_support.bits.bit2)
506 {
507 hasCriticalThresholds = true;
508 criticalHigh = pdr->critical_high;
509 }
510
511 if (pdr->range_field_support.bits.bit3)
512 {
513 hasCriticalThresholds = true;
514 criticalLow = pdr->critical_low;
515 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800516 if (pdr->range_field_support.bits.bit4)
517 {
518 hasFatalThresholds = true;
519 fatalHigh = pdr->fatal_high;
520 }
521 if (pdr->range_field_support.bits.bit5)
522 {
523 hasFatalThresholds = true;
524 fatalLow = pdr->fatal_low;
525 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000526
527 resolution = std::numeric_limits<double>::quiet_NaN();
528 offset = std::numeric_limits<double>::quiet_NaN();
529 baseUnitModifier = pdr->unit_modifier;
Gilbert Cheneac61a42022-02-23 20:56:19 +0000530 timeStamp = 0;
Thu Nguyen2027ff52024-10-03 21:58:22 +0000531 hysteresis = 0;
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000532
533 /**
534 * DEFAULT_SENSOR_UPDATER_INTERVAL is in milliseconds
535 * updateTime is in microseconds
536 */
537 updateTime = static_cast<uint64_t>(DEFAULT_SENSOR_UPDATER_INTERVAL * 1000);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000538
539 if (!useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000540 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000541 try
542 {
543 valueIntf = std::make_unique<ValueIntf>(bus, path.c_str());
544 }
545 catch (const sdbusplus::exception_t& e)
546 {
547 lg2::error(
548 "Failed to create Value interface for compact numeric sensor {PATH} error - {ERROR}",
549 "PATH", path, "ERROR", e);
550 throw sdbusplus::xyz::openbmc_project::Common::Error::
551 InvalidArgument();
552 }
553 valueIntf->maxValue(unitModifier(conversionFormula(maxValue)));
554 valueIntf->minValue(unitModifier(conversionFormula(minValue)));
555 valueIntf->unit(sensorUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000556 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000557 else
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000558 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000559 try
560 {
561 metricIntf = std::make_unique<MetricIntf>(bus, path.c_str());
562 }
563 catch (const sdbusplus::exception_t& e)
564 {
565 lg2::error(
566 "Failed to create Metric interface for compact numeric sensor {PATH} error - {ERROR}",
567 "PATH", path, "ERROR", e);
568 throw sdbusplus::xyz::openbmc_project::Common::Error::
569 InvalidArgument();
570 }
571 metricIntf->maxValue(unitModifier(conversionFormula(maxValue)));
572 metricIntf->minValue(unitModifier(conversionFormula(minValue)));
573 metricIntf->unit(metricUnit);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000574 }
Thu Nguyen6d615f12024-04-24 05:02:03 +0000575
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000576 hysteresis = unitModifier(conversionFormula(hysteresis));
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000577
Chau Lyd197f092023-11-06 07:29:06 +0000578 if (!createInventoryPath(associationPath, sensorName, pdr->entity_type,
579 pdr->entity_instance, pdr->container_id))
580 {
581 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
582 }
583
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000584 try
585 {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400586 availabilityIntf =
587 std::make_unique<AvailabilityIntf>(bus, path.c_str());
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000588 }
589 catch (const sdbusplus::exception_t& e)
590 {
591 lg2::error(
592 "Failed to create Availability interface for compact numeric sensor {PATH} error - {ERROR}",
593 "PATH", path, "ERROR", e);
594 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
595 }
596 availabilityIntf->available(true);
597
598 try
599 {
600 operationalStatusIntf =
601 std::make_unique<OperationalStatusIntf>(bus, path.c_str());
602 }
603 catch (const sdbusplus::exception_t& e)
604 {
605 lg2::error(
606 "Failed to create Operational status interface for compact numeric sensor {PATH} error - {ERROR}",
607 "PATH", path, "ERROR", e);
608 throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
609 }
610 operationalStatusIntf->functional(!sensorDisabled);
611
Thu Nguyen6d615f12024-04-24 05:02:03 +0000612 if (hasWarningThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000613 {
614 try
615 {
616 thresholdWarningIntf =
617 std::make_unique<ThresholdWarningIntf>(bus, path.c_str());
618 }
619 catch (const sdbusplus::exception_t& e)
620 {
621 lg2::error(
622 "Failed to create Warning threshold interface for compact numeric sensor {PATH} error - {ERROR}",
623 "PATH", path, "ERROR", e);
624 throw sdbusplus::xyz::openbmc_project::Common::Error::
625 InvalidArgument();
626 }
627 thresholdWarningIntf->warningHigh(unitModifier(warningHigh));
628 thresholdWarningIntf->warningLow(unitModifier(warningLow));
629 }
630
Thu Nguyen6d615f12024-04-24 05:02:03 +0000631 if (hasCriticalThresholds && !useMetricInterface)
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000632 {
633 try
634 {
635 thresholdCriticalIntf =
636 std::make_unique<ThresholdCriticalIntf>(bus, path.c_str());
637 }
638 catch (const sdbusplus::exception_t& e)
639 {
640 lg2::error(
641 "Failed to create Critical threshold interface for compact numeric sensor {PATH} error - {ERROR}",
642 "PATH", path, "ERROR", e);
643 throw sdbusplus::xyz::openbmc_project::Common::Error::
644 InvalidArgument();
645 }
646 thresholdCriticalIntf->criticalHigh(unitModifier(criticalHigh));
647 thresholdCriticalIntf->criticalLow(unitModifier(criticalLow));
648 }
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +0800649
650 if (hasFatalThresholds && !useMetricInterface)
651 {
652 try
653 {
654 thresholdHardShutdownIntf =
655 std::make_unique<ThresholdHardShutdownIntf>(bus, path.c_str());
656 }
657 catch (const sdbusplus::exception_t& e)
658 {
659 lg2::error(
660 "Failed to create HardShutdown threshold interface for numeric sensor {PATH} error - {ERROR}",
661 "PATH", path, "ERROR", e);
662 throw sdbusplus::xyz::openbmc_project::Common::Error::
663 InvalidArgument();
664 }
665 thresholdHardShutdownIntf->hardShutdownHigh(unitModifier(fatalHigh));
666 thresholdHardShutdownIntf->hardShutdownLow(unitModifier(fatalLow));
667 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000668}
669
670double NumericSensor::conversionFormula(double value)
671{
672 double convertedValue = value;
Ed Tanous0469b562025-01-06 12:35:10 -0800673 if (std::isfinite(resolution))
674 {
675 convertedValue *= resolution;
676 }
677 if (std::isfinite(offset))
678 {
679 convertedValue += offset;
680 }
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000681 return convertedValue;
682}
683
684double NumericSensor::unitModifier(double value)
685{
Ed Tanous0469b562025-01-06 12:35:10 -0800686 if (!std::isfinite(value))
687 {
688 return value;
689 }
690 return value * std::pow(10, baseUnitModifier);
Thu Nguyen3c5486d2024-08-01 08:03:08 +0000691}
Gilbert Cheneac61a42022-02-23 20:56:19 +0000692
693void NumericSensor::updateReading(bool available, bool functional, double value)
694{
Thu Nguyen6d615f12024-04-24 05:02:03 +0000695 if (!availabilityIntf || !operationalStatusIntf ||
696 (!useMetricInterface && !valueIntf) ||
697 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000698 {
699 lg2::error(
700 "Failed to update sensor {NAME} D-Bus interface don't exist.",
701 "NAME", sensorName);
702 return;
703 }
704 availabilityIntf->available(available);
705 operationalStatusIntf->functional(functional);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000706 double curValue = 0;
707 if (!useMetricInterface)
708 {
709 curValue = valueIntf->value();
710 }
711 else
712 {
713 curValue = metricIntf->value();
714 }
715
Gilbert Cheneac61a42022-02-23 20:56:19 +0000716 double newValue = std::numeric_limits<double>::quiet_NaN();
717 if (functional && available)
718 {
719 newValue = unitModifier(conversionFormula(value));
Peter Yin04d27282025-07-10 16:54:08 +0800720 if (std::isfinite(newValue) || std::isfinite(curValue))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000721 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000722 if (!useMetricInterface)
723 {
724 valueIntf->value(newValue);
725 updateThresholds();
726 }
727 else
728 {
729 metricIntf->value(newValue);
730 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000731 }
732 }
733 else
734 {
735 if (newValue != curValue &&
Ed Tanous0469b562025-01-06 12:35:10 -0800736 (std::isfinite(newValue) || std::isfinite(curValue)))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000737 {
Thu Nguyen6d615f12024-04-24 05:02:03 +0000738 if (!useMetricInterface)
739 {
740 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
741 }
742 else
743 {
744 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
745 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000746 }
747 }
748}
749
750void NumericSensor::handleErrGetSensorReading()
751{
Thu Nguyen6d615f12024-04-24 05:02:03 +0000752 if (!operationalStatusIntf || (!useMetricInterface && !valueIntf) ||
753 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +0000754 {
755 lg2::error(
756 "Failed to update sensor {NAME} D-Bus interfaces don't exist.",
757 "NAME", sensorName);
758 return;
759 }
760 operationalStatusIntf->functional(false);
Thu Nguyen6d615f12024-04-24 05:02:03 +0000761 if (!useMetricInterface)
762 {
763 valueIntf->value(std::numeric_limits<double>::quiet_NaN());
764 }
765 else
766 {
767 metricIntf->value(std::numeric_limits<double>::quiet_NaN());
768 }
Gilbert Cheneac61a42022-02-23 20:56:19 +0000769}
770
771bool NumericSensor::checkThreshold(bool alarm, bool direction, double value,
772 double threshold, double hyst)
773{
774 if (direction)
775 {
776 if (value >= threshold)
777 {
778 return true;
779 }
780 if (value < (threshold - hyst))
781 {
782 return false;
783 }
784 }
785 else
786 {
787 if (value <= threshold)
788 {
789 return true;
790 }
791 if (value > (threshold + hyst))
792 {
793 return false;
794 }
795 }
796 return alarm;
797}
Amithash Prasad79931b62025-05-01 15:34:59 -0700798
799bool NumericSensor::hasThresholdAlarm()
800{
801 bool alarm = false;
802 for (auto level : allThresholdLevels)
803 {
804 for (auto direction : allThresholdDirections)
805 {
806 alarm |= getThresholdAlarm(level, direction);
807 }
808 }
809 return alarm;
810}
811
Amithash Prasad2480c572025-05-01 15:34:34 -0700812void NumericSensor::setWarningThresholdAlarm(pldm::utils::Direction direction,
813 double value, bool newAlarm)
814{
815 if (direction == pldm::utils::Direction::HIGH)
816 {
817 thresholdWarningIntf->warningAlarmHigh(newAlarm);
818 if (newAlarm)
819 {
820 thresholdWarningIntf->warningHighAlarmAsserted(value);
821 }
822 else
823 {
824 thresholdWarningIntf->warningHighAlarmDeasserted(value);
825 }
826 }
827 else
828 {
829 thresholdWarningIntf->warningAlarmLow(newAlarm);
830 if (newAlarm)
831 {
832 thresholdWarningIntf->warningLowAlarmAsserted(value);
833 }
834 else
835 {
836 thresholdWarningIntf->warningLowAlarmDeasserted(value);
837 }
838 }
839}
840
841void NumericSensor::setCriticalThresholdAlarm(pldm::utils::Direction direction,
842 double value, bool newAlarm)
843{
844 if (direction == pldm::utils::Direction::HIGH)
845 {
846 thresholdCriticalIntf->criticalAlarmHigh(newAlarm);
847 if (newAlarm)
848 {
849 thresholdCriticalIntf->criticalHighAlarmAsserted(value);
850 }
851 else
852 {
853 thresholdCriticalIntf->criticalHighAlarmDeasserted(value);
854 }
855 }
856 else
857 {
858 thresholdCriticalIntf->criticalAlarmLow(newAlarm);
859 if (newAlarm)
860 {
861 thresholdCriticalIntf->criticalLowAlarmAsserted(value);
862 }
863 else
864 {
865 thresholdCriticalIntf->criticalLowAlarmDeasserted(value);
866 }
867 }
868}
869
870void NumericSensor::setHardShutdownThresholdAlarm(
871 pldm::utils::Direction direction, double value, bool newAlarm)
872{
873 if (direction == pldm::utils::Direction::HIGH)
874 {
875 thresholdHardShutdownIntf->hardShutdownAlarmHigh(newAlarm);
876 if (newAlarm)
877 {
878 thresholdHardShutdownIntf->hardShutdownHighAlarmAsserted(value);
879 }
880 else
881 {
882 thresholdHardShutdownIntf->hardShutdownHighAlarmDeasserted(value);
883 }
884 }
885 else
886 {
887 thresholdHardShutdownIntf->hardShutdownAlarmLow(newAlarm);
888 if (newAlarm)
889 {
890 thresholdHardShutdownIntf->hardShutdownLowAlarmAsserted(value);
891 }
892 else
893 {
894 thresholdHardShutdownIntf->hardShutdownLowAlarmDeasserted(value);
895 }
896 }
897}
898
899int NumericSensor::setThresholdAlarm(pldm::utils::Level level,
900 pldm::utils::Direction direction,
901 double value, bool newAlarm)
902{
903 if (!isThresholdValid(level, direction))
904 {
905 lg2::error(
906 "Error:Trigger sensor warning event for non warning threshold sensors {NAME}",
907 "NAME", sensorName);
908 return PLDM_ERROR;
909 }
910 auto alarm = getThresholdAlarm(level, direction);
911 if (alarm == newAlarm)
912 {
913 return PLDM_SUCCESS;
914 }
915 switch (level)
916 {
917 case pldm::utils::Level::WARNING:
918 setWarningThresholdAlarm(direction, value, newAlarm);
919 break;
920 case pldm::utils::Level::CRITICAL:
921 setCriticalThresholdAlarm(direction, value, newAlarm);
922 break;
923 case pldm::utils::Level::HARDSHUTDOWN:
924 setHardShutdownThresholdAlarm(direction, value, newAlarm);
925 break;
926 default:
927 return PLDM_ERROR;
928 }
Amithash Prasad79931b62025-05-01 15:34:59 -0700929 if (newAlarm)
930 {
931 createThresholdLog(level, direction, value);
932 }
933 else
934 {
935 auto& log = assertedLog[{level, direction}];
936 if (log.has_value())
937 {
938 clearThresholdLog(log);
939 }
940 // If all alarms have cleared. Log normal range.
941 if (!hasThresholdAlarm())
942 {
943 createNormalRangeLog(value);
944 }
945 }
Amithash Prasad2480c572025-05-01 15:34:34 -0700946 return PLDM_SUCCESS;
947}
Gilbert Cheneac61a42022-02-23 20:56:19 +0000948
Amithash Prasad79931b62025-05-01 15:34:59 -0700949void NumericSensor::clearThresholdLog(
950 std::optional<sdbusplus::message::object_path>& log)
951{
952 if (!log)
953 {
954 return;
955 }
956 try
957 {
958 /* empty log entries are returned by commit() if the
959 requested log is being filtered out */
960 if (!log->str.empty())
961 {
962 lg2::resolve(*log);
963 }
964 }
965 catch (std::exception& ec)
966 {
967 lg2::error("Error trying to resolve: {LOG} : {ERROR}", "LOG", log->str,
968 "ERROR", ec);
969 }
970 log.reset();
971}
972
973/** @brief helper function template to create a threshold log
974 *
975 * @tparam[in] errorObj - The error object of the log we want to create.
976 * @param[in] sensorObjPath - The object path of the sensor.
977 * @param[in] value - The current value of the sensor.
978 * @param[in] sensorUnit - The units of the sensor.
979 * @param[in] threshold - The threshold value.
980 *
981 * @return optional object holding the object path of the created
982 * log entry. If the log entry is being filtered, we would return
983 * a optional holding an empty string in the object path. This ensures
984 * we follow our state machine properly even if the log is being filtered.
985 */
986template <typename errorObj>
987auto logThresholdHelper(const std::string& sensorObjPath, double value,
988 SensorUnit sensorUnit, double threshold)
989 -> std::optional<sdbusplus::message::object_path>
990{
991 return lg2::commit(
992 errorObj("SENSOR_NAME", sensorObjPath, "READING_VALUE", value, "UNITS",
993 sensorUnit, "THRESHOLD_VALUE", threshold));
994}
995
996void NumericSensor::createThresholdLog(
997 pldm::utils::Level level, pldm::utils::Direction direction, double value)
998{
999 namespace Errors =
1000 sdbusplus::error::xyz::openbmc_project::sensor::Threshold;
1001 /* Map from threshold level+direction to a an instantiation of
1002 * logThresholdHelper with the required error object class */
1003 static const std::map<
1004 std::tuple<pldm::utils::Level, pldm::utils::Direction>,
1005 std::function<std::optional<sdbusplus::message::object_path>(
1006 const std::string&, double, SensorUnit, double)>>
1007 thresholdEventMap = {
1008 {{pldm::utils::Level::WARNING, pldm::utils::Direction::HIGH},
1009 &logThresholdHelper<Errors::ReadingAboveUpperWarningThreshold>},
1010 {{pldm::utils::Level::WARNING, pldm::utils::Direction::LOW},
1011 &logThresholdHelper<Errors::ReadingBelowLowerWarningThreshold>},
1012 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::HIGH},
1013 &logThresholdHelper<Errors::ReadingAboveUpperCriticalThreshold>},
1014 {{pldm::utils::Level::CRITICAL, pldm::utils::Direction::LOW},
1015 &logThresholdHelper<Errors::ReadingBelowLowerCriticalThreshold>},
1016 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::HIGH},
1017 &logThresholdHelper<
1018 Errors::ReadingAboveUpperHardShutdownThreshold>},
1019 {{pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::LOW},
1020 &logThresholdHelper<
1021 Errors::ReadingBelowLowerHardShutdownThreshold>},
1022 };
1023
1024 std::string sensorObjPath = sensorNameSpace + sensorName;
1025 double threshold = getThreshold(level, direction);
1026 try
1027 {
1028 auto helper = thresholdEventMap.at({level, direction});
1029 assertedLog[{level, direction}] =
1030 helper(sensorObjPath, value, sensorUnit, threshold);
1031 }
1032 catch (std::exception& ec)
1033 {
1034 lg2::error(
1035 "Unable to create threshold log entry for {OBJPATH}: {ERROR}",
1036 "OBJPATH", sensorObjPath, "ERROR", ec);
1037 }
1038}
1039
1040void NumericSensor::createNormalRangeLog(double value)
1041{
1042 namespace Events =
1043 sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
1044 std::string objPath = sensorNameSpace + sensorName;
1045 try
1046 {
1047 lg2::commit(Events::SensorReadingNormalRange(
1048 "SENSOR_NAME", objPath, "READING_VALUE", value, "UNITS",
1049 sensorUnit));
1050 }
1051 catch (std::exception& ec)
1052 {
1053 lg2::error(
1054 "Unable to create SensorReadingNormalRange log entry for {OBJPATH}: {ERROR}",
1055 "OBJPATH", objPath, "ERROR", ec);
1056 }
1057}
1058
Gilbert Cheneac61a42022-02-23 20:56:19 +00001059void NumericSensor::updateThresholds()
1060{
Thu Nguyen6d615f12024-04-24 05:02:03 +00001061 double value = std::numeric_limits<double>::quiet_NaN();
1062
1063 if ((!useMetricInterface && !valueIntf) ||
1064 (useMetricInterface && !metricIntf))
Gilbert Cheneac61a42022-02-23 20:56:19 +00001065 {
1066 lg2::error(
1067 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1068 "NAME", sensorName);
1069 return;
1070 }
Thu Nguyen6d615f12024-04-24 05:02:03 +00001071 if (!useMetricInterface)
1072 {
1073 value = valueIntf->value();
1074 }
1075 else
1076 {
1077 value = metricIntf->value();
1078 }
Gilbert Cheneac61a42022-02-23 20:56:19 +00001079
Amithash Prasad2480c572025-05-01 15:34:34 -07001080 for (auto level : allThresholdLevels)
Gilbert Cheneac61a42022-02-23 20:56:19 +00001081 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001082 for (auto direction : allThresholdDirections)
Gilbert Cheneac61a42022-02-23 20:56:19 +00001083 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001084 auto threshold = getThreshold(level, direction);
1085 if (!std::isfinite(threshold))
Gilbert Cheneac61a42022-02-23 20:56:19 +00001086 {
Amithash Prasad2480c572025-05-01 15:34:34 -07001087 continue;
Gilbert Cheneac61a42022-02-23 20:56:19 +00001088 }
Amithash Prasad2480c572025-05-01 15:34:34 -07001089 auto alarm = getThresholdAlarm(level, direction);
1090 auto newAlarm =
1091 checkThreshold(alarm, direction == pldm::utils::Direction::HIGH,
1092 value, threshold, hysteresis);
1093 setThresholdAlarm(level, direction, value, newAlarm);
Zoey YJ Chung9a224ae2025-04-11 15:40:42 +08001094 }
1095 }
Gilbert Cheneac61a42022-02-23 20:56:19 +00001096}
Gilbert Chen77e6fe72024-08-06 09:23:30 +00001097
1098int NumericSensor::triggerThresholdEvent(
1099 pldm::utils::Level eventType, pldm::utils::Direction direction,
1100 double rawValue, bool newAlarm, bool assert)
1101{
1102 if (!valueIntf)
1103 {
1104 lg2::error(
1105 "Failed to update thresholds sensor {NAME} D-Bus interfaces don't exist.",
1106 "NAME", sensorName);
1107 return PLDM_ERROR;
1108 }
1109
1110 auto value = unitModifier(conversionFormula(rawValue));
1111 lg2::error(
1112 "triggerThresholdEvent eventType {TID}, direction {SID} value {VAL} newAlarm {PSTATE} assert {ESTATE}",
1113 "TID", eventType, "SID", direction, "VAL", value, "PSTATE", newAlarm,
1114 "ESTATE", assert);
1115
Amithash Prasad2480c572025-05-01 15:34:34 -07001116 return setThresholdAlarm(eventType, direction, value, newAlarm);
Gilbert Chen77e6fe72024-08-06 09:23:30 +00001117}
Thu Nguyen3c5486d2024-08-01 08:03:08 +00001118} // namespace platform_mc
1119} // namespace pldm