| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_HFT_ADAPTER_DATA_SOURCE_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_HFT_ADAPTER_DATA_SOURCE_H_ |
| |
| #include <cstddef> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/hash/hash.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/substitute.h" |
| #include "absl/time/time.h" |
| #include "g3/macros.h" |
| #include "fru_identifier.pb.h" |
| #include "identifier.pb.h" |
| #include "payload.pb.h" |
| #include "sensor_identifier.pb.h" |
| #include "subscription_params.pb.h" |
| |
| namespace milotic_hft { |
| |
| class SensorMutationBatch; |
| |
| // Abstracts the underlying data source for the High Frequency Telemetry. |
| // This is implemented by tlBMC and gSys. |
| class DataSource { |
| public: |
| virtual ~DataSource() = default; |
| |
| // Configures the data source for the given `identifier` with the given |
| // `sampling_interval_ms`. |
| // `max_buffer_size` is the maximum number of readings that the data source |
| // should buffer. |
| // Returns an error if the data source cannot be configured. |
| virtual absl::Status ConfigureSamplingInterval(const Identifier& identifier, |
| int sampling_interval_ms) = 0; |
| |
| // Configures the data source for the given `identifier` with the given |
| // `max_batch_size`. |
| // Returns an error if the data source cannot be configured. |
| virtual absl::Status ConfigureBatchSize(const Identifier& identifier, |
| int max_batch_size) = 0; |
| |
| // Resets the data source to the default configuration for the given |
| // `identifier`. |
| virtual absl::Status ResetSamplingIntervalToDefault( |
| const Identifier& identifier) = 0; |
| |
| // Resets the batch size to the default configuration for the given |
| // `identifier`. |
| virtual absl::Status ResetBatchSizeToDefault( |
| const Identifier& identifier) = 0; |
| |
| // Collects the data from the data source for the given `identifier` since the |
| // `start_time`. |
| // Returns an error if the data source is cannot serve the data. |
| virtual absl::StatusOr<Payload> Collect(const Identifier& identifier, |
| absl::Time start_time) const = 0; |
| |
| virtual absl::StatusOr<std::vector<Identifier>> GetIdentifiersForResourceType( |
| const SubscriptionPolicy::ResourceType& resource_type) const = 0; |
| |
| // Returns the name of the data source. This is used for logging and |
| // debugging. |
| virtual std::string GetName() const = 0; |
| |
| // Creates a sensor mutation batch for the given resource type and |
| // identifiers. Only one resource type per batch. |
| virtual std::unique_ptr<SensorMutationBatch> CreateSensorMutationBatch( |
| SubscriptionPolicy::ResourceType resource_type, |
| std::vector<Identifier> identifiers); |
| |
| // Creates a SensorMutationBatch for the given `resource_type`. |
| // GetIdentifiersForResourceType will be called. |
| virtual absl::StatusOr<std::unique_ptr<SensorMutationBatch>> |
| CreateSensorMutationBatch(SubscriptionPolicy::ResourceType resource_type) { |
| ECCLESIA_ASSIGN_OR_RETURN(auto ids, |
| GetIdentifiersForResourceType(resource_type)); |
| return CreateSensorMutationBatch(resource_type, std::move(ids)); |
| } |
| }; |
| |
| // Applies multiple sensor mutations to the data source. |
| // Default behavior shifts changes to Apply(), through the standard DataSource |
| // interface. |
| class SensorMutationBatch { |
| // An optional sampling interval and an optional max batch size |
| using Mutation = std::pair<std::optional<int>, std::optional<int>>; |
| |
| public: |
| virtual ~SensorMutationBatch() = default; |
| |
| virtual absl::Status SetSamplingInterval(const Identifier& identifier, |
| int sampling_interval_ms) & { |
| ECCLESIA_ASSIGN_OR_RETURN(auto& mutation, GetMutation(identifier)); |
| mutation.first = sampling_interval_ms; |
| return absl::OkStatus(); |
| } |
| |
| virtual absl::Status SetBatchSize(const Identifier& identifier, |
| int max_batch_size) & { |
| ECCLESIA_ASSIGN_OR_RETURN(auto& mutation, GetMutation(identifier)); |
| mutation.second = max_batch_size; |
| return absl::OkStatus(); |
| } |
| |
| virtual absl::Status ResetSensor(const Identifier& identifier) & { |
| ECCLESIA_RETURN_IF_ERROR(CheckIdentifier(identifier)); |
| resets_.push_back(identifier); |
| return absl::OkStatus(); |
| } |
| |
| // Applies the mutations to the data source |
| virtual absl::Status Apply() && { |
| for (const auto& [identifier, mutation] : mutations_) { |
| if (mutation.second.has_value()) { |
| ECCLESIA_RETURN_IF_ERROR(data_source_->ConfigureBatchSize( |
| identifier, mutation.second.value())); |
| } |
| if (mutation.first.has_value()) { |
| ECCLESIA_RETURN_IF_ERROR(data_source_->ConfigureSamplingInterval( |
| identifier, mutation.first.value())); |
| } |
| } |
| for (const auto& identifier : resets_) { |
| ECCLESIA_RETURN_IF_ERROR( |
| data_source_->ResetSamplingIntervalToDefault(identifier)); |
| ECCLESIA_RETURN_IF_ERROR( |
| data_source_->ResetBatchSizeToDefault(identifier)); |
| } |
| return absl::OkStatus(); |
| } |
| |
| protected: |
| friend class DataSource; |
| |
| SensorMutationBatch(DataSource* data_source, |
| const std::vector<Identifier>& identifiers) |
| : data_source_(data_source), |
| identifiers_(identifiers.begin(), identifiers.end()) {} |
| |
| // Early detection of bad identifiers prevent Apply() failures. |
| absl::Status CheckIdentifier(const Identifier& identifier) { |
| if (!identifiers_.contains(identifier)) { |
| return absl::NotFoundError( |
| absl::Substitute("Identifier $0 is not found", identifier)); |
| } |
| return absl::OkStatus(); |
| } |
| |
| private: |
| struct IdentifierHash { |
| size_t operator()(const Identifier& v) const { |
| return v.identifier_case() == Identifier::kSensorIdentifier |
| ? absl::HashOf(1, v.sensor_identifier().name()) |
| : absl::HashOf(2, v.fru_identifier().barepath()); |
| } |
| }; |
| |
| struct IdentifierEqual { |
| bool operator()(const Identifier& lhs, const Identifier& rhs) const { |
| if (lhs.identifier_case() != rhs.identifier_case()) { |
| return false; |
| } |
| return lhs.identifier_case() == Identifier::kSensorIdentifier |
| ? lhs.sensor_identifier().name() == |
| rhs.sensor_identifier().name() |
| : lhs.fru_identifier().barepath() == |
| rhs.fru_identifier().barepath(); |
| } |
| }; |
| |
| absl::StatusOr<Mutation&> GetMutation(const Identifier& identifier) { |
| ECCLESIA_RETURN_IF_ERROR(CheckIdentifier(identifier)); |
| return mutations_[identifier]; |
| } |
| |
| DataSource* data_source_; |
| // All sampling interval and batch size changes, per sensor identifier |
| absl::flat_hash_map<Identifier, Mutation, IdentifierHash, IdentifierEqual> |
| mutations_; |
| std::vector<Identifier> resets_; |
| absl::flat_hash_set<Identifier, IdentifierHash, IdentifierEqual> identifiers_; |
| }; |
| |
| inline std::unique_ptr<SensorMutationBatch> |
| DataSource::CreateSensorMutationBatch( |
| SubscriptionPolicy::ResourceType /*resource_type*/, |
| std::vector<Identifier> identifiers) { |
| return std::unique_ptr<SensorMutationBatch>( |
| new SensorMutationBatch(this, std::move(identifiers))); |
| } |
| |
| } // namespace milotic_hft |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_HFT_ADAPTER_DATA_SOURCE_H_ |