| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_LED_COLLECTOR_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_LED_COLLECTOR_H_ |
| |
| #include <atomic> |
| #include <limits> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/time/time.h" |
| #include "time/clock.h" |
| #include <nlohmann/json.hpp> |
| #include "tlbmc/collector/collector.h" |
| #include "led_config.pb.h" |
| #include "power_control.pb.h" |
| #include "tlbmc/expression/expression.h" |
| #include "tlbmc/hal/led/led.h" |
| #include "tlbmc/hal/sysfs/led.h" |
| #include "tlbmc/scheduler/scheduler.h" |
| |
| namespace milotic_tlbmc { |
| |
| class PowerControlCollector; |
| |
| struct LedPresetBehavior { |
| Led& led; |
| LedBehavior behavior; |
| }; |
| |
| // Collector for managing LEDs and applying presets in tlBMC. |
| // |
| // The collector owns a set of generic Led (see hal/led/led.h), keyed by their |
| // configured name, and a set of named presets. LEDs are created via |
| // hardware-backend factory methods (such as SysfsLed::Create()). |
| // The collector is agnostic to the underlying hardware. |
| class LedCollector : public Collector { |
| public: |
| static constexpr absl::Duration kPolicyEvaluationInterval = absl::Seconds(1); |
| |
| struct Params { |
| LedConfigs led_configs; |
| const LedSysfs& led_sysfs; |
| ecclesia::Clock* clock = ecclesia::Clock::RealClock(); |
| const PowerControlCollector* power_control_collector = nullptr; |
| }; |
| |
| struct CompiledConditionVariable { |
| explicit CompiledConditionVariable(LedConditionVariable variable) |
| : variable(std::move(variable)) {} |
| |
| const LedConditionVariable variable; |
| absl::AnyInvocable<absl::StatusOr<double>(CompiledConditionVariable&)> |
| update_callback; |
| std::atomic<double> value = std::numeric_limits<double>::quiet_NaN(); |
| nlohmann::json ToJson() const; |
| }; |
| |
| struct CompiledPolicy { |
| CompiledPolicy(absl::string_view name, absl::string_view preset_name) |
| : name(name), preset_name(preset_name) {} |
| virtual ~CompiledPolicy() = default; |
| |
| std::string name; |
| std::string preset_name; |
| std::atomic<absl::Time> last_applied_time = absl::InfinitePast(); |
| }; |
| |
| struct ConditionPolicy : CompiledPolicy { |
| ConditionPolicy(absl::string_view name_, absl::string_view preset_name_, |
| absl::string_view condition_expression, |
| std::unique_ptr<Expression> expression) |
| : CompiledPolicy(name_, preset_name_), |
| condition_expression(condition_expression), |
| expression(std::move(expression)) {} |
| |
| std::string condition_expression; |
| std::unique_ptr<Expression> expression; |
| std::atomic<double> last_evaluation_result = |
| std::numeric_limits<double>::quiet_NaN(); |
| }; |
| |
| struct TriggerPolicy : CompiledPolicy { |
| TriggerPolicy(absl::string_view name_, absl::string_view preset_name_, |
| absl::string_view event_name) |
| : CompiledPolicy(name_, preset_name_), event_name(event_name) {} |
| |
| std::string event_name; |
| }; |
| |
| static absl::StatusOr<std::unique_ptr<LedCollector>> Create( |
| const Params& params); |
| |
| ~LedCollector() override { |
| // Destroy the scheduler before the LED handles so any in-flight blink tasks |
| // are cancelled while the LEDs they reference are still alive. |
| task_scheduler_.reset(); |
| } |
| |
| nlohmann::json ToJson() const override; |
| nlohmann::json GetSchedulerStats() const override { |
| return task_scheduler_->ToJson(); |
| } |
| |
| // Returns the current behavior of a specific LED by name. |
| virtual absl::StatusOr<LedBehavior> GetLedBehavior( |
| absl::string_view led_name) const; |
| |
| // Triggers an event-driven LED policy at the current time. |
| virtual absl::Status Trigger(absl::string_view event); |
| |
| // Returns all LED names managed by this collector. |
| virtual std::vector<std::string> GetLedNames() const; |
| |
| // Returns all preset names managed by this collector. |
| virtual std::vector<std::string> GetPresetNames() const; |
| |
| // Returns all indicator configurations mapped by system_id. |
| const absl::flat_hash_map<std::string, LedIndicator>& GetIndicators() const { |
| return indicators_; |
| } |
| |
| // Returns true if at least one indicator LED configuration exists. |
| virtual bool HasIndicatorConfig() const; |
| |
| // Returns true if an indicator LED configuration exists for `system_id` |
| // or if a default/fallback indicator (system_id == "") is configured. |
| virtual bool HasIndicatorConfig(absl::string_view system_id) const; |
| |
| // Returns the name of the LED assigned to indicate the given `system_id`. |
| // Falls back to the default indicator ("") if `system_id` is not found. |
| // Returns FailedPreconditionError if no indicators are configured, or |
| // NotFoundError if neither `system_id` nor the default indicator exists. |
| virtual absl::StatusOr<std::string> GetIndicatorLedName( |
| absl::string_view system_id) const; |
| virtual absl::StatusOr<std::string> GetIndicatorLedName() const; |
| |
| // Returns the current state of the indicator LED for `system_id` |
| // (falling back to the default indicator if not found). |
| virtual absl::StatusOr<LedState> GetIndicatorState( |
| absl::string_view system_id) const; |
| virtual absl::StatusOr<LedState> GetIndicatorState() const; |
| |
| // Applies `state` to the configured indicator for `system_id`. |
| // If `state` is LED_STATE_BLINK and no blink event policy is configured, |
| // it automatically falls back to the event_on policy. |
| virtual absl::Status SetIndicatorState(absl::string_view system_id, |
| LedState state); |
| virtual absl::Status SetIndicatorState(LedState state); |
| |
| protected: |
| LedCollector() = default; |
| |
| // Sets the behavior of a specific LED by name. |
| virtual absl::Status SetLedBehavior(absl::string_view led_name, |
| const LedBehavior& behavior, |
| absl::Time timestamp); |
| |
| // Applies a preset (sets the behavior for all member LEDs in the preset). |
| virtual absl::Status ApplyPreset(absl::string_view preset_name, |
| absl::Time timestamp); |
| |
| // Evaluates LED policies and applies corresponding presets or default |
| // behaviors. |
| virtual absl::Status EvaluatePolicies(); |
| |
| private: |
| LedCollector( |
| std::unique_ptr<TaskScheduler> task_scheduler, |
| absl::flat_hash_map<std::string, std::unique_ptr<Led>> leds, |
| absl::flat_hash_map<std::string, std::vector<LedPresetBehavior>> presets, |
| std::vector<std::unique_ptr<CompiledConditionVariable>> |
| condition_variables, |
| std::vector<std::unique_ptr<ConditionPolicy>> condition_policies, |
| absl::flat_hash_map<std::string, std::unique_ptr<TriggerPolicy>> |
| trigger_policies, |
| absl::flat_hash_map<std::string, LedIndicator> indicators, |
| const PowerControlCollector* power_control_collector, |
| ecclesia::Clock* clock = ecclesia::Clock::RealClock()) |
| : leds_(std::move(leds)), |
| presets_(std::move(presets)), |
| condition_variables_(std::move(condition_variables)), |
| condition_policies_(std::move(condition_policies)), |
| trigger_policies_(std::move(trigger_policies)), |
| indicators_(std::move(indicators)), |
| power_control_collector_(power_control_collector), |
| clock_(clock), |
| task_scheduler_(std::move(task_scheduler)) {} |
| |
| // Looks up the indicator for `system_id`, falling back to the default |
| // indicator ("") if present. |
| absl::StatusOr<const LedIndicator*> GetIndicator( |
| absl::string_view system_id) const; |
| |
| // Applies a policy and updates its last applied time to `timestamp`. |
| // |
| // An explicit `timestamp` is used during `EvaluatePolicies()` so that all |
| // member LEDs updated by active policies share the same evaluation time. |
| // When `EvaluatePolicies()` subsequently calls `SetToDefaultBehavior()` on |
| // all LEDs with the same timestamp, LEDs already set in this cycle will |
| // ignore the default update (since `timestamp <= last_update_time_`), |
| // ensuring that default behaviors only apply to LEDs not set by any active |
| // policy. |
| absl::Status ApplyPolicy(CompiledPolicy& policy, absl::Time timestamp); |
| |
| // Applies a policy and updates its last applied time using the current clock |
| // time. |
| absl::Status ApplyPolicy(CompiledPolicy& policy); |
| |
| absl::StatusOr<absl::flat_hash_map<std::string, double>> |
| ResolveConditionVariables(); |
| |
| const absl::flat_hash_map<std::string, std::unique_ptr<Led>> leds_; |
| const absl::flat_hash_map<std::string, std::vector<LedPresetBehavior>> |
| presets_; |
| std::vector<std::unique_ptr<CompiledConditionVariable>> condition_variables_; |
| const std::vector<std::unique_ptr<ConditionPolicy>> condition_policies_; |
| const absl::flat_hash_map<std::string, std::unique_ptr<TriggerPolicy>> |
| trigger_policies_; |
| const absl::flat_hash_map<std::string, LedIndicator> indicators_; |
| const PowerControlCollector* power_control_collector_ = nullptr; |
| ecclesia::Clock* clock_ = ecclesia::Clock::RealClock(); |
| |
| // Reserved for future GPIO-backed LEDs that blink in software. Sysfs LEDs |
| // blink via the kernel timer trigger and do not use the scheduler. |
| // TaskScheduler must be declared last so it is destroyed first. |
| std::unique_ptr<TaskScheduler> task_scheduler_; |
| |
| friend class LedCollectorTest; |
| }; |
| |
| class EmptyLedCollector final : public LedCollector { |
| public: |
| static std::unique_ptr<EmptyLedCollector> Create(); |
| |
| nlohmann::json GetSchedulerStats() const override; |
| |
| nlohmann::json ToJson() const override; |
| |
| absl::Status EvaluatePolicies() override { return absl::OkStatus(); } |
| }; |
| |
| } // namespace milotic_tlbmc |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_LED_COLLECTOR_H_ |