| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SENSORS_SENSOR_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SENSORS_SENSOR_H_ |
| |
| #include <chrono> // NOLINT |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/base/thread_annotations.h" |
| #include "absl/container/btree_set.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/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 "entity_common_config.pb.h" |
| #include "hal_common_config.pb.h" |
| #include "reading_transform_config.pb.h" |
| #include "tlbmc/configs/subscription_config.h" |
| #include "tlbmc/hal/sysfs/i2c.h" |
| #include "resource.pb.h" |
| #include "sensor.pb.h" |
| |
| namespace milotic_tlbmc { |
| |
| std::string GetTrimmedSensorName(absl::string_view sensor_name); |
| |
| // 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( |
| absl::string_view sensor_name, const SensorUnit& sensor_unit, |
| const HalCommonConfig& hal_common_config, |
| const EntityCommonConfig& entity_common_config, |
| const ReadingRangeConfigs& reading_range_configs, |
| const ReadingTransformConfig& reading_transform_config); |
| |
| virtual ~Sensor() = default; |
| |
| // Returns the latest sensor reading. |
| std::shared_ptr<const SensorValue> GetSensorData() const |
| ABSL_LOCKS_EXCLUDED(sensor_data_mutex_) { |
| absl::MutexLock lock(&sensor_data_mutex_); |
| return sensor_data_.empty() ? nullptr : sensor_data_.back(); |
| } |
| |
| std::vector<std::shared_ptr<const SensorValue>> GetSensorDataHistory() const |
| ABSL_LOCKS_EXCLUDED(sensor_data_mutex_) { |
| absl::MutexLock lock(&sensor_data_mutex_); |
| return {sensor_data_.begin(), sensor_data_.end()}; |
| } |
| |
| std::vector<std::shared_ptr<const SensorValue>> GetSensorDataHistorySince( |
| absl::Time start_time) const ABSL_LOCKS_EXCLUDED(sensor_data_mutex_); |
| |
| // 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`. |
| virtual absl::Status WriteReading(const SensorValue& value); |
| |
| // Returns the key of the sensor. A key shall be constant for a sensor's |
| // lifetime. |
| std::string GetKey() const { |
| return sensor_attributes_static_.attributes().key(); |
| } |
| |
| // Returns the config key of the board that contains this sensor. |
| std::string GetBoardConfigKey() const { |
| return sensor_attributes_static_.entity_common_config().board_config_key(); |
| } |
| |
| SensorAttributesDynamic GetSensorAttributesDynamic() const { |
| absl::MutexLock lock(&sensor_attributes_dynamic_mutex_); |
| return sensor_attributes_dynamic_; |
| } |
| |
| const SensorAttributesStatic& GetSensorAttributesStatic() const { |
| return sensor_attributes_static_; |
| } |
| |
| SensorMetrics GetSensorMetrics() const; |
| |
| // Subscribes to the sensor data. |
| absl::Status Subscribe(const SubscriptionParams* subscription_params); |
| // Deletes an existing subscription. |
| absl::Status Unsubscribe(const SubscriptionParams* subscription_params); |
| void UpdateState(State&& state); |
| // Updates the thresholds for the sensor. |
| void UpdateThresholds(const ThresholdConfigs& threshold_configs); |
| // Resize the buffer for the sensor. |
| void ResizeBuffer(size_t buffer_size); |
| // Resets the metrics for the sensor. This is used when the sensor is |
| // configured with a different sampling interval. |
| 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())); |
| } |
| |
| protected: |
| Sensor(SensorAttributesStatic&& sensor_attributes_static, |
| const ThresholdConfigs& threshold_configs, |
| std::optional<NotificationCb> notification_cb) |
| : sensor_attributes_static_(std::move(sensor_attributes_static)), |
| sensor_data_(static_cast<size_t>( |
| sensor_attributes_static_.entity_common_config().queue_size())), |
| notification_cb_(notification_cb) { |
| UpdateThresholds(threshold_configs); |
| } |
| Sensor() : sensor_data_(1) { |
| DLOG(INFO) << "Sensor created with default queue size of " |
| << sensor_data_.capacity(); |
| } |
| |
| // Buffers the sensor data and notifies the subscribers if the batch size |
| // reaches the threshold. |
| void StoreSensorData(const std::shared_ptr<const SensorValue>& sensor_data) |
| ABSL_LOCKS_EXCLUDED(sensor_data_mutex_, sensor_attributes_dynamic_mutex_); |
| |
| // Given the latency of the most recent hardware polling latency, updates the |
| // metrics. |
| void UpdateHardwarePollingMetrics(std::chrono::steady_clock::duration latency) |
| ABSL_LOCKS_EXCLUDED(sensor_metrics_mutex_); |
| |
| // Given the timestamp of the most recent software polling read start |
| // interval, updates the metrics. |
| void UpdateSoftwarePollingStartMetrics( |
| std::chrono::steady_clock::duration interval) |
| ABSL_LOCKS_EXCLUDED(sensor_metrics_mutex_); |
| |
| // Given the timestamp of the most recent software polling read end interval, |
| // updates the metrics. |
| void UpdateSoftwarePollingEndMetrics( |
| std::chrono::steady_clock::duration interval) |
| ABSL_LOCKS_EXCLUDED(sensor_metrics_mutex_); |
| |
| void SetLastRefreshStartTime(std::chrono::steady_clock::time_point time) { |
| // NOLINTNEXTLINE: Yocto's Abseil doesn't support non-pointer constructor. |
| absl::MutexLock lock(&sensor_metrics_mutex_); |
| last_refresh_start_time_ = time; |
| } |
| |
| std::chrono::steady_clock::time_point GetLastRefreshStartTime() const { |
| // NOLINTNEXTLINE: Yocto's Abseil doesn't support non-pointer constructor. |
| absl::MutexLock lock(&sensor_metrics_mutex_); |
| return last_refresh_start_time_; |
| } |
| |
| void SetLastRefreshEndTime(std::chrono::steady_clock::time_point time) { |
| // NOLINTNEXTLINE: Yocto's Abseil doesn't support non-pointer constructor. |
| absl::MutexLock lock(&sensor_metrics_mutex_); |
| last_refresh_end_time_ = time; |
| } |
| |
| std::chrono::steady_clock::time_point GetLastRefreshEndTime() const { |
| // NOLINTNEXTLINE: Yocto's Abseil doesn't support non-pointer constructor. |
| absl::MutexLock lock(&sensor_metrics_mutex_); |
| return last_refresh_end_time_; |
| } |
| |
| const SensorAttributesStatic sensor_attributes_static_; |
| |
| mutable absl::Mutex sensor_attributes_dynamic_mutex_; |
| SensorAttributesDynamic sensor_attributes_dynamic_ |
| ABSL_GUARDED_BY(sensor_attributes_dynamic_mutex_); |
| |
| mutable absl::Mutex sensor_data_mutex_; |
| // Front() returns the oldest data. |
| boost::circular_buffer<std::shared_ptr<const SensorValue>> sensor_data_ |
| ABSL_GUARDED_BY(sensor_data_mutex_); |
| |
| // The set of batch sizes that are subscribed to the sensor. |
| absl::btree_set<const SubscriptionParams*, SubscriptionParams::Compare> |
| subscription_params_ 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; |
| |
| mutable absl::Mutex sensor_metrics_mutex_; |
| // The total number of hardware reads. |
| uint64_t total_hardware_read_cnt_ = 0; |
| // The total latency of hardware reads in milliseconds. |
| uint64_t total_hardware_latency_ms_ = 0; |
| // The total software polling start interval in milliseconds. |
| uint64_t total_software_polling_start_interval_ms_ = 0; |
| // The total number of software polling start. |
| uint64_t total_software_polling_start_cnt_ = 0; |
| // The total software polling end interval in milliseconds. |
| uint64_t total_software_polling_end_interval_ms_ = 0; |
| // The total number of software polling end. |
| uint64_t total_software_polling_end_cnt_ = 0; |
| std::chrono::steady_clock::time_point last_refresh_start_time_ = |
| std::chrono::steady_clock::time_point::min(); |
| std::chrono::steady_clock::time_point last_refresh_end_time_ = |
| std::chrono::steady_clock::time_point::min(); |
| }; |
| |
| } // namespace milotic_tlbmc |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SENSORS_SENSOR_H_ |