Actions
Actions are for long-running tasks. They provide Goal semantics, streaming Feedback, and a final Result.
Golang Support
The mt_service library is written in Rust but it can be used from Golang as well. See our Go Guide on how to create a Go project for Minot.
mt_action implements a full ROS2-compatible action goal lifecycle natively over Minot. It handles:
- Goal Acceptance: Negotiating whether a goal is accepted or rejected.
- Goal Execution: Running the actual task in the background.
- Feedback Streaming: Periodically sending progress updates to the client.
- Result Reporting: Returning the final result once the goal reaches a terminal state.
- Cancellation: Safely aborting or canceling ongoing goals.
For more detailed implementation details, we recommend checking the mt_action/examples/ directory in the Minot repository, which contains complete code for action clients and servers matching ROS2 conventions.
Client
The client sends a goal and monitors its progress.
use anyhow::anyhow;
use log::{info, error};
use mt_action::{ActionClient, messages::*};
use mt_pubsub::{Node, NodeConfig};
use ros2_interfaces_jazzy_rkyv::std_msgs::msg;
use std::sync::Arc;
use tokio::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let node = Arc::new(Node::create(NodeConfig::new("action_client")).await?);
// Create client (Goal, Feedback, Result types)
let client: Arc<ActionClient<msg::String, msg::String, msg::String>> =
ActionClient::new(node.clone(), "/my_action".to_owned()).await?;
if !ActionClient::wait_for_action_server(client.clone(), Duration::from_secs(5)).await {
return Err(anyhow!("Action server not ready"));
}
// Spawn feedback listener
let feedback_client = client.clone();
tokio::spawn(async move {
while let Ok(fb) = ActionClient::get_feedback(feedback_client.clone()).await {
info!("Got feedback: {}", fb.msg.data);
}
});
// Send a goal
let res = ActionClient::send_goal(
client.clone(),
ActionSendGoalRequest { id: 1, goal: msg::String { data: "Start task".to_owned() } },
None
).await?;
if res.accepted {
info!("Goal accepted!");
// Wait for final result
let result = ActionClient::get_result(client.clone(), ActionGetResultRequest { id: 1 }, None).await?;
info!("Final state: {:?}", result.status);
}
Ok(())
}
Server
The server implements the state machine for processing goals.
The implementation involves creating a struct that implements the Action trait and starting the ActionServer. See mt_action/examples/server.rs for a full boilerplate!