Implement LED trigger policy & LED Indicator PiperOrigin-RevId: 975754032 Change-Id: I9aaa4e75a63aa1e5300ef0cdff87fc208613e862
diff --git a/tlbmc/collector/led_collector.cc b/tlbmc/collector/led_collector.cc index 76ac82f..079c8da 100644 --- a/tlbmc/collector/led_collector.cc +++ b/tlbmc/collector/led_collector.cc
@@ -82,6 +82,8 @@ } std::vector<std::unique_ptr<ConditionPolicy>> condition_policies; + absl::flat_hash_map<std::string, std::unique_ptr<TriggerPolicy>> + trigger_policies; condition_policies.reserve(params.led_configs.policies_size()); for (const LedPolicy& policy : params.led_configs.policies()) { if (policy.preset_name().empty()) { @@ -100,8 +102,25 @@ policy.condition().expression(), std::move(expr))); break; } - case LedPolicy::EventCase::kTrigger: - return absl::UnimplementedError("LED triggers are not yet supported"); + case LedPolicy::EventCase::kTrigger: { + absl::string_view event_name = policy.trigger().event(); + if (event_name.empty()) { + return absl::InvalidArgumentError("Trigger event cannot be empty"); + } + auto compiled_policy = std::make_unique<TriggerPolicy>( + policy.name(), policy.preset_name(), event_name); + + // TODO(b/546330351): Implement GPIO triggers. + + auto [_, inserted] = trigger_policies.try_emplace( + event_name, std::move(compiled_policy)); + if (!inserted) { + return absl::InvalidArgumentError( + absl::StrCat("Duplicate trigger event: ", event_name)); + } + break; + } + case LedPolicy::EventCase::EVENT_NOT_SET: default: return absl::InvalidArgumentError("LED policy event must be set"); } @@ -174,6 +193,30 @@ } bool has_conditional_policies = !condition_policies.empty(); + absl::flat_hash_map<std::string, LedIndicator> indicators; + for (const LedIndicator& indicator : params.led_configs.indicators()) { + if (indicator.led_name().empty()) { + return absl::InvalidArgumentError("Indicator led_name cannot be empty"); + } + if (!leds.contains(indicator.led_name())) { + return absl::NotFoundError( + absl::StrCat("Indicator LED not found: ", indicator.led_name())); + } + if (indicator.event_on().empty()) { + return absl::InvalidArgumentError("Indicator event_on cannot be empty"); + } + if (indicator.event_off().empty()) { + return absl::InvalidArgumentError("Indicator event_off cannot be empty"); + } + if (indicator.event_blink().empty()) { + return absl::InvalidArgumentError( + "Indicator event_blink cannot be empty"); + } + if (!indicators.try_emplace(indicator.system_id(), indicator).second) { + return absl::InvalidArgumentError(absl::StrCat( + "Duplicate indicator system_id: ", indicator.system_id())); + } + } if (!has_conditional_policies) { for (const auto& [_, led] : leds) { @@ -184,6 +227,7 @@ auto collector = absl::WrapUnique(new LedCollector( std::move(task_scheduler), std::move(leds), std::move(presets), std::move(compiled_condition_variables), std::move(condition_policies), + std::move(trigger_policies), std::move(indicators), params.power_control_collector, params.clock)); if (has_conditional_policies) { @@ -285,6 +329,89 @@ return ApplyPolicy(policy, clock_->Now()); } +absl::Status LedCollector::Trigger(absl::string_view event) { + auto it = trigger_policies_.find(event); + if (it == trigger_policies_.end()) { + return absl::NotFoundError( + absl::StrCat("Trigger event not found: ", event)); + } + return ApplyPolicy(*it->second); +} + +absl::StatusOr<const LedIndicator*> LedCollector::GetIndicator( + absl::string_view system_id) const { + if (indicators_.empty()) { + return absl::FailedPreconditionError("Indicator LED is not configured"); + } + auto it = indicators_.find(system_id); + if (it != indicators_.end()) { + return &it->second; + } + it = indicators_.find(""); + if (it != indicators_.end()) { + return &it->second; + } + return absl::NotFoundError( + absl::StrCat("Indicator LED not found for system: ", system_id)); +} + +bool LedCollector::HasIndicatorConfig() const { return !indicators_.empty(); } + +bool LedCollector::HasIndicatorConfig(absl::string_view system_id) const { + return indicators_.contains(system_id) || indicators_.contains(""); +} + +absl::StatusOr<std::string> LedCollector::GetIndicatorLedName( + absl::string_view system_id) const { + ECCLESIA_ASSIGN_OR_RETURN(const LedIndicator* indicator, + GetIndicator(system_id)); + return indicator->led_name(); +} + +absl::StatusOr<std::string> LedCollector::GetIndicatorLedName() const { + return GetIndicatorLedName(""); +} + +absl::StatusOr<LedState> LedCollector::GetIndicatorState( + absl::string_view system_id) const { + ECCLESIA_ASSIGN_OR_RETURN(const LedIndicator* indicator, + GetIndicator(system_id)); + ECCLESIA_ASSIGN_OR_RETURN(LedBehavior behavior, + GetLedBehavior(indicator->led_name())); + return behavior.state(); +} + +absl::StatusOr<LedState> LedCollector::GetIndicatorState() const { + return GetIndicatorState(""); +} + +absl::Status LedCollector::SetIndicatorState(absl::string_view system_id, + LedState state) { + ECCLESIA_ASSIGN_OR_RETURN(const LedIndicator* indicator, + GetIndicator(system_id)); + switch (state) { + case LED_STATE_ON: + return Trigger(indicator->event_on()); + case LED_STATE_BLINK: { + absl::Status status = Trigger(indicator->event_blink()); + if (absl::IsNotFound(status)) { + return Trigger(indicator->event_on()); + } + return status; + } + case LED_STATE_OFF: + return Trigger(indicator->event_off()); + case LED_STATE_UNSPECIFIED: + default: + return absl::InvalidArgumentError( + absl::StrCat("Invalid LED state: ", LedState_Name(state))); + } +} + +absl::Status LedCollector::SetIndicatorState(LedState state) { + return SetIndicatorState("", state); +} + absl::Status LedCollector::EvaluatePolicies() { absl::flat_hash_map<std::string, double> variable_map; ECCLESIA_ASSIGN_OR_RETURN(variable_map, ResolveConditionVariables()); @@ -383,6 +510,20 @@ } response["condition_policies"] = condition_policies; + nlohmann::json trigger_policies = nlohmann::json::object(); + for (const auto& [event, policy] : trigger_policies_) { + nlohmann::json policy_json; + policy_json["name"] = policy->name; + policy_json["preset_name"] = policy->preset_name; + policy_json["event"] = policy->event_name; + const absl::Time last_applied_time = policy->last_applied_time.load(); + if (last_applied_time != absl::InfinitePast()) { + policy_json["last_applied_time"] = absl::FormatTime(last_applied_time); + } + trigger_policies[event] = policy_json; + } + response["trigger_policies"] = trigger_policies; + return response; }
diff --git a/tlbmc/collector/led_collector.h b/tlbmc/collector/led_collector.h index dccd2b7..b642a2c 100644 --- a/tlbmc/collector/led_collector.h +++ b/tlbmc/collector/led_collector.h
@@ -85,6 +85,14 @@ 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); @@ -103,12 +111,48 @@ 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; @@ -133,16 +177,26 @@ 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 @@ -166,6 +220,9 @@ 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();
diff --git a/tlbmc/led_config.proto b/tlbmc/led_config.proto index cc4e12b..458c807 100644 --- a/tlbmc/led_config.proto +++ b/tlbmc/led_config.proto
@@ -89,9 +89,9 @@ string expression = 1; } -enum LedTrigger { - LED_TRIGGER_UNSPECIFIED = 0; - // TODO: b/546330351 - impl GPIO trigger +message LedTrigger { + // Name of the event that activates this policy (e.g., "indicator:on"). + string event = 1; } message LedPolicy { @@ -103,9 +103,26 @@ } } +// Configuration mapping Redfish indicator LED states to trigger events. +message LedIndicator { + // System ID of the LED (e.g., "system", "chassis"). + // If not set, the LED is applied to unmatched systems as a fallback. + string system_id = 1; + // Name of the LED to control, matching a defined LedConfig.name. + string led_name = 2; + // Event name emitted when the indicator is set to ON. + string event_on = 3 [default = "indicator:on"]; + // Event name emitted when the indicator is set to OFF. + string event_off = 4 [default = "indicator:off"]; + // Event name emitted when the indicator is set to BLINK. + string event_blink = 5 [default = "indicator:blink"]; +} + message LedConfigs { repeated LedConfig leds = 1; repeated LedPreset presets = 2; repeated LedConditionVariable variables = 3; repeated LedPolicy policies = 4; + // Indicator configurations mapping system IDs to LED trigger events. + repeated LedIndicator indicators = 5; }