blob: 61688db1b14a4431af3ce662750aea3b82b46f6b [file]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SENSORS_SENSOR_BASE_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SENSORS_SENSOR_BASE_H_
#include <atomic>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/functional/function_ref.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "boost/circular_buffer.hpp" //NOLINT: boost is commonly used in BMC
#include <nlohmann/json.hpp>
#include "entity_common_config.pb.h"
#include "hal_common_config.pb.h"
#include "reading_transform_config.pb.h"
#include "sensor_instance_properties.pb.h"
#include "threshold_config.pb.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "google/protobuf/util/json_util.h"
namespace milotic_tlbmc {
// The base class for all sensors.
// Any implementation of this interface must be thread-safe regarding the listed
// functions.
class Sensor {
public:
// Callback used export batch of sensor data.
using NotificationCb = absl::FunctionRef<void(
std::vector<std::shared_ptr<const SensorValue>>&&)>;
static SensorAttributesStatic CreateStaticAttributes(
const SensorInstanceProperties& sensor_instance_properties,
const HalCommonConfig& hal_common_config,
const EntityCommonConfig& entity_common_config);
virtual ~Sensor() = default;
// Returns the latest sensor reading.
virtual std::shared_ptr<const SensorValue> GetSensorData() const = 0;
virtual std::vector<std::shared_ptr<const SensorValue>> GetSensorDataHistory()
const = 0;
virtual std::vector<std::shared_ptr<const SensorValue>>
GetSensorDataHistorySince(absl::Time start_time) const = 0;
// Refreshes the sensor data once asynchronously. The sensor data will be
// updated when the refresh is done. On errors, nullptr will be fed to the
// callback. Detailed error message can be found in the sensor's state.
// The `callback` will be called when the refresh is done with the SensorData
// that has been refreshed or nullptr if there is an error. The `callback` can
// be nullptr. The callback is supposed to be blocking.
// Implementation is also supposed to implement metrics update in this method.
virtual void RefreshOnceAsync(
absl::AnyInvocable<void(const std::shared_ptr<const SensorValue>&)>
callback) = 0;
// Writes the sensor reading to the sensor hardware synchronously.
// Note, this does not update the cached sensor data directly. Sensor data is
// still updated asynchronously by `RefreshOnceAsync`.
// Not all sensors support this functionality, which is currently used only
// through a RedFish endpoint.
virtual absl::Status WriteReading(const SensorValue& /*unused*/) {
return absl::UnimplementedError(
"WriteReading is not implemented or supported for this "
"implementation.");
}
// Returns the key of the sensor. A key shall be constant for a sensor's
// lifetime.
const std::string& GetKey() const {
return sensor_attributes_static_.attributes().key();
}
// Returns the config key of the board that contains this sensor.
const std::string& GetBoardConfigKey() const {
return sensor_attributes_static_.entity_common_config().board_config_key();
}
virtual SensorAttributesDynamic GetSensorAttributesDynamic() const {
absl::MutexLock lock(sensor_attributes_dynamic_mutex_);
SensorAttributesDynamic attributes = sensor_attributes_dynamic_;
{
absl::MutexLock override_lock(override_mutex_);
if (override_value_.has_value()) {
attributes.mutable_state()->set_status(STATUS_READY);
}
}
return attributes;
}
const SensorAttributesStatic& GetSensorAttributesStatic() const {
return sensor_attributes_static_;
}
// Sets the software override value for this sensor.
// When set, GetSensorData() will return this value and RefreshOnceAsync()
// will not overwrite it.
virtual absl::Status SetOverrideValue(const SensorValue& value);
// Clears any active software override, restoring normal hardware polling.
virtual absl::Status ClearOverride();
// Returns true if a software override is currently active.
virtual bool IsOverridden() const;
// Returns the active override value if present.
virtual std::optional<SensorValue> GetOverrideValue() const;
virtual SensorMetrics GetSensorMetrics() const;
virtual absl::StatusOr<nlohmann::json> GetMetricsAsJson(
const ::google::protobuf::util::JsonPrintOptions& options) const;
// Converts the sensor to a JSON object.
// Do not use this function to format Redfish request. This is a debug only
// API.
virtual absl::StatusOr<nlohmann::json> ToJson() const;
void UpdateState(State&& state);
// Updates the thresholds for the sensor.
void UpdateThresholds(const ThresholdConfigs& threshold_configs);
// Resize the buffer for the sensor.
virtual void ResizeBuffer(size_t buffer_size) = 0;
// Resets the metrics for the sensor. This is used when the sensor is
// configured with a different sampling interval.
virtual void ResetMetrics() {}
// When a sensor is configured with a different sampling interval, we use this
// function to update the sampling interval in the sensor dynamic attributes
void UpdateSensorSamplingInterval(absl::Duration new_sampling_interval);
// Since Reinitialize may have to be performed asynchronously, we will invoke
// the callback when the reinitialization is done. The status passed to it
// will indicate the status of the reinitialization.
virtual void Reinitialize(absl::AnyInvocable<void(absl::Status)> callback) {
callback(absl::UnimplementedError("Reinitialize is not implemented for " +
GetKey() +
" with "
"config " +
GetBoardConfigKey()));
}
// Some sensors are responsible for managing their own devpath and this
// function will be used to obtain devpaths for those sensors. Most devpaths
// will be found by looking at the devpath for the related item associated
// with the sensor
virtual absl::StatusOr<const std::string&> GetDevpath() const {
if (!devpath_.empty()) {
return devpath_;
}
return absl::UnimplementedError("GetDevpath is not implemented for " +
GetKey() +
" with "
"config " +
GetBoardConfigKey());
}
// Sets the devpath for the sensor. Expect this to be called only by store
// during initialization.
absl::Status SetDevpath(absl::string_view devpath) {
if (!devpath_.empty()) {
return absl::FailedPreconditionError("Devpath is already set.");
}
devpath_ = devpath;
return absl::OkStatus();
}
// Callback to be invoked when the sensor is ready to read.
virtual void PowerOffToOnCallback([[maybe_unused]] bool setup_run);
// Callback to be invoked when the sensor is not ready to read.
virtual void PowerOnToOffCallback([[maybe_unused]] bool setup_run);
// Callback to be invoked when the sensor is ready to read.
virtual void OSInactiveToStandbyCallback([[maybe_unused]] bool setup_run);
// Callback to be invoked when the sensor is not ready to read.
virtual void OSStandbyToInactiveCallback([[maybe_unused]] bool setup_run);
bool IsReadyForRead() const { return ready_for_read_; }
// Threshold event is used to notify the subscriber that the sensor has
// exceeded a threshold. The event contains the threshold type, the reading
// value, and whether the threshold is asserted or de-asserted.
struct ThresholdEvent {
ThresholdType threshold_type = THRESHOLD_TYPE_UNKNOWN;
bool is_assert = false;
double reading = 0.0;
bool operator==(const ThresholdEvent& other) const {
return threshold_type == other.threshold_type &&
is_assert == other.is_assert;
}
};
std::vector<ThresholdEvent> CheckSensorThresholds()
ABSL_LOCKS_EXCLUDED(sensor_attributes_dynamic_mutex_);
protected:
Sensor(SensorAttributesStatic&& sensor_attributes_static,
const ThresholdConfigs& threshold_configs,
std::optional<NotificationCb> notification_cb)
: sensor_attributes_static_(std::move(sensor_attributes_static)),
notification_cb_(notification_cb) {
if (!sensor_attributes_static_.devpath().empty()) {
devpath_ = sensor_attributes_static_.devpath();
}
UpdateThresholds(threshold_configs);
}
Sensor() = default;
// Buffers the sensor data and notifies the subscribers if the batch size
// reaches the threshold.
virtual void StoreSensorData(
const std::shared_ptr<const SensorValue>& sensor_data) = 0;
// Calculates the highest exceeded threshold type for the given reading.
static ThresholdType CalculateExceededThreshold(
double reading, const ThresholdConfigs& threshold_configs);
const SensorAttributesStatic sensor_attributes_static_;
mutable absl::Mutex sensor_attributes_dynamic_mutex_;
SensorAttributesDynamic sensor_attributes_dynamic_
ABSL_GUARDED_BY(sensor_attributes_dynamic_mutex_);
// Callback to be invoked when the batch size reaches the threshold.
// Not locked: The callback is instantiated in the constructor and is
// supposed to be invoked from a single io_context thread.
const std::optional<NotificationCb> notification_cb_ = std::nullopt;
// Threshold state for the sensor.
bool is_over_critical_ = false;
bool is_over_non_critical_ = false;
bool is_under_critical_ = false;
bool is_under_non_critical_ = false;
// If true, the sensor is ready for read. If false, the sensor is not ready
// for read and the sensor data will not be updated. This is used because some
// sensors are not ready to read when the corresponding power is off.
std::atomic<bool> ready_for_read_ = true;
std::string devpath_;
mutable absl::Mutex override_mutex_;
std::optional<SensorValue> override_value_ ABSL_GUARDED_BY(override_mutex_);
};
std::string GetTrimmedSensorName(absl::string_view sensor_name);
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SENSORS_SENSOR_BASE_H_