blob: 115fec3c55c7ff3d8d84e875f88e28a63179f126 [file] [log] [blame]
#include "sensors/pldm_sensor.hpp"
#include "common/utils.hpp"
#include "sensors/hwmon.hpp"
#include <libpldm/state_set.h>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstring>
#include <format>
namespace pldm
{
namespace sensor
{
using namespace pldm::sensor;
using namespace pldm::utils;
// Initialization for Warning Objects
decltype(Thresholds<WarningObject>::setLo) Thresholds<WarningObject>::setLo =
&WarningObject::warningLow;
decltype(Thresholds<WarningObject>::setHi) Thresholds<WarningObject>::setHi =
&WarningObject::warningHigh;
decltype(Thresholds<WarningObject>::getLo) Thresholds<WarningObject>::getLo =
&WarningObject::warningLow;
decltype(Thresholds<WarningObject>::getHi) Thresholds<WarningObject>::getHi =
&WarningObject::warningHigh;
decltype(Thresholds<WarningObject>::alarmLo)
Thresholds<WarningObject>::alarmLo = &WarningObject::warningAlarmLow;
decltype(Thresholds<WarningObject>::alarmHi)
Thresholds<WarningObject>::alarmHi = &WarningObject::warningAlarmHigh;
decltype(Thresholds<WarningObject>::getAlarmLow)
Thresholds<WarningObject>::getAlarmLow = &WarningObject::warningAlarmLow;
decltype(Thresholds<WarningObject>::getAlarmHigh)
Thresholds<WarningObject>::getAlarmHigh = &WarningObject::warningAlarmHigh;
decltype(Thresholds<WarningObject>::assertLowSignal)
Thresholds<WarningObject>::assertLowSignal =
&WarningObject::warningLowAlarmAsserted;
decltype(Thresholds<WarningObject>::assertHighSignal)
Thresholds<WarningObject>::assertHighSignal =
&WarningObject::warningHighAlarmAsserted;
decltype(Thresholds<WarningObject>::deassertLowSignal)
Thresholds<WarningObject>::deassertLowSignal =
&WarningObject::warningLowAlarmDeasserted;
decltype(Thresholds<WarningObject>::deassertHighSignal)
Thresholds<WarningObject>::deassertHighSignal =
&WarningObject::warningHighAlarmDeasserted;
// Initialization for Critical Objects
decltype(Thresholds<CriticalObject>::setLo) Thresholds<CriticalObject>::setLo =
&CriticalObject::criticalLow;
decltype(Thresholds<CriticalObject>::setHi) Thresholds<CriticalObject>::setHi =
&CriticalObject::criticalHigh;
decltype(Thresholds<CriticalObject>::getLo) Thresholds<CriticalObject>::getLo =
&CriticalObject::criticalLow;
decltype(Thresholds<CriticalObject>::getHi) Thresholds<CriticalObject>::getHi =
&CriticalObject::criticalHigh;
decltype(Thresholds<CriticalObject>::alarmLo)
Thresholds<CriticalObject>::alarmLo = &CriticalObject::criticalAlarmLow;
decltype(Thresholds<CriticalObject>::alarmHi)
Thresholds<CriticalObject>::alarmHi = &CriticalObject::criticalAlarmHigh;
decltype(Thresholds<CriticalObject>::getAlarmLow)
Thresholds<CriticalObject>::getAlarmLow = &CriticalObject::criticalAlarmLow;
decltype(Thresholds<CriticalObject>::getAlarmHigh)
Thresholds<CriticalObject>::getAlarmHigh =
&CriticalObject::criticalAlarmHigh;
decltype(Thresholds<CriticalObject>::assertLowSignal)
Thresholds<CriticalObject>::assertLowSignal =
&CriticalObject::criticalLowAlarmAsserted;
decltype(Thresholds<CriticalObject>::assertHighSignal)
Thresholds<CriticalObject>::assertHighSignal =
&CriticalObject::criticalHighAlarmAsserted;
decltype(Thresholds<CriticalObject>::deassertLowSignal)
Thresholds<CriticalObject>::deassertLowSignal =
&CriticalObject::criticalLowAlarmDeasserted;
decltype(Thresholds<CriticalObject>::deassertHighSignal)
Thresholds<CriticalObject>::deassertHighSignal =
&CriticalObject::criticalHighAlarmDeasserted;
/**
* @brief Constructs PldmSensor object
*/
PldmSensor::PldmSensor(sdbusplus::bus::bus& bus, const std::string& name,
uint8_t baseUnit, int8_t unitModifier, double offset,
double resolution, double warningHigh, double warningLow,
double criticalHigh, double criticalLow) :
_bus(bus), sensorName(name), baseUnit(baseUnit), unitModifier(unitModifier),
offset(offset), resolution(resolution), warningHigh(warningHigh),
warningLow(warningLow), criticalHigh(criticalHigh), criticalLow(criticalLow)
{}
/**
* @brief De-constructs PldmSensor object
*/
PldmSensor::~PldmSensor()
{
_bus.emit_object_removed(sensorPath.c_str());
}
/**
* @brief Create sensor interfaces
* @details After init the sensor data, call createSensor to create the
* sensor interfaces such as value, functional status, thresholds
*
* @return - Shared pointer to the object data
*/
std::optional<ObjectStateData> PldmSensor::createSensor()
{
Attributes attrs;
if (!std::isnan(warningLow))
{
warningLow = adjustValue(warningLow);
}
if (!std::isnan(warningHigh))
{
warningHigh = adjustValue(warningHigh);
}
if (!std::isnan(criticalLow))
{
criticalLow = adjustValue(criticalLow);
}
if (!std::isnan(criticalHigh))
{
criticalHigh = adjustValue(criticalHigh);
}
if (!std::isnan(sensorMaxValue))
{
sensorMaxValue = adjustValue(sensorMaxValue);
}
if (!std::isnan(sensorMinValue))
{
sensorMinValue = adjustValue(sensorMinValue);
}
bool foundType = getAttributes(baseUnit, attrs);
if (!foundType)
{
std::cerr << "Failed to find numeric sensor type for baseUnit:"
<< (int)baseUnit << " in sensor: " << sensorName << std::endl;
return {};
}
sensorPath = _root + "/" + getNamespace(attrs) + "/" + sensorName;
double sensorValue = std::numeric_limits<double>::quiet_NaN();
ObjectInfo info(&_bus, std::move(sensorPath), InterfaceMap());
try
{
statusInterface = addStatusInterface(info, true);
valueInterface = addValueInterface(info, sensorValue, sensorMaxValue,
sensorMinValue);
if (!valueInterface)
{
return {};
}
valueInterface->unit(getUnit(attrs));
}
catch (const std::system_error& e)
{
return {};
}
warnObject = addThreshold<WarningObject>(info, sensorValue, warningLow,
warningHigh);
critObject = addThreshold<CriticalObject>(info, sensorValue, criticalLow,
criticalHigh);
valueInterface->emit_object_added();
associationInterface = addAssociationInterface(info);
return std::make_pair(sensorName, std::move(info));
}
/**
* @brief Add value interface for Sensor
*/
std::shared_ptr<ValueObject>
PldmSensor::addValueInterface(ObjectInfo& info, SensorValueType value,
SensorValueType max, SensorValueType min)
{
std::shared_ptr<ValueObject> iface = nullptr;
auto& bus = *std::get<sdbusplus::bus::bus*>(info);
auto& obj = std::get<InterfaceMap>(info);
auto& objPath = std::get<std::string>(info);
try
{
auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>(
obj[InterfaceType::STATUS]);
assert(statusIface);
iface = std::make_shared<ValueObject>(bus, objPath.c_str(),
ValueObject::action::defer_emit);
iface->value(value);
iface->maxValue(max);
iface->minValue(min);
obj[InterfaceType::VALUE] = iface;
}
catch (const std::system_error& e)
{
return nullptr;
}
return iface;
}
std::shared_ptr<AssociationDefinitionsInterface>
PldmSensor::addAssociationInterface(ObjectInfo& info)
{
if (chassisInventoryPath.empty())
{
std::cerr << "Warning: empty chassisInventoryPath,"
" skip create AssociationDefinitionsInterface"
<< std::endl;
return nullptr;
}
std::shared_ptr<AssociationDefinitionsInterface> iface = nullptr;
auto& bus = *std::get<sdbusplus::bus::bus*>(info);
auto& objPath = std::get<std::string>(info);
try
{
iface = std::make_shared<AssociationDefinitionsInterface>(
bus, objPath.c_str());
iface->associations({{"chassis", "all_sensors", chassisInventoryPath}});
}
catch (const std::system_error& e)
{
std::cerr << "Error: failed to create AssociationDefinitionsInterface"
<< "for " << objPath.c_str() << std::endl;
return nullptr;
}
return iface;
}
/**
* @brief Add functional status interface for Sensor
*/
std::shared_ptr<StatusObject> PldmSensor::addStatusInterface(ObjectInfo& info,
bool functional)
{
std::shared_ptr<StatusObject> iface = nullptr;
auto& objPath = std::get<std::string>(info);
auto& obj = std::get<InterfaceMap>(info);
auto& bus = *std::get<sdbusplus::bus::bus*>(info);
try
{
iface = std::make_shared<StatusObject>(
bus, objPath.c_str(), StatusObject::action::defer_emit);
iface->functional(functional);
obj[InterfaceType::STATUS] = iface;
}
catch (const std::system_error& e)
{
return nullptr;
}
return iface;
}
/**
* @brief Apply unitModifier to sensor value
*/
SensorValueType PldmSensor::adjustValue(SensorValueType value)
{
if (value < 0)
{
return value;
}
if constexpr (std::is_same<SensorValueType, double>::value)
{
value = (value * resolution + offset) *
std::pow(10, signed(unitModifier));
}
return value;
}
/**
* @brief Update sensor interfaces
*/
void PldmSensor::updateValue(SensorValueType sensorValue)
{
auto value = adjustValue(sensorValue);
if (value == lastValue)
{
return;
}
lastValue = value;
valueInterface->value(lastValue);
if (!std::isnan(lastValue))
{
if (warnObject)
{
checkThresholds<WarningObject>(warnObject, lastValue);
}
if (critObject)
{
checkThresholds<CriticalObject>(critObject, lastValue);
}
}
if (inventoryObject)
{
auto port = std::dynamic_pointer_cast<PortInventory>(inventoryObject);
if (port && (baseUnit == pldm::sensor::type::bits))
{
port->setSpeedGbps(value / 1000000000.0);
}
}
return;
}
// PldmStateSensor implement
PldmStateSensor::PldmStateSensor(sdbusplus::bus::bus& bus,
const std::string& name) :
_bus(bus), sensorName(name){};
PldmStateSensor::~PldmStateSensor()
{
_bus.emit_object_removed(sensorPath.c_str());
}
std::shared_ptr<StateReadingInterface>
PldmStateSensor::addStateReadingInterface(ObjectInfo& info)
{
std::shared_ptr<StateReadingInterface> iface = nullptr;
auto& bus = *std::get<sdbusplus::bus::bus*>(info);
auto& objPath = std::get<std::string>(info);
try
{
iface = std::make_shared<StateReadingInterface>(bus, objPath.c_str());
}
catch (const std::system_error& e)
{
return nullptr;
}
return iface;
}
std::shared_ptr<StatusObject>
PldmStateSensor::addStatusInterface(ObjectInfo& info, bool functional)
{
std::shared_ptr<StatusObject> iface = nullptr;
auto& objPath = std::get<std::string>(info);
auto& obj = std::get<InterfaceMap>(info);
auto& bus = *std::get<sdbusplus::bus::bus*>(info);
try
{
iface = std::make_shared<StatusObject>(
bus, objPath.c_str(), StatusObject::action::defer_emit);
iface->functional(functional);
obj[InterfaceType::STATUS] = iface;
}
catch (const std::system_error& e)
{
return nullptr;
}
return iface;
}
std::string
PldmStateSensor::decodeHealthState(const get_sensor_state_field& reading)
{
if (reading.sensor_op_state == PLDM_SENSOR_ENABLED)
{
switch (reading.present_state)
{
case PLDM_STATE_SET_HEALTH_STATE_NORMAL:
return InventoryBase::HealthStateOK;
case PLDM_STATE_SET_HEALTH_STATE_NON_CRITICAL:
case PLDM_STATE_SET_HEALTH_STATE_UPPER_NON_CRITICAL:
case PLDM_STATE_SET_HEALTH_STATE_LOWER_NON_CRITICAL:
return InventoryBase::HealthStateWarning;
case PLDM_STATE_SET_HEALTH_STATE_CRITICAL:
case PLDM_STATE_SET_HEALTH_STATE_FATAL:
case PLDM_STATE_SET_HEALTH_STATE_UPPER_CRITICAL:
case PLDM_STATE_SET_HEALTH_STATE_LOWER_CRITICAL:
case PLDM_STATE_SET_HEALTH_STATE_UPPER_FATAL:
case PLDM_STATE_SET_HEALTH_STATE_LOWER_FATAL:
return InventoryBase::HealthStateCritical;
default:
return InventoryBase::Unknown;
}
}
else
{
return InventoryBase::Unknown;
}
}
void PldmStateSensor::updatePortInventory(const get_sensor_state_field& reading)
{
auto port = std::dynamic_pointer_cast<PortInventory>(inventoryObject);
if (!port)
return;
if (reading.sensor_op_state != PLDM_SENSOR_ENABLED)
{
port->setLinkState(PortInventory::LinkStateDisabled);
port->setLinkStatus(PortInventory::Unknown);
return;
}
switch (reading.present_state)
{
case PLDM_STATE_SET_LINK_STATE_CONNECTED:
port->setLinkState(PortInventory::LinkStateEnabled);
port->setLinkStatus(PortInventory::LinkStatusLinkUp);
break;
case PLDM_STATE_SET_LINK_STATE_DISCONNECTED:
port->setLinkState(PortInventory::LinkStateDisabled);
port->setLinkStatus(PortInventory::LinkStatusLinkDown);
break;
default:
port->setLinkState(PortInventory::LinkStateDisabled);
port->setLinkStatus(PortInventory::Unknown);
}
}
void PldmStateSensor::updateInventory(uint16_t stateSetId,
const get_sensor_state_field& reading)
{
if (PLDM_STATE_SET_HEALTH_STATE == stateSetId)
{
if (inventoryObject)
{
inventoryObject->setHealthState(decodeHealthState(reading));
}
}
else if (PLDM_STATE_SET_LINK_STATE == stateSetId)
{
updatePortInventory(reading);
}
}
void PldmStateSensor::updateStateReading(uint8_t cnt,
get_sensor_state_field fields[])
{
if (cnt != getCompositeSensorCount())
{
std::cerr
<< std::format(
"Error : {} got invalid CompositeSensorCount, expect:{}, got: {}",
sensorName, unsigned(getCompositeSensorCount()),
unsigned(cnt))
<< std::endl;
setFunctionalStatus(false);
return;
}
std::vector<PldmStateReadingTuple> newReading;
for (int i = 0; i < cnt; i++)
{
compositeSensors[i].reading = fields[i];
newReading.push_back(PldmStateReadingTuple{
compositeSensors[i].stateSetId, fields[i].sensor_op_state,
fields[i].present_state, fields[i].previous_state,
fields[i].event_state});
updateInventory(compositeSensors[i].stateSetId,
compositeSensors[i].reading);
}
stateReadingInterface->values(newReading);
setFunctionalStatus(true);
}
void PldmStateSensor::invalidateStateSensor()
{
std::vector<PldmStateReadingTuple> newReading;
get_sensor_state_field field{PLDM_SENSOR_STATUSUNKOWN, 0, 0, 0};
for (PldmStateSetReading& reading : compositeSensors)
{
reading.reading = field;
updateInventory(reading.stateSetId, reading.reading);
newReading.push_back(PldmStateReadingTuple{
reading.stateSetId, field.sensor_op_state, field.present_state,
field.previous_state, field.event_state});
}
stateReadingInterface->values(newReading);
statusInterface->functional(false);
}
std::optional<ObjectStateData> PldmStateSensor::createStateSensor()
{
sensorPath = _root + "/PLDMState/" + sensorName;
ObjectInfo info(&_bus, std::move(sensorPath), InterfaceMap());
// status will be updated to operational on the first successful sensor read
statusInterface = addStatusInterface(info, false);
stateReadingInterface = addStateReadingInterface(info);
return std::make_pair(sensorName, std::move(info));
}
} // namespace sensor
} // namespace pldm