| #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 |