1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Purpose of this example is to show how the ServiceProvider trait can be use
//! to create code that is generic of which communication backend it will use.
#[cfg(all(feature = "rosbridge", feature = "ros1"))]
roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces");
#[cfg(all(feature = "rosbridge", feature = "ros1"))]
#[tokio::main]
async fn main() {
// Important to bring these traits into scope so we can use them
use roslibrust::{Ros, Service};
env_logger::init();
// TopicProvider cannot be an "Object Safe Trait" due to its generic parameters
// This means we can't do:
// Which specific TopicProvider you are going to use must be known at
// compile time! We can use features to build multiple copies of our
// executable with different backends. Or mix and match within a
// single application. The critical part is to make TopicProvider a
// generic type on you Node.
struct MyNode<T: Ros> {
ros: T,
}
// Basic example of a node that publishes and subscribes to itself
// This node will work with any backend
impl<T: Ros> MyNode<T> {
fn handle_service(
_request: std_srvs::SetBoolRequest,
) -> Result<std_srvs::SetBoolResponse, roslibrust::ServiceError> {
// Not actually doing anything here just example
// Note: if we did want to set a bool, we'd probably want to use Arc<Mutex<bool>>
Ok(std_srvs::SetBoolResponse {
success: true,
message: "You set my bool!".to_string(),
})
}
async fn run(self) {
let _handle = self
.ros
.advertise_service::<std_srvs::SetBool, _>("/my_set_bool", Self::handle_service)
.await
.unwrap();
let client = self
.ros
.service_client::<std_srvs::SetBool>("/my_set_bool")
.await
.unwrap();
loop {
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
println!("sleeping");
let response = client
.call(&std_srvs::SetBoolRequest { data: true })
.await
.unwrap();
println!("Got response: {response:?}");
}
}
}
// Use our generic node with the rosbridge backend
// create a rosbridge handle and start node
let ros = roslibrust::rosbridge::ClientHandle::new("ws://localhost:9090")
.await
.unwrap();
let node = MyNode { ros };
tokio::spawn(async move { node.run().await });
// Use our generic node with the ros1 backend
// create a ros1 handle and start node
let ros = roslibrust::ros1::NodeHandle::new("http://localhost:11311", "/my_node")
.await
.unwrap();
let node = MyNode { ros };
tokio::spawn(async move { node.run().await });
loop {
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
println!("sleeping");
}
// With this executable running
// RUST_LOG=debug cargo run --features ros1,topic_provider --example generic_client_services
// You should see log output from both nodes
}
#[cfg(not(all(feature = "rosbridge", feature = "ros1")))]
fn main() {
eprintln!(
"This example does nothing without compiling with the feature 'rosbridge' and 'ros1'"
);
}