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