Skip to content
Merged
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
8 changes: 4 additions & 4 deletions r2r/src/action_clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,10 @@ where
.map_err(|_| Error::RCL_RET_CLIENT_INVALID)
.map(|r| match r {
Ok(r) => match r.return_code {
0 => Ok(()),
1 => Err(Error::GoalCancelRejected),
2 => Err(Error::GoalCancelUnknownGoalID),
3 => Err(Error::GoalCancelAlreadyTerminated),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_NONE as i8 => Ok(()),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_REJECTED as i8 => Err(Error::GoalCancelRejected),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_UNKNOWN_GOAL_ID as i8 => Err(Error::GoalCancelUnknownGoalID),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_GOAL_TERMINATED as i8 => Err(Error::GoalCancelAlreadyTerminated),
x => panic!("unknown error code return from action server: {}", x),
},
Err(e) => Err(e),
Expand Down
8 changes: 4 additions & 4 deletions r2r/src/action_clients_untyped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,10 @@ impl WrappedActionClientUntyped {
.map_err(|_| Error::RCL_RET_CLIENT_INVALID)
.map(|r| match r {
Ok(r) => match r.return_code {
0 => Ok(()),
1 => Err(Error::GoalCancelRejected),
2 => Err(Error::GoalCancelUnknownGoalID),
3 => Err(Error::GoalCancelAlreadyTerminated),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_NONE as i8 => Ok(()),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_REJECTED as i8 => Err(Error::GoalCancelRejected),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_UNKNOWN_GOAL_ID as i8 => Err(Error::GoalCancelUnknownGoalID),
e if e == action_msgs::srv::CancelGoal::Response::ERROR_GOAL_TERMINATED as i8 => Err(Error::GoalCancelAlreadyTerminated),
x => panic!("unknown error code return from action server: {}", x),
},
Err(e) => Err(e),
Expand Down
28 changes: 14 additions & 14 deletions r2r/src/action_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ impl GoalStatus {
#[allow(dead_code)]
pub fn to_rcl(&self) -> i8 {
match self {
GoalStatus::Unknown => 0,
GoalStatus::Accepted => 1,
GoalStatus::Executing => 2,
GoalStatus::Canceling => 3,
GoalStatus::Succeeded => 4,
GoalStatus::Canceled => 5,
GoalStatus::Aborted => 6,
GoalStatus::Unknown => crate::action_msgs::msg::GoalStatus::STATUS_UNKNOWN as i8,
GoalStatus::Accepted => crate::action_msgs::msg::GoalStatus::STATUS_ACCEPTED as i8,
GoalStatus::Executing => crate::action_msgs::msg::GoalStatus::STATUS_EXECUTING as i8,
GoalStatus::Canceling => crate::action_msgs::msg::GoalStatus::STATUS_CANCELING as i8,
GoalStatus::Succeeded => crate::action_msgs::msg::GoalStatus::STATUS_SUCCEEDED as i8,
GoalStatus::Canceled => crate::action_msgs::msg::GoalStatus::STATUS_CANCELED as i8,
GoalStatus::Aborted => crate::action_msgs::msg::GoalStatus::STATUS_ABORTED as i8,
}
}

pub fn from_rcl(s: i8) -> Self {
match s {
0 => GoalStatus::Unknown,
1 => GoalStatus::Accepted,
2 => GoalStatus::Executing,
3 => GoalStatus::Canceling,
4 => GoalStatus::Succeeded,
5 => GoalStatus::Canceled,
6 => GoalStatus::Aborted,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_UNKNOWN as i8 => GoalStatus::Unknown,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_ACCEPTED as i8 => GoalStatus::Accepted,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_EXECUTING as i8 => GoalStatus::Executing,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_CANCELING as i8 => GoalStatus::Canceling,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_SUCCEEDED as i8 => GoalStatus::Succeeded,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_CANCELED as i8 => GoalStatus::Canceled,
s if s == crate::action_msgs::msg::GoalStatus::STATUS_ABORTED as i8 => GoalStatus::Aborted,
_ => panic!("unknown action status: {}", s),
}
}
Expand Down
17 changes: 7 additions & 10 deletions r2r/src/action_servers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::ffi::CString;
use std::mem::MaybeUninit;
use std::sync::{Arc, Mutex, Weak};

use crate::action_common::*;
use crate::error::*;
use crate::msg_types::generated_msgs::{action_msgs, builtin_interfaces, unique_identifier_msgs};
use crate::msg_types::*;
Expand Down Expand Up @@ -192,14 +191,14 @@ where

fn is_cancelling(&self, uuid: &uuid::Uuid) -> Result<bool> {
if let Some(handle) = self.goals.get(uuid) {
let mut state = 0u8; // TODO: int8 STATUS_UNKNOWN = 0;
let mut state = action_msgs::msg::GoalStatus::STATUS_UNKNOWN as u8;
let ret = unsafe { rcl_action_goal_handle_get_status(*handle, &mut state) };

if ret != RCL_RET_OK as i32 {
println!("action server: Failed to get goal handle state: {}", ret);
return Err(Error::from_rcl_error(ret));
}
return Ok(state == 3u8); // TODO: int8 STATUS_CANCELING
return Ok(state == action_msgs::msg::GoalStatus::STATUS_CANCELING as u8);
}
Err(Error::RCL_RET_ACTION_GOAL_HANDLE_INVALID)
}
Expand Down Expand Up @@ -295,7 +294,7 @@ where

// check if all cancels were rejected.
if requested_cancels >= 1 && response_msg.goals_canceling.is_empty() {
response_msg.return_code = 1; // TODO: auto generate these (int8 ERROR_REJECTED=1)
response_msg.return_code = action_msgs::srv::CancelGoal::Response::ERROR_REJECTED as i8;
}

responses.push((*request_id, response_msg));
Expand Down Expand Up @@ -534,9 +533,7 @@ where

let response_msg = if !goal_exists {
// Goal does not exists
println!("goal does not exist :(");
let status = GoalStatus::Unknown;
let msg = T::make_result_response_msg(status.to_rcl(), T::Result::default());
let msg = T::make_result_response_msg(action_msgs::msg::GoalStatus::STATUS_UNKNOWN as i8, T::Result::default());
let mut response_msg = WrappedNativeMsg::<
<<T as WrappedActionTypeSupport>::GetResult as WrappedServiceTypeSupport>::Response,
>::from(&msg);
Expand Down Expand Up @@ -667,7 +664,7 @@ where
action_server.publish_status();

// create result message
let result_msg = T::make_result_response_msg(5, msg); // todo: int8 STATUS_CANCELED = 5
let result_msg = T::make_result_response_msg(action_msgs::msg::GoalStatus::STATUS_CANCELED as i8, msg);
let native_msg = WrappedNativeMsg::<
<<T as WrappedActionTypeSupport>::GetResult as WrappedServiceTypeSupport>::Response,
>::from(&result_msg);
Expand All @@ -687,7 +684,7 @@ where
action_server.set_goal_state(&self.uuid, rcl_action_goal_event_t::GOAL_EVENT_ABORT)?;

// create result message
let result_msg = T::make_result_response_msg(6, msg); // todo: int8 STATUS_ABORTED = 6
let result_msg = T::make_result_response_msg(action_msgs::msg::GoalStatus::STATUS_ABORTED as i8, msg);
let native_msg = WrappedNativeMsg::<
<<T as WrappedActionTypeSupport>::GetResult as WrappedServiceTypeSupport>::Response,
>::from(&result_msg);
Expand All @@ -710,7 +707,7 @@ where
action_server.set_goal_state(&self.uuid, rcl_action_goal_event_t::GOAL_EVENT_SUCCEED)?;

// create result message
let result_msg = T::make_result_response_msg(4, msg); // todo: int8 STATUS_SUCCEEDED = 4
let result_msg = T::make_result_response_msg(action_msgs::msg::GoalStatus::STATUS_SUCCEEDED as i8, msg);
let native_msg = WrappedNativeMsg::<
<<T as WrappedActionTypeSupport>::GetResult as WrappedServiceTypeSupport>::Response,
>::from(&result_msg);
Expand Down
27 changes: 27 additions & 0 deletions r2r/src/msg_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,4 +770,31 @@ mod tests {
// the message should contain something (default msg)
assert!(!json_request.to_string().is_empty());
}


#[cfg(r2r__action_msgs__msg__GoalStatus)]
#[test]
fn test_msg_constants() {
use action_msgs::msg::GoalStatus;
let gs = GoalStatus::default();

assert_eq!(gs.status, GoalStatus::STATUS_UNKNOWN as i8);
assert_eq!(0, GoalStatus::STATUS_UNKNOWN as i8);
assert_eq!(1, GoalStatus::STATUS_ACCEPTED as i8);
assert_eq!(2, GoalStatus::STATUS_EXECUTING as i8);
assert_eq!(3, GoalStatus::STATUS_CANCELING as i8);
assert_eq!(4, GoalStatus::STATUS_SUCCEEDED as i8);
assert_eq!(5, GoalStatus::STATUS_CANCELED as i8);
assert_eq!(6, GoalStatus::STATUS_ABORTED as i8);

use action_msgs::srv::CancelGoal;
let cgr = CancelGoal::Response::default();

assert_eq!(cgr.return_code, CancelGoal::Response::ERROR_NONE as i8);
assert_eq!(0, CancelGoal::Response::ERROR_NONE as i8);
assert_eq!(1, CancelGoal::Response::ERROR_REJECTED as i8);
assert_eq!(2, CancelGoal::Response::ERROR_UNKNOWN_GOAL_ID as i8);
assert_eq!(3, CancelGoal::Response::ERROR_GOAL_TERMINATED as i8);

}
}
69 changes: 69 additions & 0 deletions r2r_msg_gen/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ use r2r_common::RosMsg;
use std::fs::OpenOptions;
use std::path::{Path, PathBuf};
use std::{env, fs};
use std::collections::HashMap;

const MSG_INCLUDES_FILENAME: &str = "msg_includes.h";
const INTROSPECTION_FILENAME: &str = "introspection_functions.rs";
const CONSTANTS_FILENAME: &str = "constants.rs";
const BINDINGS_FILENAME: &str = "msg_bindings.rs";
const GENERATED_FILES: &[&str] = &[
MSG_INCLUDES_FILENAME,
INTROSPECTION_FILENAME,
CONSTANTS_FILENAME,
BINDINGS_FILENAME,
];

Expand Down Expand Up @@ -76,6 +79,7 @@ fn run_bindgen(msg_list: &[RosMsg]) {
fn generate_bindings(bindgen_dir: &Path, msg_list: &[RosMsg]) {
let msg_includes_file = bindgen_dir.join(MSG_INCLUDES_FILENAME);
let introspection_file = bindgen_dir.join(INTROSPECTION_FILENAME);
let constants_file = bindgen_dir.join(CONSTANTS_FILENAME);
let bindings_file = bindgen_dir.join(BINDINGS_FILENAME);

let mut includes = String::new();
Expand Down Expand Up @@ -170,11 +174,76 @@ fn generate_bindings(bindgen_dir: &Path, msg_list: &[RosMsg]) {

let bindings = builder.generate().expect("Unable to generate bindings");

// Let's add a hack to generate constants.
let str_bindings = bindings.to_string();
// find all lines which look suspiciosly like a constant.
let mut constants: HashMap<String, Vec<(String,String)>> = HashMap::new();
for msg in msg_list {
if msg.prefix == "srv" {
for s in &["Request", "Response"] {
let key = format!("{}__{}__{}_{}", &msg.module, &msg.prefix, &msg.name, s);
add_constants(&key, &str_bindings, &mut constants);
}
} else if msg.prefix == "action" {
for s in &["Goal", "Result", "Feedback", "FeedbackMessage"] {
let key = format!("{}__{}__{}_{}", &msg.module, &msg.prefix, &msg.name, s);
add_constants(&key, &str_bindings, &mut constants);
}
} else {
let key = format!("{}__{}__{}", &msg.module, &msg.prefix, &msg.name);
add_constants(&key, &str_bindings, &mut constants);
}
}
// generate a constant which holds all constants.
let mut constants_map = String::from(
"\
lazy_static! {
static ref CONSTANTS_MAP: HashMap<&'static str, Vec<(String,String)>> = {
let mut m = HashMap::new();\
");
for (msg,msg_constants) in constants {
let msg_constants_str = format!("vec![{}]",
msg_constants.iter()
.map(|(c,t)| format!("(\"{c}\".to_string(), \"{t}\".to_string())"))
.collect::<Vec<String>>().join(","));

constants_map.push_str(&format!("m.insert(\"{}\", {});\n", msg, msg_constants_str));
}
constants_map.push_str("m \n }; }\n\n");
fs::write(&constants_file, constants_map).unwrap();

bindings
.write_to_file(bindings_file)
.expect("Couldn't write bindings!");
}

fn add_constants(key: &str, bindings: &str, constants: &mut HashMap<String, Vec<(String, String)>>) {
let mut lines = bindings.lines();
while let Some(line) = lines.next() {
let prefix = format!("pub const {}__", key);
if let Some(constant) = line.strip_prefix(&prefix) {
if let Some((con, typ)) = constant.split_once(":") {
// These are generated automatically for arrays and strings, we don't need to expose them.
if con.ends_with("__MAX_SIZE") || con.ends_with("__MAX_STRING_SIZE") {
continue;
}
if let Some((t, _)) = typ.split_once("=") {
constants.entry(key.into()).or_default().push((con.to_string(), t.trim().to_string()));
} else if let Some(next_line) = lines.next() {
// type has moved down to the next line. (bindgen has a max line width)
if let Some((t, _)) = next_line.split_once("=") {
constants.entry(key.into()).or_default().push((con.to_string(), t.trim().to_string()));
} else {
panic!("Code generation failure. Type not found in line! {}", next_line);
}
} else {
panic!("Code generation failure. Type not found in line! {}", line);
}
}
}
}
}

fn run_dynlink(#[allow(unused_variables)] msg_list: &[RosMsg]) {
#[cfg(not(feature = "doc-only"))]
{
Expand Down
27 changes: 25 additions & 2 deletions r2r_msg_gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#![allow(clippy::all)]
include!(concat!(env!("OUT_DIR"), "/msg_bindings.rs"));
include!(concat!(env!("OUT_DIR"), "/introspection_functions.rs"));
include!(concat!(env!("OUT_DIR"), "/constants.rs"));

#[macro_use]
extern crate lazy_static;
Expand Down Expand Up @@ -602,6 +603,26 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
msgname = name
);


let constants = CONSTANTS_MAP.get(key.as_str()).cloned().unwrap_or_default();
let mut constant_strings = vec![];
for (c, typ) in constants {
constant_strings.push(format!(" pub const {c}: {typ} = {key}__{c};"));
}
let impl_constants = if constant_strings.is_empty() {
String::new()
} else {
format!("
#[allow(non_upper_case_globals)]
impl {msgname} {{
{constants}
}}
",
msgname = name,
constants = constant_strings.join("\n"))
};


let module_str = format!(
"
#[derive(Clone,Debug,PartialEq,Serialize,Deserialize)]
Expand All @@ -610,12 +631,14 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
{fields}
}}\n
{typesupport}\n
{default}\n\n
{default}\n
{constants}\n\n
",
msgname = name,
fields = fields,
typesupport = typesupport,
default = impl_default
default = impl_default,
constants = impl_constants,
);

module_str
Expand Down