# 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. :::