# Actions
**Executable context-aware behaviors for your robotic system.**
Actions are not just static function calls; they are **dynamic, context-aware routines** that can adapt their parameters in real-time based on live system data.
They can represent:
- Component Behaviors - Routines defined within your components. *e.g., Stopping the robot, executing a motion pattern, or saying a sentence.*
- System Behaviors - Lifecycle management, configuration and plumbing. *e.g., Reconfiguring a node, restarting a driver, or re-routing input streams.*
- User Custom Behaviors - Arbitrary Python functions. *e.g., Calling an external REST API, logging to a file, or sending a slack notification.*
## Trigger Mechanisms
Actions sit dormant until activated by one of two mechanisms:
- {material-regular}`flash_on;1.2em;sd-text-primary` Event-Driven (Reflexive) - Triggered instantly when a specific **Event** condition is met.
**Example:** "Obstacle Detected" $\rightarrow$ `stop_robot()`
- {material-regular}`healing;1.2em;sd-text-primary` Fallback-Driven (Restorative) - Triggered automatically by a Component when its internal **Health Status** degrades.
**Example:** "Camera Driver Failed" $\rightarrow$ `restart_driver()`
## The `Action` Class
At its core, the `Action` class is a wrapper around any Python callable. It packages a function along with its arguments, preparing them for execution at runtime.
But unlike standard Python functions, Sugarcoat Actions possess a superpower: [Dynamic Data Injection](#dynamic-data-injection). You can bind their arguments directly to live ROS2 Topics, allowing the Action to fetch the latest topic message or a specific message argument the moment it triggers.
```python
class Action:
def __init__(self, method: Callable, args: tuple = (), kwargs: Optional[Dict] = None):
```
- method: The function or routine to execute.
- args: Positional arguments (can be static values OR dynamic Topic values).
- kwargs: Keyword arguments (can be static values OR dynamic Topic values).
## Basic Usage
```python
from ros_sugar.component import BaseComponent
from ros_sugar.core import Action
import logging
def custom_routine():
logging.info("I am executing an action!")
my_component = BaseComponent(node_name='test_component')
# 1. Component Method
action1 = Action(method=my_component.start)
# 2. Method with keyword arguments
action2 = Action(method=my_component.update_parameter, kwargs={"param_name": "fallback_rate", "new_value": 1000})
# 3. External Function
action3 = Action(method=custom_routine)
```
## Dynamic Data Injection
**This is Sugarcoat's superpower.**
You can create complex, context-aware behaviors without writing any "glue code" or custom parsers.
When you bind an Action argument to a `Topic`, the system automatically resolves the binding at runtime, fetching the current value from the topic attributes and injecting it into your function.
### Example: Cross-Topic Data Access
**Scenario**: An event occurs on Topic 1. You want to log a message that includes the current status from Topic 2 and a sensor reading from Topic 3.
```python
from ros_sugar.core import Event, Action
from ros_sugar.io import Topic
# 1. Define Topics
topic_1 = Topic(name="system_alarm", msg_type="Bool")
topic_2 = Topic(name="robot_mode", msg_type="String")
topic_3 = Topic(name="battery_voltage", msg_type="Float32")
# 2. Define the Event
# Trigger when Topic 1 becomes True
event_on_first_topic = Event(topic_1.msg.data.is_true())
# 3. Define the Target Function
def log_context_message(mode, voltage):
print(f"System Alarm! Current Mode: {mode}, Voltage: {voltage}V")
# 4. Define the Dynamic Action
# We bind the function arguments directly to the data fields of Topic 2 and Topic 3
my_action = Action(
method=log_context_message,
# At runtime, these are replaced by the actual values from the topics
args=(topic_2.msg.data, topic_3.msg.data)
)
```
## Pre-defined Actions
Sugarcoat provides a suite of pre-defined, thread-safe actions for managing components and system resources via the `ros_sugar.actions` module.
:::{admonition} Import Note
:class: tip
All pre-defined actions are **keyword-only** arguments. They can be imported directly:
`from ros_sugar.actions import start, stop, reconfigure`
:::
### Component-Level Actions
These actions directly manipulate the state or configuration of a specific `BaseComponent` derived object.
| Action Method | Arguments | Description |
| :-------------------------------------- | :------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------ |
| **`start`** | `component` | Triggers the component's Lifecycle transition to **Active**. |
| **`stop`** | `component` | Triggers the component's Lifecycle transition to **Inactive**. |
| **`restart`** | `component`
`wait_time` (opt) | Stops the component, waits `wait_time` seconds (default 0), and Starts it again. |
| **`reconfigure`** | `component`
`new_config`
`keep_alive` | Reloads the component with a new configuration object or file path.
`keep_alive=True` (default) keeps the node running during update. |
| **`update_parameter`** | `component`
`param_name`
`new_value`
`keep_alive` | Updates a **single** configuration parameter. |
| **`update_parameters`** | `component`
`params_names`
`new_values`
`keep_alive` | Updates **multiple** configuration parameters simultaneously. |
| **`send_component_service_request`** | `component`
`srv_request_msg` | Sends a request to the component's main service with a specific message. |
| **`trigger_component_service`** | `component` | Triggers the component's main service.
Creates the request message dynamically during runtime from the incoming Event topic data. |
| **`send_component_action_server_goal`** | `component`
`request_msg` | Sends a goal to the component's main action server with a specific message. |
| **`trigger_component_action_server`** | `component` | Triggers the component's main action server.
Creates the request message dynamically during runtime from the incoming Event topic data. |
### System-Level Actions
These actions interact with the broader ROS2 system and are executed by the central `Monitor`.
| Action Method | Arguments | Description |
| :-------------------------- | :---------------------------------------------- | :----------------------------------------------------------------------- |
| **`log`** | `msg`
`logger_name` (opt) | Logs a message to the ROS console. |
| **`publish_message`** | `topic`
`msg`
`publish_rate`/`period` | Publishes a specific message to a topic. Can be single-shot or periodic. |
| **`send_srv_request`** | `srv_name`
`srv_type`
`srv_request_msg` | Sends a request to a ROS 2 Service with a specific message. |
| **`trigger_service`** | `srv_name`
`srv_type` | Triggers the a given ROS2 service. |
| **`send_action_goal`** | `server_name`
`server_type`
`request_msg` | Sends a specific goal to a ROS 2 Action Server. |
| **`trigger_action_server`** | `server_name`
`server_type` | Triggers a given ROS2 action server. |
:::{admonition} Automatic Data Conversion
:class: note
When using **`trigger_*`** actions paired with an Event, Sugarcoat attempts to create the required service/action request from the incoming Event topic data automatically via **duck typing**.
If automatic conversion is not possible, or if the action is not paired with an Event, it sends a default (empty) request.
:::