Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for actions #295

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions examples/minimal_action_client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "examples_rclrs_minimal_action_client"
version = "0.3.1"
# This project is not military-sponsored, Jacob's employment contract just requires him to use this email address
authors = ["Esteve Fernandez <[email protected]>", "Nikolai Morin <[email protected]>", "Jacob Hassold <[email protected]>"]
edition = "2021"

[[bin]]
name = "minimal_action_client"
path = "src/minimal_action_client.rs"

[dependencies]
anyhow = {version = "1", features = ["backtrace"]}

[dependencies.rclrs]
version = "0.4"

[dependencies.rosidl_runtime_rs]
version = "0.4"

[dependencies.example_interfaces]
version = "*"
26 changes: 26 additions & 0 deletions examples/minimal_action_client/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>examples_rclrs_minimal_action_client</name>
<version>0.3.1</version>
<description>Minimal action client examples for rclrs.</description>
<maintainer email="[email protected]">Esteve Fernandez</maintainer>
<maintainer email="[email protected]">Nikolai Morin</maintainer>
<!-- This project is not military-sponsored, Jacob's employment contract just requires him to use this email address -->
<maintainer email="[email protected]">Jacob Hassold</maintainer>
<license>Apache License 2.0</license>

<build_depend>rclrs</build_depend>
<build_depend>rosidl_runtime_rs</build_depend>
<build_depend>example_interfaces</build_depend>

<exec_depend>rclrs</exec_depend>
<exec_depend>rosidl_runtime_rs</exec_depend>
<exec_depend>example_interfaces</exec_depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
14 changes: 14 additions & 0 deletions examples/minimal_action_client/src/minimal_action_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use std::env;

use anyhow::{Error, Result};

fn main() -> Result<(), Error> {
let context = rclrs::Context::new(env::args())?;

let node = rclrs::create_node(&context, "minimal_client")?;

let _client =
node.create_action_client::<example_interfaces::action::Fibonacci>("fibonacci")?;

Ok(())
}
22 changes: 22 additions & 0 deletions examples/minimal_action_server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "examples_rclrs_minimal_action_server"
version = "0.3.1"
# This project is not military-sponsored, Jacob's employment contract just requires him to use this email address
authors = ["Esteve Fernandez <[email protected]>", "Nikolai Morin <[email protected]>", "Jacob Hassold <[email protected]>"]
edition = "2021"

[[bin]]
name = "minimal_action_server"
path = "src/minimal_action_server.rs"

[dependencies]
anyhow = {version = "1", features = ["backtrace"]}

[dependencies.rclrs]
version = "0.4"

[dependencies.rosidl_runtime_rs]
version = "0.4"

[dependencies.example_interfaces]
version = "*"
26 changes: 26 additions & 0 deletions examples/minimal_action_server/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>examples_rclrs_minimal_action_server</name>
<version>0.3.1</version>
<description>Minimal action server examples for rclrs.</description>
<maintainer email="[email protected]">Esteve Fernandez</maintainer>
<maintainer email="[email protected]">Nikolai Morin</maintainer>
<!-- This project is not military-sponsored, Jacob's employment contract just requires him to use this email address -->
<maintainer email="[email protected]">Jacob Hassold</maintainer>
<license>Apache License 2.0</license>

<build_depend>rclrs</build_depend>
<build_depend>rosidl_runtime_rs</build_depend>
<build_depend>example_interfaces</build_depend>

<exec_depend>rclrs</exec_depend>
<exec_depend>rosidl_runtime_rs</exec_depend>
<exec_depend>example_interfaces</exec_depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
81 changes: 81 additions & 0 deletions examples/minimal_action_server/src/minimal_action_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::env;
use std::sync::Arc;
use std::thread;

use anyhow::{Error, Result};

type Fibonacci = example_interfaces::action::Fibonacci;
type GoalHandleFibonacci = rclrs::ServerGoalHandle<Fibonacci>;

fn handle_goal(
_uuid: &rclrs::GoalUUID,
goal: Arc<example_interfaces::action::rmw::Fibonacci_Goal>,
) -> rclrs::GoalResponse {
println!("Received goal request with order {}", goal.order);
if goal.order > 9000 {
rclrs::GoalResponse::Reject
} else {
rclrs::GoalResponse::AcceptAndExecute
}
}

fn handle_cancel(_goal_handle: Arc<GoalHandleFibonacci>) -> rclrs::CancelResponse {
println!("Got request to cancel goal");
rclrs::CancelResponse::Accept
}

fn execute(goal_handle: Arc<GoalHandleFibonacci>) {
println!("Executing goal");
let feedback = example_interfaces::action::Fibonacci_Feedback {
sequence: [0, 1].to_vec(),
};

for i in 1..goal_handle.goal_request.order {
if goal_handle.is_canceling() {
let result = example_interfaces::action::Fibonacci_Result {
sequence: Vec::new(),
};

goal_handle.canceled(&result);
println!("Goal canceled");
return;
}

// Update sequence sequence
feedback
.sequence
.push(feedback.sequence[i as usize] + feedback.sequence[(i - 1) as usize]);
// Publish feedback
goal_handle.publish_feedback(&feedback);
println!("Publishing feedback");
thread::sleep(std::time::Duration::from_millis(100));
}

let result = example_interfaces::action::Fibonacci_Result {
sequence: Vec::new(),
};
result.sequence = feedback.sequence.clone();
goal_handle.succeed(&result);
println!("Goal succeeded");
}

fn handle_accepted(goal_handle: Arc<GoalHandleFibonacci>) {
thread::spawn(move || {
execute(goal_handle);
});
}

fn main() -> Result<(), Error> {
let context = rclrs::Context::new(env::args())?;

let mut node = rclrs::create_node(&context, "minimal_action_server")?;

let _action_server = node.create_action_server::<example_interfaces::action::Fibonacci>(
"fibonacci",
handle_goal,
handle_cancel,
handle_accepted,
);

rclrs::spin(node).map_err(|err| err.into())
}
109 changes: 109 additions & 0 deletions rclrs/src/action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use crate::{rcl_bindings::*, RclrsError};
use std::sync::{Arc, Mutex, MutexGuard};

// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
// they are running in. Therefore, this type can be safely sent to another thread.
unsafe impl Send for rcl_action_goal_handle_t {}

unsafe impl Sync for rcl_action_goal_handle_t {}

use std::marker::PhantomData;

pub type GoalUUID = [u8; RCL_ACTION_UUID_SIZE];

pub enum GoalResponse {
Reject = 1,
AcceptAndExecute = 2,
AcceptAndDefer = 3,
}

pub enum CancelResponse {
Reject = 1,
Accept = 2,
}

pub struct ActionClient<T>
where
T: rosidl_runtime_rs::Action,
{
_marker: PhantomData<T>,
}

impl<T> ActionClient<T>
where
T: rosidl_runtime_rs::Action,
{
/// Creates a new action client.
pub(crate) fn new(rcl_node_mtx: Arc<Mutex<rcl_node_t>>, topic: &str) -> Result<Self, RclrsError>
where
T: rosidl_runtime_rs::Action,
{
Ok(Self {
_marker: Default::default(),
})
}
}

pub struct ActionServer<T>
where
T: rosidl_runtime_rs::Action,
{
_marker: PhantomData<T>,
}

impl<T> ActionServer<T>
where
T: rosidl_runtime_rs::Action,
{
/// Creates a new action server.
pub(crate) fn new(rcl_node_mtx: Arc<Mutex<rcl_node_t>>, topic: &str) -> Result<Self, RclrsError>
where
T: rosidl_runtime_rs::Action,
{
Ok(Self {
_marker: Default::default(),
})
}
}

pub struct ServerGoalHandle<T>
where
T: rosidl_runtime_rs::Action,
{
rcl_handle: Arc<rcl_action_goal_handle_t>,
goal_request: Arc<T>,
_marker: PhantomData<T>,
}

impl<T> ServerGoalHandle<T>
where
T: rosidl_runtime_rs::Action,
{
pub fn new(rcl_handle: Arc<rcl_action_goal_handle_t>, goal_request: Arc<T>) -> Self {
Self {
rcl_handle,
goal_request: Arc::clone(&goal_request),
_marker: Default::default(),
}
}

pub fn is_canceling(&self) -> bool {
false
}

pub fn is_active(&self) -> bool {
false
}

pub fn is_executing(&self) -> bool {
false
}

pub fn succeed(&self, result: &T::Result) -> Result<(), RclrsError> {
Ok(())
}

pub fn canceled(&self, result: &T::Result) -> Result<(), RclrsError> {
Ok(())
}
}
4 changes: 4 additions & 0 deletions rclrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//!
//! [1]: https://github.com/ros2-rust/ros2_rust/blob/main/README.md

mod action;
mod arguments;
mod client;
mod clock;
Expand All @@ -30,6 +31,7 @@ pub mod dynamic_message;
use std::sync::Arc;
use std::time::Duration;

pub use action::*;
pub use arguments::*;
pub use client::*;
pub use clock::*;
Expand All @@ -40,6 +42,8 @@ pub use node::*;
pub use parameter::*;
pub use publisher::*;
pub use qos::*;
use rcl_bindings::rcl_context_is_valid;
use rcl_bindings::rcl_action_goal_handle_t;
pub use rcl_bindings::rmw_request_id_t;
pub use service::*;
pub use subscription::*;
Expand Down
50 changes: 47 additions & 3 deletions rclrs/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ pub use self::builder::*;
pub use self::graph::*;
use crate::rcl_bindings::*;
use crate::{
Client, ClientBase, Clock, Context, GuardCondition, ParameterBuilder, ParameterInterface,
ParameterVariant, Parameters, Publisher, QoSProfile, RclrsError, Service, ServiceBase,
Subscription, SubscriptionBase, SubscriptionCallback, TimeSource, ToResult,
ActionClient, ActionServer, CancelResponse, Client, ClientBase, Clock, Context, GoalResponse,
GoalUUID, GuardCondition, ParameterBuilder, ParameterInterface, ParameterVariant, Parameters,
Publisher, QoSProfile, RclrsError, ServerGoalHandle, Service, ServiceBase, Subscription,
SubscriptionBase, SubscriptionCallback, TimeSource, ToResult,
};

impl Drop for rcl_node_t {
Expand Down Expand Up @@ -195,6 +196,49 @@ impl Node {
Ok(client)
}

/// Creates an [`ActionClient`][1].
///
/// [1]: crate::ActionClient
// TODO: make action client's lifetime depend on node's lifetime
pub fn create_action_client<T>(
&self,
topic: &str,
) -> Result<Arc<ActionClient<T>>, RclrsError>
where
T: rosidl_runtime_rs::Action,
{
let action_client = Arc::new(ActionClient::<T>::new(
Arc::clone(&self.rcl_node_mtx),
topic,
)?);
// self.clients
// .push(Arc::downgrade(&client) as Weak<dyn ClientBase>);
Ok(action_client)
}

/// Creates an [`ActionServer`][1].
///
/// [1]: crate::ActionServer
// TODO: make action server's lifetime depend on node's lifetime
pub fn create_action_server<T>(
&mut self,
topic: &str,
handle_goal: fn(&crate::action::GoalUUID, Arc<T::Goal>) -> GoalResponse,
handle_cancel: fn(Arc<ServerGoalHandle<T>>) -> CancelResponse,
handle_accepted: fn(Arc<ServerGoalHandle<T>>),
) -> Result<Arc<ActionServer<T>>, RclrsError>
where
T: rosidl_runtime_rs::Action,
{
let action_server = Arc::new(ActionServer::<T>::new(
Arc::clone(&self.rcl_node_mtx),
topic,
)?);
// self.servers
// .push(Arc::downgrade(&server) as Weak<dyn ClientBase>);
Ok(action_server)
}

/// Creates a [`GuardCondition`][1] with no callback.
///
/// A weak pointer to the `GuardCondition` is stored within this node.
Expand Down
Loading
Loading