blob: 74eede6fc98cd5854d2c25029420b60f8cf692a0 [file] [log] [blame]
#pragma once
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdeventplus/clock.hpp>
#include <sdeventplus/event.hpp>
#include <sdeventplus/utility/timer.hpp>
#include <chrono>
#include <deque>
#include <format>
#include <optional>
#include <utility>
namespace phosphor
{
namespace fan
{
namespace monitor
{
class Fan;
constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/";
/**
* The mode fan monitor will run in:
* - init - only do the initialization steps
* - monitor - run normal monitoring algorithm
*/
enum class Mode
{
init,
monitor
};
/**
* The mode that the timer is running in:
* - func - Transition to functional state timer
* - nonfunc - Transition to nonfunctional state timer
*/
enum class TimerMode
{
func,
nonfunc
};
/**
* The mode that the method is running in:
* - time - Use a percentage based deviation
* - count - Run up/down count fault detection
*/
enum MethodMode
{
timebased = 0,
count
};
/**
* @class TachSensor
*
* This class represents the sensor that reads a tach value.
* It may also support a Target, which is the property used to
* set a speed. Since it doesn't necessarily have a Target, it
* won't for sure know if it is running too slow, so it leaves
* that determination to other code.
*
* This class has a parent Fan object that knows about all
* sensors for that fan.
*/
class TachSensor
{
public:
TachSensor() = delete;
TachSensor(const TachSensor&) = delete;
// TachSensor is not moveable since the this pointer is used as systemd
// callback context.
TachSensor(TachSensor&&) = delete;
TachSensor& operator=(const TachSensor&) = delete;
TachSensor& operator=(TachSensor&&) = delete;
~TachSensor() = default;
/**
* @brief Constructor
*
* @param[in] mode - mode of fan monitor
* @param[in] bus - the dbus object
* @param[in] fan - the parent fan object
* @param[in] id - the id of the sensor
* @param[in] hasTarget - if the sensor supports
* setting the speed
* @param[in] funcDelay - Delay to mark functional
* @param[in] interface - the interface of the target
* @param[in] path - the object path of the sensor target
* @param[in] factor - the factor of the sensor target
* @param[in] offset - the offset of the sensor target
* @param[in] method - the method of out of range
* @param[in] threshold - the threshold of counter method
* @param[in] ignoreAboveMax - whether to ignore being above max or not
* @param[in] timeout - Normal timeout value to use
* @param[in] errorDelay - Delay in seconds before creating an error
* or std::nullopt if no errors.
* @param[in] countInterval - In count mode interval
*
* @param[in] event - Event loop reference
*/
TachSensor(Mode mode, sdbusplus::bus_t& bus, Fan& fan,
const std::string& id, bool hasTarget, size_t funcDelay,
const std::string& interface, const std::string& path,
double factor, int64_t offset, size_t method, size_t threshold,
bool ignoreAboveMax, size_t timeout,
const std::optional<size_t>& errorDelay, size_t countInterval,
const sdeventplus::Event& event);
/**
* @brief Reads a property from the input message and stores it in value.
* T is the value type.
*
* Note: This can only be called once per message.
*
* @param[in] msg - the dbus message that contains the data
* @param[in] interface - the interface the property is on
* @param[in] propertName - the name of the property
* @param[out] value - the value to store the property value in
*/
template <typename T>
static void readPropertyFromMessage(
sdbusplus::message_t& msg, const std::string& interface,
const std::string& propertyName, T& value)
{
std::string sensor;
std::map<std::string, std::variant<T>> data;
msg.read(sensor, data);
if (sensor.compare(interface) == 0)
{
auto propertyMap = data.find(propertyName);
if (propertyMap != data.end())
{
value = std::get<T>(propertyMap->second);
}
}
}
/**
* @brief Returns the target speed value
*/
uint64_t getTarget() const;
/**
* @brief Returns the input speed value
*/
inline double getInput() const
{
return _tachInput;
}
/**
* @brief Returns true if sensor has a target
*/
inline bool hasTarget() const
{
return _hasTarget;
}
/**
* @brief Returns the interface of the sensor target
*/
inline std::string getInterface() const
{
return _interface;
}
/**
* @brief Returns true if sensor has a D-Bus owner
*/
inline bool hasOwner() const
{
return _hasOwner;
}
/**
* @brief sets D-Bus owner status
*
* @param[in] val - new owner status
*/
inline void setOwner(bool val)
{
_hasOwner = val;
}
/**
* @brief Returns the factor of the sensor target
*/
inline double getFactor() const
{
return _factor;
}
/**
* @brief Returns a reference to the sensor's Fan object
*/
inline Fan& getFan() const
{
return _fan;
}
/**
* @brief Returns the offset of the sensor target
*/
inline int64_t getOffset() const
{
return _offset;
}
/**
* @brief Returns the method of out of range
*/
inline size_t getMethod() const
{
return _method;
}
/**
* @brief Returns the threshold of count method
*/
inline size_t getThreshold() const
{
return _threshold;
}
/**
* Set the sensor faulted counter
*/
void setCounter(bool count);
/**
* @brief Returns the sensor faulted count
*/
inline size_t getCounter() const
{
return _counter;
}
/**
* Returns true if the hardware behind this
* sensor is considered working OK/functional.
*/
inline bool functional() const
{
return _functional;
}
/**
* Set the functional status and update inventory to match
*
* @param[in] functional - The new state
* @param[in] skipErrorTimer - If setting the sensor to
* nonfunctional, don't start the error timer.
*/
void setFunctional(bool functional, bool skipErrorTimer = false);
/**
* @brief Says if the timer is running or not
*
* @return bool - if timer is currently running
*/
inline bool timerRunning()
{
return _timer.isEnabled();
}
/**
* @brief Stops the timer when the given mode differs and starts
* the associated timer for the mode given if not already running
*
* @param[in] mode - mode of timer to start
*/
void startTimer(TimerMode mode);
/**
* @brief Stops the timer
*/
inline void stopTimer()
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
std::format("Stop running timer on tach sensor {}.", _name)
.c_str());
_timer.setEnabled(false);
}
/**
* @brief Says if the count timer is running
*
* @return bool - If count timer running
*/
inline bool countTimerRunning() const
{
return _countTimer && _countTimer->isEnabled();
}
/**
* @brief Stops the count timer
*/
void stopCountTimer();
/**
* @brief Starts the count timer
*/
void startCountTimer();
/**
* @brief Return the given timer mode's delay time
*
* @param[in] mode - mode of timer to get delay time for
*/
std::chrono::microseconds getDelay(TimerMode mode);
/**
* Returns the sensor name
*/
inline const std::string& name() const
{
return _name;
};
/**
* @brief Says if the error timer is running
*
* @return bool - If the timer is running
*/
bool errorTimerRunning() const
{
if (_errorTimer && _errorTimer->isEnabled())
{
return true;
}
return false;
}
/**
* @brief Get the current allowed range of speeds
*
* @param[in] lowerDeviation - The configured lower deviation(in percent)
* allowed
* @param[in] upperDeviation - The configured upper deviation(in percent)
* allowed
*
* @return pair - Min/Max(optional) range of speeds allowed
*/
std::pair<uint64_t, std::optional<uint64_t>> getRange(
const size_t lowerDeviation, const size_t upperDeviation) const;
/**
* @brief Processes the current state of the sensor
*/
void processState();
/**
* @brief Resets the monitoring method of the sensor
*/
void resetMethod();
/**
* @brief Refreshes the tach input and target values by
* reading them from D-Bus.
*/
void updateTachAndTarget();
/**
* @brief return the previous tach values
*/
const std::deque<uint64_t>& getPrevTach() const
{
return _prevTachs;
}
/**
* @brief return the previous target values
*/
const std::deque<uint64_t>& getPrevTarget() const
{
return _prevTargets;
}
private:
/**
* @brief Returns the match string to use for matching
* on a properties changed signal.
*/
std::string getMatchString(const std::optional<std::string> path,
const std::string& interface);
/**
* @brief Reads the Target property and stores in _tachTarget.
* Also calls Fan::tachChanged().
*
* @param[in] msg - the dbus message
*/
void handleTargetChange(sdbusplus::message_t& msg);
/**
* @brief Reads the Value property and stores in _tachInput.
* Also calls Fan::tachChanged().
*
* @param[in] msg - the dbus message
*/
void handleTachChange(sdbusplus::message_t& msg);
/**
* @brief Updates the Functional property in the inventory
* for this tach sensor based on the value passed in.
*
* @param[in] functional - If the Functional property should
* be set to true or false.
*/
void updateInventory(bool functional);
/**
* @brief the dbus object
*/
sdbusplus::bus_t& _bus;
/**
* @brief Reference to the parent Fan object
*/
Fan& _fan;
/**
* @brief The name of the sensor, including the full path
*
* For example /xyz/openbmc_project/sensors/fan_tach/fan0
*/
const std::string _name;
/**
* @brief The inventory name of the sensor, including the full path
*/
const std::string _invName;
/**
* @brief If functional (not too slow). The parent
* fan object sets this.
*/
bool _functional = true;
/**
* @brief If the sensor has a Target property (can set speed)
*/
const bool _hasTarget;
/**
* @brief If the sensor has a D-Bus owner
*/
bool _hasOwner = true;
/**
* @brief Amount of time to delay updating to functional
*/
const size_t _funcDelay;
/**
* @brief The interface that the target implements
*/
const std::string _interface;
/**
* @brief The object path to set sensor's target
*/
const std::string _path;
/**
* @brief The factor of target to get fan rpm
*/
const double _factor;
/**
* @brief The offset of target to get fan rpm
*/
const int64_t _offset;
/**
* @brief The method of out of range
*/
const size_t _method;
/**
* @brief The threshold for count method
*/
const size_t _threshold;
/**
* @brief Whether to ignore being above the max or not
*/
const bool _ignoreAboveMax;
/**
* @brief The counter for count method
*/
size_t _counter = 0;
/**
* @brief The input speed, from the Value dbus property
*/
double _tachInput = 0;
/**
* @brief The current target speed, from the Target dbus property
* (if applicable)
*/
uint64_t _tachTarget = 0;
/**
* @brief The timeout value to use
*/
const size_t _timeout;
/**
* @brief Mode that current timer is in
*/
TimerMode _timerMode;
/**
* The timer object
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
/**
* @brief The match object for the Value properties changed signal
*/
std::unique_ptr<sdbusplus::bus::match_t> tachSignal;
/**
* @brief The match object for the Target properties changed signal
*/
std::unique_ptr<sdbusplus::bus::match_t> targetSignal;
/**
* @brief The number of seconds to wait between a sensor being set
* to nonfunctional and creating an error for it.
*
* If std::nullopt, no errors will be created.
*/
const std::optional<size_t> _errorDelay;
/**
* @brief The timer that uses _errorDelay. When it expires an error
* will be created for a faulted fan sensor (rotor).
*
* If _errorDelay is std::nullopt, then this won't be created.
*/
std::unique_ptr<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
_errorTimer;
/**
* @brief The interval, in seconds, to use for the timer that runs
* the checks when using the 'count' method.
*/
size_t _countInterval;
/**
* @brief The timer used by the 'count' method for determining
* functional status.
*/
std::unique_ptr<
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
_countTimer;
/**
* @brief record of previous targets
*/
std::deque<uint64_t> _prevTargets;
/**
* @brief record of previous tach readings
*/
std::deque<uint64_t> _prevTachs;
};
} // namespace monitor
} // namespace fan
} // namespace phosphor