blob: b642a2c6859ebee1e6579acd65d5452ad6b257d2 [file]
#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_